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

Commit 32e1832d authored by Andreas Huber's avatar Andreas Huber
Browse files

Support even more .avi files with .mp3 content not aligned to chunk boundaries

Change-Id: I3ace4118e2e519648c23c51d0b699ba0d7511a5d
related-to-bug: 5349382
parent e2c10206
Loading
Loading
Loading
Loading
+304 −38
Original line number Original line Diff line number Diff line
@@ -56,11 +56,36 @@ private:
    MediaBufferGroup *mBufferGroup;
    MediaBufferGroup *mBufferGroup;
    size_t mSampleIndex;
    size_t mSampleIndex;


    sp<MP3Splitter> mSplitter;

    DISALLOW_EVIL_CONSTRUCTORS(AVISource);
    DISALLOW_EVIL_CONSTRUCTORS(AVISource);
};
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////


struct AVIExtractor::MP3Splitter : public RefBase {
    MP3Splitter();

    void clear();
    void append(MediaBuffer *buffer);
    status_t read(MediaBuffer **buffer);

protected:
    virtual ~MP3Splitter();

private:
    bool mFindSync;
    int64_t mBaseTimeUs;
    int64_t mNumSamplesRead;
    sp<ABuffer> mBuffer;

    bool resync();

    DISALLOW_EVIL_CONSTRUCTORS(MP3Splitter);
};

////////////////////////////////////////////////////////////////////////////////

AVIExtractor::AVISource::AVISource(
AVIExtractor::AVISource::AVISource(
        const sp<AVIExtractor> &extractor, size_t trackIndex)
        const sp<AVIExtractor> &extractor, size_t trackIndex)
    : mExtractor(extractor),
    : mExtractor(extractor),
@@ -84,6 +109,15 @@ status_t AVIExtractor::AVISource::start(MetaData *params) {
    mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
    mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
    mSampleIndex = 0;
    mSampleIndex = 0;


    const char *mime;
    CHECK(mTrack.mMeta->findCString(kKeyMIMEType, &mime));

    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
        mSplitter = new MP3Splitter;
    } else {
        mSplitter.clear();
    }

    return OK;
    return OK;
}
}


@@ -93,6 +127,8 @@ status_t AVIExtractor::AVISource::stop() {
    delete mBufferGroup;
    delete mBufferGroup;
    mBufferGroup = NULL;
    mBufferGroup = NULL;


    mSplitter.clear();

    return OK;
    return OK;
}
}


@@ -116,6 +152,21 @@ status_t AVIExtractor::AVISource::read(
        if (err != OK) {
        if (err != OK) {
            return ERROR_END_OF_STREAM;
            return ERROR_END_OF_STREAM;
        }
        }

        if (mSplitter != NULL) {
            mSplitter->clear();
        }
    }

    for (;;) {
        if (mSplitter != NULL) {
            status_t err = mSplitter->read(buffer);

            if (err == OK) {
                break;
            } else if (err != -EAGAIN) {
                return err;
            }
        }
        }


        off64_t offset;
        off64_t offset;
@@ -148,7 +199,166 @@ status_t AVIExtractor::AVISource::read(
            out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
            out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
        }
        }


        if (mSplitter == NULL) {
            *buffer = out;
            *buffer = out;
            break;
        }

        mSplitter->append(out);
        out->release();
        out = NULL;
    }

    return OK;
}

////////////////////////////////////////////////////////////////////////////////

AVIExtractor::MP3Splitter::MP3Splitter()
    : mFindSync(true),
      mBaseTimeUs(-1ll),
      mNumSamplesRead(0) {
}

AVIExtractor::MP3Splitter::~MP3Splitter() {
}

void AVIExtractor::MP3Splitter::clear() {
    mFindSync = true;
    mBaseTimeUs = -1ll;
    mNumSamplesRead = 0;

    if (mBuffer != NULL) {
        mBuffer->setRange(0, 0);
    }
}

