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

Commit 4079be01 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "heif: adding libheif to be used by skia heif codec" into oc-mr1-dev

parents 00bbd4fa ea280cb3
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
cc_library_shared {
    name: "libheif",

    srcs: [
        "HeifDecoderImpl.cpp",
    ],

    shared_libs: [
        "libbinder",
        "liblog",
        "libutils",
        "libmedia",
    ],

    cflags: [
        "-Werror",
        "-Wall",
    ],

    include_dirs: [],

    export_include_dirs: ["include"],
}
+315 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "HeifDecoderImpl"

#include "HeifDecoderImpl.h"

#include <stdio.h>

#include <binder/IMemory.h>
#include <drm/drm_framework_common.h>
#include <media/IDataSource.h>
#include <media/mediametadataretriever.h>
#include <media/stagefright/MediaSource.h>
#include <private/media/VideoFrame.h>
#include <utils/Log.h>
#include <utils/RefBase.h>

HeifDecoder* createHeifDecoder() {
    return new android::HeifDecoderImpl();
}

namespace android {

/*
 * HeifDataSource
 *
 * Proxies data requests over IDataSource interface from MediaMetadataRetriever
 * to the HeifStream interface we received from the heif decoder client.
 */
class HeifDataSource : public BnDataSource {
public:
    /*
     * Constructs HeifDataSource; will take ownership of |stream|.
     */
    HeifDataSource(HeifStream* stream)
        : mStream(stream), mReadPos(0), mEOS(false) {}

    ~HeifDataSource() override {}

    /*
     * Initializes internal resources.
     */
    bool init();

    sp<IMemory> getIMemory() override { return mMemory; }
    ssize_t readAt(off64_t offset, size_t size) override;
    status_t getSize(off64_t* size) override ;
    void close() {}
    uint32_t getFlags() override { return 0; }
    String8 toString() override { return String8("HeifDataSource"); }
    sp<DecryptHandle> DrmInitialization(const char*) override {
        return nullptr;
    }

private:
    /*
     * Buffer size for passing the read data to mediaserver. Set to 64K
     * (which is what MediaDataSource Java API's jni implementation uses).
     */
    enum {
        kBufferSize = 64 * 1024,
    };
    sp<IMemory> mMemory;
    std::unique_ptr<HeifStream> mStream;
    off64_t mReadPos;
    bool mEOS;
};

bool HeifDataSource::init() {
    sp<MemoryDealer> memoryDealer =
            new MemoryDealer(kBufferSize, "HeifDataSource");
    mMemory = memoryDealer->allocate(kBufferSize);
    if (mMemory == nullptr) {
        ALOGE("Failed to allocate shared memory!");
        return false;
    }
    return true;
}

ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
    ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);

    if (size == 0) {
        return mEOS ? ERROR_END_OF_STREAM : 0;
    }

    if (offset < mReadPos) {
        // try seek, then rewind/skip, fail if none worked
        if (mStream->seek(offset)) {
            ALOGV("readAt: seek to offset=%lld", (long long)offset);
            mReadPos = offset;
            mEOS = false;
        } else if (mStream->rewind()) {
            ALOGV("readAt: rewind to offset=0");
            mReadPos = 0;
            mEOS = false;
        } else {
            ALOGE("readAt: couldn't seek or rewind!");
            mEOS = true;
        }
    }

    if (mEOS) {
        ALOGV("readAt: EOS");
        return ERROR_END_OF_STREAM;
    }

    if (offset > mReadPos) {
        // skipping
        size_t skipSize = offset - mReadPos;
        size_t bytesSkipped = mStream->read(nullptr, skipSize);
        if (bytesSkipped <= skipSize) {
            mReadPos += bytesSkipped;
        }
        if (bytesSkipped != skipSize) {
            mEOS = true;
            return ERROR_END_OF_STREAM;
        }
    }

    if (size > kBufferSize) {
        size = kBufferSize;
    }
    size_t bytesRead = mStream->read(mMemory->pointer(), size);
    if (bytesRead > size || bytesRead == 0) {
        // bytesRead is invalid
        mEOS = true;
        return ERROR_END_OF_STREAM;
    } if (bytesRead < size) {
        // read some bytes but not all, set EOS and return ERROR_END_OF_STREAM next time
        mEOS = true;
    }
    mReadPos += bytesRead;
    return bytesRead;
}

status_t HeifDataSource::getSize(off64_t* size) {
    if (!mStream->hasLength()) {
        *size = -1;
        ALOGE("getSize: not supported!");
        return ERROR_UNSUPPORTED;
    }
    *size = mStream->getLength();
    ALOGV("getSize: size=%lld", (long long)*size);
    return OK;
}

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

HeifDecoderImpl::HeifDecoderImpl() :
    // output color format should always be set via setOutputColor(), in case
    // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
    mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
    mCurScanline(0) {
}

HeifDecoderImpl::~HeifDecoderImpl() {
}

bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
    sp<HeifDataSource> dataSource = new HeifDataSource(stream);
    if (!dataSource->init()) {
        return false;
    }
    mDataSource = dataSource;

    mRetriever = new MediaMetadataRetriever();
    status_t err = mRetriever->setDataSource(mDataSource, "video/mp4");
    if (err != OK) {
        ALOGE("failed to set data source!");

        mRetriever.clear();
        mDataSource.clear();
        return false;
    }
    ALOGV("successfully set data source.");

    const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
    if (!hasVideo || strcasecmp(hasVideo, "yes")) {
        ALOGE("no video: %s", hasVideo ? hasVideo : "null");
        return false;
    }

    mFrameMemory = mRetriever->getFrameAtTime(0,
            IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
            mOutputColor, true /*metaOnly*/);
    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
        ALOGE("getFrameAtTime: videoFrame is a nullptr");
        return false;
    }

    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());

    ALOGV("Meta dimension %dx%d, display %dx%d, angle %d, iccSize %d",
            videoFrame->mWidth,
            videoFrame->mHeight,
            videoFrame->mDisplayWidth,
            videoFrame->mDisplayHeight,
            videoFrame->mRotationAngle,
            videoFrame->mIccSize);

    if (frameInfo != nullptr) {
        frameInfo->set(
                videoFrame->mWidth,
                videoFrame->mHeight,
                videoFrame->mRotationAngle,
                videoFrame->mBytesPerPixel,
                videoFrame->mIccSize,
                videoFrame->getFlattenedIccData());
    }
    return true;
}

bool HeifDecoderImpl::getEncodedColor(HeifEncodedColor* /*outColor*/) const {
    ALOGW("getEncodedColor: not implemented!");
    return false;
}

bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) {
    switch(heifColor) {
        case kHeifColorFormat_RGB565:
        {
            mOutputColor = HAL_PIXEL_FORMAT_RGB_565;
            return true;
        }
        case kHeifColorFormat_RGBA_8888:
        {
            mOutputColor = HAL_PIXEL_FORMAT_RGBA_8888;
            return true;
        }
        case kHeifColorFormat_BGRA_8888:
        {
            mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888;
            return true;
        }
        default:
            break;
    }
    ALOGE("Unsupported output color format %d", heifColor);
    return false;
}

bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
    mFrameMemory = mRetriever->getFrameAtTime(0,
            IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
        ALOGE("getFrameAtTime: videoFrame is a nullptr");
        return false;
    }

    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
    ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
            videoFrame->mWidth,
            videoFrame->mHeight,
            videoFrame->mDisplayWidth,
            videoFrame->mDisplayHeight,
            videoFrame->mRotationAngle,
            videoFrame->mRowBytes,
            videoFrame->mSize);

    if (frameInfo != nullptr) {
        frameInfo->set(
                videoFrame->mWidth,
                videoFrame->mHeight,
                videoFrame->mRotationAngle,
                videoFrame->mBytesPerPixel,
                videoFrame->mIccSize,
                videoFrame->getFlattenedIccData());
    }
    return true;
}

bool HeifDecoderImpl::getScanline(uint8_t* dst) {
    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
        return false;
    }
    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
    if (mCurScanline >= videoFrame->mHeight) {
        return false;
    }
    uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
    memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
    return true;
}

size_t HeifDecoderImpl::skipScanlines(size_t count) {
    if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
        return 0;
    }
    VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());

    uint32_t oldScanline = mCurScanline;
    mCurScanline += count;
    if (mCurScanline >= videoFrame->mHeight) {
        mCurScanline = videoFrame->mHeight;
    }
    return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
}

} // namespace android
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _HEIF_DECODER_IMPL_
#define _HEIF_DECODER_IMPL_

#include "include/HeifDecoderAPI.h"
#include <system/graphics.h>
#include <utils/RefBase.h>

namespace android {

class IDataSource;
class IMemory;
class MediaMetadataRetriever;

/*
 * An implementation of HeifDecoder based on Android's MediaMetadataRetriever.
 */
class HeifDecoderImpl : public HeifDecoder {
public:

    HeifDecoderImpl();
    ~HeifDecoderImpl() override;

    bool init(HeifStream* stream, HeifFrameInfo* frameInfo) override;

    bool getEncodedColor(HeifEncodedColor* outColor) const override;

    bool setOutputColor(HeifColorFormat heifColor) override;

    bool decode(HeifFrameInfo* frameInfo) override;

    bool getScanline(uint8_t* dst) override;

    size_t skipScanlines(size_t count) override;

private:
    sp<IDataSource> mDataSource;
    sp<MediaMetadataRetriever> mRetriever;
    sp<IMemory> mFrameMemory;
    android_pixel_format_t mOutputColor;
    size_t mCurScanline;
};

} // namespace android

#endif // _HEIF_DECODER_IMPL_
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _HEIF_DECODER_API_
#define _HEIF_DECODER_API_

#include <memory>

/*
 * The output color pixel format of heif decoder.
 */