void AVIExtractor::MP3Splitter::append(MediaBuffer *buffer) {
    size_t prevCapacity = (mBuffer != NULL) ? mBuffer->capacity() : 0;

    if (mBaseTimeUs < 0) {
        CHECK(mBuffer == NULL || mBuffer->size() == 0);
        CHECK(buffer->meta_data()->findInt64(kKeyTime, &mBaseTimeUs));
        mNumSamplesRead = 0;
    }

    if (mBuffer != NULL && mBuffer->offset() > 0) {
        memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
        mBuffer->setRange(0, mBuffer->size());
    }

    if (mBuffer == NULL
            || mBuffer->size() + buffer->range_length() > prevCapacity) {
        size_t newCapacity =
            (prevCapacity + buffer->range_length() + 1023) & ~1023;

        sp<ABuffer> newBuffer = new ABuffer(newCapacity);
        if (mBuffer != NULL) {
            memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
            newBuffer->setRange(0, mBuffer->size());
        } else {
            newBuffer->setRange(0, 0);
        }
        mBuffer = newBuffer;
    }

    memcpy(mBuffer->data() + mBuffer->size(),
           (const uint8_t *)buffer->data() + buffer->range_offset(),
           buffer->range_length());

    mBuffer->setRange(0, mBuffer->size() + buffer->range_length());
}

bool AVIExtractor::MP3Splitter::resync() {
    if (mBuffer == NULL) {
        return false;
    }

    bool foundSync = false;
    for (size_t offset = 0; offset + 3 < mBuffer->size(); ++offset) {
        uint32_t firstHeader = U32_AT(mBuffer->data() + offset);

        size_t frameSize;
        if (!GetMPEGAudioFrameSize(firstHeader, &frameSize)) {
            continue;
        }

        size_t subsequentOffset = offset + frameSize;
        size_t i = 3;
        while (i > 0) {
            if (subsequentOffset + 3 >= mBuffer->size()) {
                break;
            }

            static const uint32_t kMask = 0xfffe0c00;

            uint32_t header = U32_AT(mBuffer->data() + subsequentOffset);
            if ((header & kMask) != (firstHeader & kMask)) {
                break;
            }

            if (!GetMPEGAudioFrameSize(header, &frameSize)) {
                break;
            }

            subsequentOffset += frameSize;
            --i;
        }

        if (i == 0) {
            foundSync = true;
            memmove(mBuffer->data(),
                    mBuffer->data() + offset,
                    mBuffer->size() - offset);

            mBuffer->setRange(0, mBuffer->size() - offset);
            break;
        }
    }

    return foundSync;
}

status_t AVIExtractor::MP3Splitter::read(MediaBuffer **out) {
    *out = NULL;

    if (mFindSync) {
        if (!resync()) {
            return -EAGAIN;
        }

        mFindSync = false;
    }

    if (mBuffer->size() < 4) {
        return -EAGAIN;
    }

    uint32_t header = U32_AT(mBuffer->data());
    size_t frameSize;
    int sampleRate;
    int numSamples;
    if (!GetMPEGAudioFrameSize(
                header, &frameSize, &sampleRate, NULL, NULL, &numSamples)) {
        return ERROR_MALFORMED;
    }

    if (mBuffer->size() < frameSize) {
        return -EAGAIN;
    }

    MediaBuffer *mbuf = new MediaBuffer(frameSize);
    memcpy(mbuf->data(), mBuffer->data(), frameSize);

    int64_t timeUs = mBaseTimeUs + (mNumSamplesRead * 1000000ll) / sampleRate;
    mNumSamplesRead += numSamples;

    mbuf->meta_data()->setInt64(kKeyTime, timeUs);

    mBuffer->setRange(
            mBuffer->offset() + frameSize, mBuffer->size() - frameSize);

    *out = mbuf;


    return OK;
    return OK;
}
}
@@ -449,6 +659,8 @@ status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) {
    track->mThumbnailSampleSize = 0;
    track->mThumbnailSampleSize = 0;
    track->mThumbnailSampleIndex = -1;
    track->mThumbnailSampleIndex = -1;
    track->mMaxSampleSize = 0;
    track->mMaxSampleSize = 0;
    track->mAvgChunkSize = 1.0;
    track->mFirstChunkSize = 0;


    return OK;
    return OK;
}
}
@@ -467,8 +679,8 @@ status_t AVIExtractor::parseStreamFormat(off64_t offset, size_t size) {


    bool isVideo = (track->mKind == Track::VIDEO);
    bool isVideo = (track->mKind == Track::VIDEO);


    if ((isVideo && size < 40) || (!isVideo && size < 18)) {
    if ((isVideo && size < 40) || (!isVideo && size < 16)) {
        // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively.
        // Expected a BITMAPINFO or WAVEFORMAT(EX) structure, respectively.
        return ERROR_MALFORMED;
        return ERROR_MALFORMED;
    }
    }


@@ -651,6 +863,47 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) {
    for (size_t i = 0; i < mTracks.size(); ++i) {
    for (size_t i = 0; i < mTracks.size(); ++i) {
        Track *track = &mTracks.editItemAt(i);
        Track *track = &mTracks.editItemAt(i);


        if (track->mBytesPerSample > 0) {
            // Assume all chunks are roughly the same size for now.

            // Compute the avg. size of the first 128 chunks (if there are
            // that many), but exclude the size of the first one, since
            // it may be an outlier.
            size_t numSamplesToAverage = track->mSamples.size();
            if (numSamplesToAverage > 256) {
                numSamplesToAverage = 256;
            }

            double avgChunkSize = 0;
            size_t j;
            for (j = 0; j <= numSamplesToAverage; ++j) {
                off64_t offset;
                size_t size;
                bool isKey;
                int64_t dummy;

                status_t err =
                    getSampleInfo(
                            i, j,
                            &offset, &size, &isKey, &dummy);

                if (err != OK) {
                    return err;
                }

                if (j == 0) {
                    track->mFirstChunkSize = size;
                    continue;
                }

                avgChunkSize += size;
            }

            avgChunkSize /= numSamplesToAverage;

            track->mAvgChunkSize = avgChunkSize;
        }

        int64_t durationUs;
        int64_t durationUs;
        CHECK_EQ((status_t)OK,
        CHECK_EQ((status_t)OK,
                 getSampleTime(i, track->mSamples.size() - 1, &durationUs));
                 getSampleTime(i, track->mSamples.size() - 1, &durationUs));
@@ -687,21 +940,6 @@ status_t AVIExtractor::parseIndex(off64_t offset, size_t size) {
                return err;
                return err;
            }
            }
        }
        }

        if (track->mBytesPerSample != 0) {
            // Assume all chunks are the same size for now.

            off64_t offset;
            size_t size;
            bool isKey;
            int64_t sampleTimeUs;
            CHECK_EQ((status_t)OK,
                     getSampleInfo(
                         i, 0,
                         &offset, &size, &isKey, &sampleTimeUs));

            track->mRate *= size / track->mBytesPerSample;
        }
    }
    }


    mFoundIndex = true;
    mFoundIndex = true;
@@ -904,6 +1142,18 @@ status_t AVIExtractor::getSampleInfo(


    *isKey = info.mIsKey;
    *isKey = info.mIsKey;


    if (track.mBytesPerSample > 0) {
        size_t sampleStartInBytes;
        if (sampleIndex == 0) {
            sampleStartInBytes = 0;
        } else {
            sampleStartInBytes =
                track.mFirstChunkSize + track.mAvgChunkSize * (sampleIndex - 1);
        }

        sampleIndex = sampleStartInBytes / track.mBytesPerSample;
    }

    *sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale;
    *sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale;


    return OK;
    return OK;
@@ -928,8 +1178,24 @@ status_t AVIExtractor::getSampleIndexAtTime(


    const Track &track = mTracks.itemAt(trackIndex);
    const Track &track = mTracks.itemAt(trackIndex);


    ssize_t closestSampleIndex =
    ssize_t closestSampleIndex;
        timeUs / track.mRate * track.mScale / 1000000ll;

    if (track.mBytesPerSample > 0) {
        size_t closestByteOffset =
            (timeUs * track.mBytesPerSample)
                / track.mRate * track.mScale / 1000000ll;

        if (closestByteOffset <= track.mFirstChunkSize) {
            closestSampleIndex = 0;
        } else {
            closestSampleIndex =
                (closestByteOffset - track.mFirstChunkSize)
                    / track.mAvgChunkSize;
        }
    } else {
        // Each chunk contains a single sample.
        closestSampleIndex = timeUs / track.mRate * track.mScale / 1000000ll;
    }


    ssize_t numSamples = track.mSamples.size();
    ssize_t numSamples = track.mSamples.size();


+5 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ protected:


private:
private:
    struct AVISource;
    struct AVISource;
    struct MP3Splitter;


    struct SampleInfo {
    struct SampleInfo {
        uint32_t mOffset;
        uint32_t mOffset;
@@ -70,6 +71,10 @@ private:
        size_t mThumbnailSampleSize;
        size_t mThumbnailSampleSize;
        ssize_t mThumbnailSampleIndex;
        ssize_t mThumbnailSampleIndex;
        size_t mMaxSampleSize;
        size_t mMaxSampleSize;

        // If mBytesPerSample > 0:
        double mAvgChunkSize;
        size_t mFirstChunkSize;
    };
    };


    sp<DataSource> mDataSource;
    sp<DataSource> mDataSource;