typedef enum {
    kHeifColorFormat_RGB565     = 0,
    kHeifColorFormat_RGBA_8888  = 1,
    kHeifColorFormat_BGRA_8888  = 2,
} HeifColorFormat;

/*
 * The color spaces encoded in the heif image.
 */
typedef enum {
    kHeifEncodedColor_RGB = 0,
    kHeifEncodedColor_YUV = 1,
    kHeifEncodedColor_CMYK = 2,
} HeifEncodedColor;

/*
 * Represents a color converted (RGB-based) video frame
 */
struct HeifFrameInfo
{
    HeifFrameInfo() :
        mWidth(0), mHeight(0), mRotationAngle(0), mBytesPerPixel(0),
        mIccSize(0), mIccData(nullptr) {}

    // update the frame info, will make a copy of |iccData| internally
    void set(uint32_t width, uint32_t height, int32_t rotation, uint32_t bpp,
            uint32_t iccSize, uint8_t* iccData) {
        mWidth = width;
        mHeight = height;
        mRotationAngle = rotation;
        mBytesPerPixel = bpp;

        if (mIccData != nullptr) {
            mIccData.reset(nullptr);
        }
        mIccSize = iccSize;
        if (iccSize > 0) {
            mIccData.reset(new uint8_t[iccSize]);
            if (mIccData.get() != nullptr) {
                memcpy(mIccData.get(), iccData, iccSize);
            } else {
                mIccSize = 0;
            }
        }
    }

    // Intentional public access modifiers:
    uint32_t mWidth;
    uint32_t mHeight;
    int32_t  mRotationAngle;           // Rotation angle, clockwise, should be multiple of 90
    uint32_t mBytesPerPixel;           // Number of bytes for one pixel
    uint32_t mIccSize;                 // Number of bytes in mIccData
    std::unique_ptr<uint8_t> mIccData; // Actual ICC data, memory is owned by this structure
};

/*
 * Abstract interface to provide data to HeifDecoder.
 */
struct HeifStream {
    HeifStream() {}

    virtual ~HeifStream() {}

    /*
     * Reads or skips size number of bytes. return the number of bytes actually
     * read or skipped.
     * If |buffer| == NULL, skip size bytes, return how many were skipped.
     * If |buffer| != NULL, copy size bytes into buffer, return how many were copied.
     */
    virtual size_t read(void* buffer, size_t size) = 0;

    /*
     * Rewinds to the beginning of the stream. Returns true if the stream is known
     * to be at the beginning after this call returns.
     */
    virtual bool rewind() = 0;

    /*
     * Seeks to an absolute position in the stream. If this cannot be done, returns false.
     * If an attempt is made to seek past the end of the stream, the position will be set
     * to the end of the stream.
     */
    virtual bool seek(size_t /*position*/) = 0;

    /** Returns true if this stream can report its total length. */
    virtual bool hasLength() const = 0;

    /** Returns the total length of the stream. If this cannot be done, returns 0. */
    virtual size_t getLength() const = 0;

private:
    HeifStream(const HeifFrameInfo&) = delete;
    HeifStream& operator=(const HeifFrameInfo&) = delete;
};

/*
 * Abstract interface to decode heif images from a HeifStream data source.
 */
struct HeifDecoder {
    HeifDecoder() {}

    virtual ~HeifDecoder() {}

    /*
     * Returns true if it successfully sets outColor to the encoded color,
     * and false otherwise.
     */
    virtual bool getEncodedColor(HeifEncodedColor* outColor) const = 0;

    /*
     * Returns true if it successfully sets the output color format to color,
     * and false otherwise.
     */
    virtual bool setOutputColor(HeifColorFormat color) = 0;

    /*
     * Returns true if it successfully initialize heif decoder with source,
     * and false otherwise. |frameInfo| will be filled with information of
     * the primary picture upon success and unmodified upon failure.
     * Takes ownership of |stream| regardless of result.
     */
    virtual bool init(HeifStream* stream, HeifFrameInfo* frameInfo) = 0;

    /*
     * Decode the picture internally, returning whether it succeeded. |frameInfo|
     * will be filled with information of the primary picture upon success and
     * unmodified upon failure.
     *
     * After this succeeded, getScanline can be called to read the scanlines
     * that were decoded.
     */
    virtual bool decode(HeifFrameInfo* frameInfo) = 0;

    /*
     * Read the next scanline (in top-down order), returns true upon success
     * and false otherwise.
     */
    virtual bool getScanline(uint8_t* dst) = 0;

    /*
     * Skip the next |count| scanlines, returns true upon success and
     * false otherwise.
     */
    virtual size_t skipScanlines(size_t count) = 0;

private:
    HeifDecoder(const HeifFrameInfo&) = delete;
    HeifDecoder& operator=(const HeifFrameInfo&) = delete;
};

/*
 * Creates a HeifDecoder. Returns a HeifDecoder instance upon success, or NULL
 * if the creation has failed.
 */
HeifDecoder* createHeifDecoder();

#endif // _HEIF_DECODER_API_