Merge changes from topic 'gralloc_usage_flags' into oc-mr1-dev
* changes:
Camera: Switch usage flags to 64-bit unsigned
Camera: Remove dead legacy code
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 51050cd..a9d4dd1 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -30,14 +30,41 @@
class VideoFrame
{
public:
- VideoFrame(): mWidth(0), mHeight(0), mDisplayWidth(0), mDisplayHeight(0), mSize(0),
- mRotationAngle(0), mData(0) {}
+ // Construct a VideoFrame object with the specified parameters,
+ // will allocate frame buffer if |allocate| is set to true, will
+ // allocate buffer to hold ICC data if |iccData| and |iccSize|
+ // indicate its presence.
+ VideoFrame(uint32_t width, uint32_t height,
+ uint32_t displayWidth, uint32_t displayHeight,
+ uint32_t angle, uint32_t bpp, bool allocate,
+ const void *iccData, size_t iccSize):
+ mWidth(width), mHeight(height),
+ mDisplayWidth(displayWidth), mDisplayHeight(displayHeight),
+ mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width),
+ mSize(0), mIccSize(0), mReserved(0), mData(0), mIccData(0) {
+ if (allocate) {
+ mSize = mRowBytes * mHeight;
+ mData = new uint8_t[mSize];
+ if (mData == NULL) {
+ mSize = 0;
+ }
+ }
+ if (iccData != NULL && iccSize > 0) {
+ mIccSize = iccSize;
+ mIccData = new uint8_t[iccSize];
+ if (mIccData != NULL) {
+ memcpy(mIccData, iccData, iccSize);
+ } else {
+ mIccSize = 0;
+ }
+ }
+ }
+
+ // Deep copy of both the information fields and the frame data
VideoFrame(const VideoFrame& copy) {
- mWidth = copy.mWidth;
- mHeight = copy.mHeight;
- mDisplayWidth = copy.mDisplayWidth;
- mDisplayHeight = copy.mDisplayHeight;
+ copyInfoOnly(copy);
+
mSize = copy.mSize;
mData = NULL; // initialize it first
if (mSize > 0 && copy.mData != NULL) {
@@ -48,26 +75,99 @@
mSize = 0;
}
}
- mRotationAngle = copy.mRotationAngle;
+
+ mIccSize = copy.mIccSize;
+ mIccData = NULL; // initialize it first
+ if (mIccSize > 0 && copy.mIccData != NULL) {
+ mIccData = new uint8_t[mIccSize];
+ if (mIccData != NULL) {
+ memcpy(mIccData, copy.mIccData, mIccSize);
+ } else {
+ mIccSize = 0;
+ }
+ }
}
~VideoFrame() {
if (mData != 0) {
delete[] mData;
}
+ if (mIccData != 0) {
+ delete[] mIccData;
+ }
+ }
+
+ // Copy |copy| to a flattened VideoFrame in IMemory, 'this' must point to
+ // a chunk of memory back by IMemory of size at least getFlattenedSize()
+ // of |copy|.
+ void copyFlattened(const VideoFrame& copy) {
+ copyInfoOnly(copy);
+
+ mSize = copy.mSize;
+ mData = NULL; // initialize it first
+ if (copy.mSize > 0 && copy.mData != NULL) {
+ memcpy(getFlattenedData(), copy.mData, copy.mSize);
+ }
+
+ mIccSize = copy.mIccSize;
+ mIccData = NULL; // initialize it first
+ if (copy.mIccSize > 0 && copy.mIccData != NULL) {
+ memcpy(getFlattenedIccData(), copy.mIccData, copy.mIccSize);
+ }
+ }
+
+ // Calculate the flattened size to put it in IMemory
+ size_t getFlattenedSize() const {
+ return sizeof(VideoFrame) + mSize + mIccSize;
+ }
+
+ // Get the pointer to the frame data in a flattened VideoFrame in IMemory
+ uint8_t* getFlattenedData() const {
+ return (uint8_t*)this + sizeof(VideoFrame);
+ }
+
+ // Get the pointer to the ICC data in a flattened VideoFrame in IMemory
+ uint8_t* getFlattenedIccData() const {
+ return (uint8_t*)this + sizeof(VideoFrame) + mSize;
}
// Intentional public access modifier:
- uint32_t mWidth;
- uint32_t mHeight;
- uint32_t mDisplayWidth;
- uint32_t mDisplayHeight;
+ uint32_t mWidth; // Decoded image width before rotation
+ uint32_t mHeight; // Decoded image height before rotation
+ uint32_t mDisplayWidth; // Display width before rotation
+ uint32_t mDisplayHeight; // Display height before rotation
+ int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
+ uint32_t mBytesPerPixel; // Number of bytes per pixel
+ uint32_t mRowBytes; // Number of bytes per row before rotation
uint32_t mSize; // Number of bytes in mData
- int32_t mRotationAngle; // rotation angle, clockwise, should be multiple of 90
- // mData should be 64 bit aligned to prevent additional padding
+ uint32_t mIccSize; // Number of bytes in mIccData
+ uint32_t mReserved; // (padding to make mData 64-bit aligned)
+
+ // mData should be 64-bit aligned to prevent additional padding
uint8_t* mData; // Actual binary data
- // pad structure so it's the same size on 64 bit and 32 bit
+ // pad structure so it's the same size on 64-bit and 32-bit
char mPadding[8 - sizeof(mData)];
+
+ // mIccData should be 64-bit aligned to prevent additional padding
+ uint8_t* mIccData; // Actual binary data
+ // pad structure so it's the same size on 64-bit and 32-bit
+ char mIccPadding[8 - sizeof(mIccData)];
+
+private:
+ //
+ // Utility methods used only within VideoFrame struct
+ //
+
+ // Copy the information fields only
+ void copyInfoOnly(const VideoFrame& copy) {
+ mWidth = copy.mWidth;
+ mHeight = copy.mHeight;
+ mDisplayWidth = copy.mDisplayWidth;
+ mDisplayHeight = copy.mDisplayHeight;
+ mRotationAngle = copy.mRotationAngle;
+ mBytesPerPixel = copy.mBytesPerPixel;
+ mRowBytes = copy.mRowBytes;
+ }
};
}; // namespace android
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 0125c0f..87fb40b 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -16,11 +16,14 @@
// Play sine waves using AAudio.
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
#include <aaudio/AAudio.h>
#include <aaudio/AAudioTesting.h>
+#include <asm/fcntl.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
@@ -48,6 +51,8 @@
float *floatData = nullptr;
int16_t *shortData = nullptr;
+ int testFd = -1;
+
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
@@ -95,6 +100,9 @@
goto finish;
}
+ testFd = open("/data/aaudio_temp.raw", O_CREAT | O_RDWR, S_IRWXU);
+ printf("testFd = %d, pid = %d\n", testFd, getpid());
+
// Start the stream.
printf("call player.start()\n");
result = player.start();
@@ -176,7 +184,17 @@
}
finish:
+ printf("testFd = %d, fcntl before aaudio close returns 0x%08X\n",
+ testFd, fcntl(testFd, F_GETFD));
player.close();
+ printf("testFd = %d, fcntl after aaudio close returns 0x%08X\n",
+ testFd, fcntl(testFd, F_GETFD));
+ if (::close(testFd) != 0) {
+ printf("ERROR SharedMemoryParcelable::close() of testFd = %d, errno = %s\n",
+ testFd, strerror(errno));
+ }
+ printf("testFd = %d, fcntl after close() returns 0x%08X\n", testFd, fcntl(testFd, F_GETFD));
+
delete[] floatData;
delete[] shortData;
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
index d05abb0..1a97555 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp
@@ -28,6 +28,7 @@
#include "binding/RingBufferParcelable.h"
#include "binding/AudioEndpointParcelable.h"
+using android::base::unique_fd;
using android::NO_ERROR;
using android::status_t;
using android::Parcel;
@@ -49,7 +50,8 @@
* Add the file descriptor to the table.
* @return index in table or negative error
*/
-int32_t AudioEndpointParcelable::addFileDescriptor(int fd, int32_t sizeInBytes) {
+int32_t AudioEndpointParcelable::addFileDescriptor(const unique_fd& fd,
+ int32_t sizeInBytes) {
if (mNumSharedMemories >= MAX_SHARED_MEMORIES) {
return AAUDIO_ERROR_OUT_OF_RANGE;
}
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 993075c..aa8573f 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -20,6 +20,7 @@
#include <stdint.h>
//#include <sys/mman.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
@@ -47,7 +48,7 @@
* Add the file descriptor to the table.
* @return index in table or negative error
*/
- int32_t addFileDescriptor(int fd, int32_t sizeInBytes);
+ int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);
virtual status_t writeToParcel(Parcel* parcel) const override;
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index b582b99..90217ab 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -24,11 +24,13 @@
#include <sys/mman.h>
#include <aaudio/AAudio.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcelable.h>
#include <utility/AAudioUtilities.h>
#include "binding/SharedMemoryParcelable.h"
+using android::base::unique_fd;
using android::NO_ERROR;
using android::status_t;
using android::Parcel;
@@ -39,17 +41,19 @@
SharedMemoryParcelable::SharedMemoryParcelable() {}
SharedMemoryParcelable::~SharedMemoryParcelable() {};
-void SharedMemoryParcelable::setup(int fd, int32_t sizeInBytes) {
- mFd = fd;
+void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) {
+ mFd.reset(dup(fd.get())); // store a duplicate fd
+ ALOGV("SharedMemoryParcelable::setup(%d -> %d, %d) this = %p\n",
+ fd.get(), mFd.get(), sizeInBytes, this);
mSizeInBytes = sizeInBytes;
-
}
status_t SharedMemoryParcelable::writeToParcel(Parcel* parcel) const {
status_t status = parcel->writeInt32(mSizeInBytes);
if (status != NO_ERROR) return status;
if (mSizeInBytes > 0) {
- status = parcel->writeDupFileDescriptor(mFd);
+ ALOGV("SharedMemoryParcelable::writeToParcel() mFd = %d, this = %p\n", mFd.get(), this);
+ status = parcel->writeUniqueFileDescriptor(mFd);
ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d",
status);
}
@@ -62,15 +66,16 @@
return status;
}
if (mSizeInBytes > 0) {
- // Keep the original FD until you are done with the mFd.
- // If you close it in here then it will prevent mFd from working.
- int originalFd = parcel->readFileDescriptor();
- ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? originalFd = %d\n", originalFd);
- mFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0);
- ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mFd = %d\n", mFd);
- if (mFd == -1) {
- status = -errno;
- ALOGE("SharedMemoryParcelable readFromParcel fcntl() failed : %d", status);
+ // The Parcel owns the file descriptor and will close it later.
+ unique_fd mmapFd;
+ status = parcel->readUniqueFileDescriptor(&mmapFd);
+ if (status != NO_ERROR) {
+ ALOGE("SharedMemoryParcelable::readFromParcel() readUniqueFileDescriptor() failed : %d",
+ status);
+ } else {
+ // Resolve the memory now while we still have the FD from the Parcel.
+ // Closing the FD will not affect the shared memory once mmap() has been called.
+ status = AAudioConvert_androidToAAudioResult(resolveSharedMemory(mmapFd));
}
}
return status;
@@ -85,44 +90,50 @@
}
mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
}
- if (mFd != -1) {
- ALOGV("SharedMemoryParcelable::close() LEAK? mFd = %d\n", mFd);
- if(::close(mFd) < 0) {
- int err = errno;
- ALOGE("SharedMemoryParcelable close failed for fd = %d, errno = %d (%s)",
- mFd, err, strerror(err));
- }
- mFd = -1;
+ return AAUDIO_OK;
+}
+
+aaudio_result_t SharedMemoryParcelable::resolveSharedMemory(const unique_fd& fd) {
+ mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd.get(), 0);
+ if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
+ ALOGE("SharedMemoryParcelable mmap() failed for fd = %d, errno = %s",
+ fd.get(), strerror(errno));
+ return AAUDIO_ERROR_INTERNAL;
}
return AAUDIO_OK;
}
aaudio_result_t SharedMemoryParcelable::resolve(int32_t offsetInBytes, int32_t sizeInBytes,
void **regionAddressPtr) {
-
if (offsetInBytes < 0) {
ALOGE("SharedMemoryParcelable illegal offsetInBytes = %d", offsetInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
} else if ((offsetInBytes + sizeInBytes) > mSizeInBytes) {
ALOGE("SharedMemoryParcelable out of range, offsetInBytes = %d, "
- "sizeInBytes = %d, mSizeInBytes = %d",
+ "sizeInBytes = %d, mSizeInBytes = %d",
offsetInBytes, sizeInBytes, mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+
+ aaudio_result_t result = AAUDIO_OK;
+
if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
- MAP_SHARED, mFd, 0);
- if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- ALOGE("SharedMemoryParcelable mmap failed for fd = %d, errno = %s",
- mFd, strerror(errno));
- return AAUDIO_ERROR_INTERNAL;
+ if (mFd.get() != -1) {
+ result = resolveSharedMemory(mFd);
+ } else {
+ ALOGE("SharedMemoryParcelable has no file descriptor for shared memory.");
+ result = AAUDIO_ERROR_INTERNAL;
}
}
- *regionAddressPtr = mResolvedAddress + offsetInBytes;
- ALOGV("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
- ALOGV("SharedMemoryParcelable offset by %d, *regionAddressPtr = %p",
- offsetInBytes, *regionAddressPtr);
- return AAUDIO_OK;
+
+ if (result == AAUDIO_OK && mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
+ *regionAddressPtr = mResolvedAddress + offsetInBytes;
+ ALOGV("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
+ ALOGV("SharedMemoryParcelable offset by %d, *regionAddressPtr = %p",
+ offsetInBytes, *regionAddressPtr);
+ }
+ return result;
}
int32_t SharedMemoryParcelable::getSizeInBytes() {
@@ -134,17 +145,11 @@
ALOGE("SharedMemoryParcelable invalid mSizeInBytes = %d", mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
- if (mSizeInBytes > 0) {
- if (mFd == -1) {
- ALOGE("SharedMemoryParcelable uninitialized mFd = %d", mFd);
- return AAUDIO_ERROR_INTERNAL;
- }
- }
return AAUDIO_OK;
}
void SharedMemoryParcelable::dump() {
- ALOGD("SharedMemoryParcelable mFd = %d", mFd);
+ ALOGD("SharedMemoryParcelable mFd = %d", mFd.get());
ALOGD("SharedMemoryParcelable mSizeInBytes = %d", mSizeInBytes);
ALOGD("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index c5107d3..2a634e0 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -18,15 +18,12 @@
#define ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
#include <stdint.h>
-
#include <sys/mman.h>
+
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
-using android::status_t;
-using android::Parcel;
-using android::Parcelable;
-
namespace aaudio {
// Arbitrary limits for sanity checks. TODO remove after debugging.
@@ -37,17 +34,24 @@
/**
* This is a parcelable description of a shared memory referenced by a file descriptor.
* It may be divided into several regions.
+ * The memory can be shared using Binder or simply shared between threads.
*/
-class SharedMemoryParcelable : public Parcelable {
+class SharedMemoryParcelable : public android::Parcelable {
public:
SharedMemoryParcelable();
virtual ~SharedMemoryParcelable();
- void setup(int fd, int32_t sizeInBytes);
+ /**
+ * Make a dup() of the fd and store it for later use.
+ *
+ * @param fd
+ * @param sizeInBytes
+ */
+ void setup(const android::base::unique_fd& fd, int32_t sizeInBytes);
- virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual android::status_t writeToParcel(android::Parcel* parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ virtual android::status_t readFromParcel(const android::Parcel* parcel) override;
// mmap() shared memory
aaudio_result_t resolve(int32_t offsetInBytes, int32_t sizeInBytes, void **regionAddressPtr);
@@ -55,8 +59,6 @@
// munmap() any mapped memory
aaudio_result_t close();
- bool isFileDescriptorSafe();
-
int32_t getSizeInBytes();
aaudio_result_t validate();
@@ -67,9 +69,11 @@
#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
- int mFd = -1;
- int32_t mSizeInBytes = 0;
- uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+ aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
+
+ android::base::unique_fd mFd;
+ int32_t mSizeInBytes = 0;
+ uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/tests/test_marshalling.cpp b/media/libaaudio/tests/test_marshalling.cpp
index 79beed6..c51fbce 100644
--- a/media/libaaudio/tests/test_marshalling.cpp
+++ b/media/libaaudio/tests/test_marshalling.cpp
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <math.h>
+#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <cutils/ashmem.h>
@@ -28,6 +29,7 @@
#include <aaudio/AAudio.h>
#include <binding/AudioEndpointParcelable.h>
+using android::base::unique_fd;
using namespace android;
using namespace aaudio;
@@ -48,7 +50,7 @@
SharedMemoryParcelable sharedMemoryA;
SharedMemoryParcelable sharedMemoryB;
const size_t memSizeBytes = 840;
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemoryA.setup(fd, memSizeBytes);
void *region1;
@@ -81,7 +83,7 @@
SharedRegionParcelable sharedRegionA;
SharedRegionParcelable sharedRegionB;
const size_t memSizeBytes = 840;
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemories[0].setup(fd, memSizeBytes);
int32_t regionOffset1 = 32;
@@ -119,7 +121,7 @@
const int32_t counterSizeBytes = sizeof(int64_t);
const size_t memSizeBytes = dataSizeBytes + (2 * counterSizeBytes);
- int fd = ashmem_create_region("TestMarshalling", memSizeBytes);
+ unique_fd fd(ashmem_create_region("TestMarshalling Z", memSizeBytes));
ASSERT_LE(0, fd);
sharedMemories[0].setup(fd, memSizeBytes);
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
index b49b975..48d3a32 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -49,6 +49,9 @@
mEffect.clear();
hardware::IPCThreadState::self()->flushCommands();
}
+ if (mEfGroup) {
+ EventFlag::deleteEventFlag(&mEfGroup);
+ }
}
// static
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 7abf09f..a462f3a 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -124,6 +124,9 @@
cc_library_shared {
name: "libmedia_omx",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
defaults: ["libmedia_omx_defaults"],
}
diff --git a/media/libmedia/include/media/mediametadataretriever.h b/media/libmedia/include/media/mediametadataretriever.h
index aa2c868..65c266b 100644
--- a/media/libmedia/include/media/mediametadataretriever.h
+++ b/media/libmedia/include/media/mediametadataretriever.h
@@ -79,7 +79,7 @@
status_t setDataSource(
const sp<IDataSource>& dataSource, const char *mime = NULL);
sp<IMemory> getFrameAtTime(int64_t timeUs, int option,
- int colorFormat = 0, bool metaOnly = false);
+ int colorFormat = HAL_PIXEL_FORMAT_RGB_565, bool metaOnly = false);
sp<IMemory> extractAlbumArt();
const char* extractMetadata(int keyCode);
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 47c5aaf..5a468f3 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -211,7 +211,7 @@
ALOGE("failed to capture a video frame");
return NULL;
}
- size_t size = sizeof(VideoFrame) + frame->mSize;
+ size_t size = frame->getFlattenedSize();
sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient");
if (heap == NULL) {
ALOGE("failed to create MemoryDealer");
@@ -225,16 +225,7 @@
return NULL;
}
VideoFrame *frameCopy = static_cast<VideoFrame *>(mThumbnail->pointer());
- frameCopy->mWidth = frame->mWidth;
- frameCopy->mHeight = frame->mHeight;
- frameCopy->mDisplayWidth = frame->mDisplayWidth;
- frameCopy->mDisplayHeight = frame->mDisplayHeight;
- frameCopy->mSize = frame->mSize;
- frameCopy->mRotationAngle = frame->mRotationAngle;
- ALOGV("rotation: %d", frameCopy->mRotationAngle);
- frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame);
- memcpy(frameCopy->mData, frame->mData, frame->mSize);
- frameCopy->mData = 0;
+ frameCopy->copyFlattened(*frame);
delete frame; // Fix memory leakage
return mThumbnail;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8fe255b..ac187cc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -343,7 +343,7 @@
format, mSurface, crypto, 0 /* flags */);
if (err != OK) {
- ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
+ ALOGE("Failed to configure [%s] decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
mCodec.clear();
handleError(err);
@@ -372,7 +372,7 @@
err = mCodec->start();
if (err != OK) {
- ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
+ ALOGE("Failed to start [%s] decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
mCodec.clear();
handleError(err);
@@ -460,6 +460,12 @@
if (notifyComplete) {
mResumePending = true;
}
+
+ if (mCodec == NULL) {
+ ALOGE("[%s] onResume without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return;
+ }
mCodec->start();
}
@@ -481,7 +487,7 @@
}
if (err != OK) {
- ALOGE("failed to flush %s (err=%d)", mComponentName.c_str(), err);
+ ALOGE("failed to flush [%s] (err=%d)", mComponentName.c_str(), err);
handleError(err);
// finish with posting kWhatFlushCompleted.
// we attempt to release the buffers even if flush fails.
@@ -530,7 +536,7 @@
releaseAndResetMediaBuffers();
if (err != OK) {
- ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
+ ALOGE("failed to release [%s] (err=%d)", mComponentName.c_str(), err);
handleError(err);
// finish with posting kWhatShutdownCompleted.
}
@@ -631,10 +637,17 @@
return false;
}
+ if (mCodec == NULL) {
+ ALOGE("[%s] handleAnInputBuffer without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
sp<MediaCodecBuffer> buffer;
mCodec->getInputBuffer(index, &buffer);
if (buffer == NULL) {
+ ALOGE("[%s] handleAnInputBuffer, failed to get input buffer", mComponentName.c_str());
handleError(UNKNOWN_ERROR);
return false;
}
@@ -697,11 +710,18 @@
size_t size,
int64_t timeUs,
int32_t flags) {
+ if (mCodec == NULL) {
+ ALOGE("[%s] handleAnOutputBuffer without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
// CHECK_LT(bufferIx, mOutputBuffers.size());
sp<MediaCodecBuffer> buffer;
mCodec->getOutputBuffer(index, &buffer);
if (buffer == NULL) {
+ ALOGE("[%s] handleAnOutputBuffer, failed to get output buffer", mComponentName.c_str());
handleError(UNKNOWN_ERROR);
return false;
}
@@ -949,6 +969,12 @@
}
bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
+ if (mCodec == NULL) {
+ ALOGE("[%s] onInputBufferFetched without a valid codec", mComponentName.c_str());
+ handleError(NO_INIT);
+ return false;
+ }
+
size_t bufferIx;
CHECK(msg->findSize("buffer-ix", &bufferIx));
CHECK_LT(bufferIx, mInputBuffers.size());
@@ -979,7 +1005,7 @@
}
if (streamErr != ERROR_END_OF_STREAM) {
- ALOGE("Stream error for %s (err=%d), EOS %s queued",
+ ALOGE("Stream error for [%s] (err=%d), EOS %s queued",
mComponentName.c_str(),
streamErr,
err == OK ? "successfully" : "unsuccessfully");
@@ -1073,7 +1099,7 @@
} // no cryptInfo
if (err != OK) {
- ALOGE("onInputBufferFetched: queue%sInputBuffer failed for %s (err=%d, %s)",
+ ALOGE("onInputBufferFetched: queue%sInputBuffer failed for [%s] (err=%d, %s)",
(cryptInfo != NULL ? "Secure" : ""),
mComponentName.c_str(), err, errorDetailMsg.c_str());
handleError(err);
@@ -1102,7 +1128,9 @@
}
}
- if (msg->findInt32("render", &render) && render) {
+ if (mCodec == NULL) {
+ err = NO_INIT;
+ } else if (msg->findInt32("render", &render) && render) {
int64_t timestampNs;
CHECK(msg->findInt64("timestampNs", ×tampNs));
err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
@@ -1111,7 +1139,7 @@
err = mCodec->releaseOutputBuffer(bufferIx);
}
if (err != OK) {
- ALOGE("failed to release output buffer for %s (err=%d)",
+ ALOGE("failed to release output buffer for [%s] (err=%d)",
mComponentName.c_str(), err);
handleError(err);
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 19973bd..348abf8 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -30,6 +30,7 @@
"FrameRenderTracker.cpp",
"HTTPBase.cpp",
"HevcUtils.cpp",
+ "ItemTable.cpp",
"JPEGSource.cpp",
"MP3Extractor.cpp",
"MPEG2TSWriter.cpp",
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a5760d1..c22053e 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -92,6 +92,48 @@
return true;
}
+bool DataSource::getUInt16Var(off64_t offset, uint16_t *x, size_t size) {
+ if (size == 2) {
+ return getUInt16(offset, x);
+ }
+ if (size == 1) {
+ uint8_t tmp;
+ if (readAt(offset, &tmp, 1) == 1) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DataSource::getUInt32Var(off64_t offset, uint32_t *x, size_t size) {
+ if (size == 4) {
+ return getUInt32(offset, x);
+ }
+ if (size == 2) {
+ uint16_t tmp;
+ if (getUInt16(offset, &tmp)) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DataSource::getUInt64Var(off64_t offset, uint64_t *x, size_t size) {
+ if (size == 8) {
+ return getUInt64(offset, x);
+ }
+ if (size == 4) {
+ uint32_t tmp;
+ if (getUInt32(offset, &tmp)) {
+ *x = tmp;
+ return true;
+ }
+ }
+ return false;
+}
+
status_t DataSource::getSize(off64_t *size) {
*size = 0;
diff --git a/media/libstagefright/ItemTable.cpp b/media/libstagefright/ItemTable.cpp
new file mode 100644
index 0000000..b7ff21b
--- /dev/null
+++ b/media/libstagefright/ItemTable.cpp
@@ -0,0 +1,1544 @@
+/*
+ * 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_TAG "ItemTable"
+//#define LOG_NDEBUG 0
+
+#include <include/ItemTable.h>
+#include <media/MediaDefs.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <utils/Log.h>
+
+namespace android {
+
+namespace heif {
+
+/////////////////////////////////////////////////////////////////////
+//
+// struct to keep track of one image item
+//
+
+struct ImageItem {
+ friend struct ItemReference;
+ friend struct ItemProperty;
+
+ ImageItem() : ImageItem(0) {}
+ ImageItem(uint32_t _type) : type(_type),
+ rows(0), columns(0), width(0), height(0), rotation(0),
+ offset(0), size(0), nextTileIndex(0) {}
+
+ bool isGrid() const {
+ return type == FOURCC('g', 'r', 'i', 'd');
+ }
+
+ status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
+ if (reset) {
+ nextTileIndex = 0;
+ }
+ if (nextTileIndex >= dimgRefs.size()) {
+ return ERROR_END_OF_STREAM;
+ }
+ *nextTileItemId = dimgRefs[nextTileIndex++];
+ return OK;
+ }
+
+ uint32_t type;
+ int32_t rows;
+ int32_t columns;
+ int32_t width;
+ int32_t height;
+ int32_t rotation;
+ off64_t offset;
+ size_t size;
+ sp<ABuffer> hvcc;
+ sp<ABuffer> icc;
+
+ Vector<uint32_t> thumbnails;
+ Vector<uint32_t> dimgRefs;
+ size_t nextTileIndex;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// ISO boxes
+//
+
+struct Box {
+protected:
+ Box(const sp<DataSource> source, uint32_t type) :
+ mDataSource(source), mType(type) {}
+
+ virtual ~Box() {}
+
+ virtual status_t onChunkData(
+ uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
+ return OK;
+ }
+
+ inline uint32_t type() const { return mType; }
+
+ inline sp<DataSource> source() const { return mDataSource; }
+
+ status_t parseChunk(off64_t *offset);
+
+ status_t parseChunks(off64_t offset, size_t size);
+
+private:
+ sp<DataSource> mDataSource;
+ uint32_t mType;
+};
+
+status_t Box::parseChunk(off64_t *offset) {
+ if (*offset < 0) {
+ ALOGE("b/23540914");
+ return ERROR_MALFORMED;
+ }
+ uint32_t hdr[2];
+ if (mDataSource->readAt(*offset, hdr, 8) < 8) {
+ return ERROR_IO;
+ }
+ uint64_t chunk_size = ntohl(hdr[0]);
+ int32_t chunk_type = ntohl(hdr[1]);
+ off64_t data_offset = *offset + 8;
+
+ if (chunk_size == 1) {
+ if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ data_offset += 8;
+
+ if (chunk_size < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
+ return ERROR_MALFORMED;
+ }
+ } else if (chunk_size == 0) {
+ // This shouldn't happen since we should never be top level
+ ALOGE("invalid chunk size 0 for non-top level box");
+ return ERROR_MALFORMED;
+ } else if (chunk_size < 8) {
+ // The smallest valid chunk is 8 bytes long.
+ ALOGE("invalid chunk size: %lld", (long long)chunk_size);
+ return ERROR_MALFORMED;
+ }
+
+ char chunk[5];
+ MakeFourCCString(chunk_type, chunk);
+ ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
+
+ off64_t chunk_data_size = chunk_size - (data_offset - *offset);
+ if (chunk_data_size < 0) {
+ ALOGE("b/23540914");
+ return ERROR_MALFORMED;
+ }
+
+ status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+ *offset += chunk_size;
+ return OK;
+}
+
+status_t Box::parseChunks(off64_t offset, size_t size) {
+ off64_t stopOffset = offset + size;
+ while (offset < stopOffset) {
+ status_t err = parseChunk(&offset);
+ if (err != OK) {
+ return err;
+ }
+ }
+ if (offset != stopOffset) {
+ return ERROR_MALFORMED;
+ }
+ return OK;
+}
+
+///////////////////////////////////////////////////////////////////////
+
+struct FullBox : public Box {
+protected:
+ FullBox(const sp<DataSource> source, uint32_t type) :
+ Box(source, type), mVersion(0), mFlags(0) {}
+
+ inline uint8_t version() const { return mVersion; }
+
+ inline uint32_t flags() const { return mFlags; }
+
+ status_t parseFullBoxHeader(off64_t *offset, size_t *size);
+
+private:
+ uint8_t mVersion;
+ uint32_t mFlags;
+};
+
+status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
+ if (*size < 4) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->readAt(*offset, &mVersion, 1)) {
+ return ERROR_IO;
+ }
+ if (!source()->getUInt24(*offset + 1, &mFlags)) {
+ return ERROR_IO;
+ }
+ *offset += 4;
+ *size -= 4;
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// PrimaryImage box
+//
+
+struct PitmBox : public FullBox {
+ PitmBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('p', 'i', 't', 'm')) {}
+
+ status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
+};
+
+status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t itemIdSize = (version() == 0) ? 2 : 4;
+ if (size < itemIdSize) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t itemId;
+ if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
+ return ERROR_IO;
+ }
+
+ ALOGV("primary id %d", itemId);
+ *primaryItemId = itemId;
+
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemLocation related boxes
+//
+
+struct ExtentEntry {
+ uint64_t extentIndex;
+ uint64_t extentOffset;
+ uint64_t extentLength;
+};
+
+struct ItemLoc {
+ ItemLoc() : ItemLoc(0, 0, 0, 0) {}
+ ItemLoc(uint32_t item_id, uint16_t construction_method,
+ uint16_t data_reference_index, uint64_t base_offset) :
+ itemId(item_id),
+ constructionMethod(construction_method),
+ dataReferenceIndex(data_reference_index),
+ baseOffset(base_offset) {}
+
+ void addExtent(const ExtentEntry& extent) {
+ extents.push_back(extent);
+ }
+
+ status_t getLoc(off64_t *offset, size_t *size,
+ off64_t idatOffset, size_t idatSize) const {
+ // TODO: fix extent handling, fix constructionMethod = 2
+ CHECK(extents.size() == 1);
+ if (constructionMethod == 0) {
+ *offset = baseOffset + extents[0].extentOffset;
+ *size = extents[0].extentLength;
+ return OK;
+ } else if (constructionMethod == 1) {
+ if (baseOffset + extents[0].extentOffset + extents[0].extentLength
+ > idatSize) {
+ return ERROR_MALFORMED;
+ }
+ *offset = baseOffset + extents[0].extentOffset + idatOffset;
+ *size = extents[0].extentLength;
+ return OK;
+ }
+ return ERROR_UNSUPPORTED;
+ }
+
+ // parsed info
+ uint32_t itemId;
+ uint16_t constructionMethod;
+ uint16_t dataReferenceIndex;
+ off64_t baseOffset;
+ Vector<ExtentEntry> extents;
+};
+
+struct IlocBox : public FullBox {
+ IlocBox(const sp<DataSource> source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
+ FullBox(source, FOURCC('i', 'l', 'o', 'c')),
+ mItemLocs(itemLocs), mHasConstructMethod1(false) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ bool hasConstructMethod1() { return mHasConstructMethod1; }
+
+private:
+ static bool isSizeFieldValid(uint32_t offset_size) {
+ return offset_size == 0 || offset_size == 4 || offset_size == 8;
+ }
+ KeyedVector<uint32_t, ItemLoc> *mItemLocs;
+ bool mHasConstructMethod1;
+};
+
+status_t IlocBox::parse(off64_t offset, size_t size) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+ if (version() > 2) {
+ ALOGE("%s: invalid version %d", __FUNCTION__, version());
+ return ERROR_MALFORMED;
+ }
+
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+ uint8_t offset_size;
+ if (!source()->readAt(offset++, &offset_size, 1)) {
+ return ERROR_IO;
+ }
+ uint8_t length_size = (offset_size & 0xF);
+ offset_size >>= 4;
+
+ uint8_t base_offset_size;
+ if (!source()->readAt(offset++, &base_offset_size, 1)) {
+ return ERROR_IO;
+ }
+ uint8_t index_size = 0;
+ if (version() == 1 || version() == 2) {
+ index_size = (base_offset_size & 0xF);
+ }
+ base_offset_size >>= 4;
+ size -= 2;
+
+ if (!isSizeFieldValid(offset_size)
+ || !isSizeFieldValid(length_size)
+ || !isSizeFieldValid(base_offset_size)
+ || !isSizeFieldValid((index_size))) {
+ ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
+ offset_size, length_size, base_offset_size, index_size);
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t item_count;
+ size_t itemFieldSize = version() < 2 ? 2 : 4;
+ if (size < itemFieldSize) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
+ return ERROR_IO;
+ }
+
+ ALOGV("item_count %lld", (long long) item_count);
+ offset += itemFieldSize;
+ size -= itemFieldSize;
+
+ for (size_t i = 0; i < item_count; i++) {
+ uint32_t item_id;
+ if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("item[%zu]: id %lld", i, (long long)item_id);
+ offset += itemFieldSize;
+
+ uint8_t construction_method = 0;
+ if (version() == 1 || version() == 2) {
+ uint8_t buf[2];
+ if (!source()->readAt(offset, buf, 2)) {
+ return ERROR_IO;
+ }
+ construction_method = (buf[1] & 0xF);
+ ALOGV("construction_method %d", construction_method);
+ if (construction_method == 1) {
+ mHasConstructMethod1 = true;
+ }
+
+ offset += 2;
+ }
+
+ uint16_t data_reference_index;
+ if (!source()->getUInt16(offset, &data_reference_index)) {
+ return ERROR_IO;
+ }
+ ALOGV("data_reference_index %d", data_reference_index);
+ if (data_reference_index != 0) {
+ // we don't support reference to other files
+ return ERROR_UNSUPPORTED;
+ }
+ offset += 2;
+
+ uint64_t base_offset = 0;
+ if (base_offset_size != 0) {
+ if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
+ return ERROR_IO;
+ }
+ offset += base_offset_size;
+ }
+ ALOGV("base_offset %lld", (long long) base_offset);
+
+ ssize_t index = mItemLocs->add(item_id, ItemLoc(
+ item_id, construction_method, data_reference_index, base_offset));
+ ItemLoc &item = mItemLocs->editValueAt(index);
+
+ uint16_t extent_count;
+ if (!source()->getUInt16(offset, &extent_count)) {
+ return ERROR_IO;
+ }
+ ALOGV("extent_count %d", extent_count);
+
+ if (extent_count > 1 && (offset_size == 0 || length_size == 0)) {
+ // if the item is dividec into more than one extents, offset and
+ // length must be present.
+ return ERROR_MALFORMED;
+ }
+ offset += 2;
+
+ for (size_t j = 0; j < extent_count; j++) {
+ uint64_t extent_index = 1; // default=1
+ if ((version() == 1 || version() == 2) && (index_size > 0)) {
+ if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
+ return ERROR_IO;
+ }
+ // TODO: add support for this mode
+ offset += index_size;
+ ALOGV("extent_index %lld", (long long)extent_index);
+ }
+
+ uint64_t extent_offset = 0; // default=0
+ if (offset_size > 0) {
+ if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
+ return ERROR_IO;
+ }
+ offset += offset_size;
+ }
+ ALOGV("extent_offset %lld", (long long)extent_offset);
+
+ uint64_t extent_length = 0; // this indicates full length of file
+ if (length_size > 0) {
+ if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
+ return ERROR_IO;
+ }
+ offset += length_size;
+ }
+ ALOGV("extent_length %lld", (long long)extent_length);
+
+ item.addExtent({ extent_index, extent_offset, extent_length });
+ }
+ }
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemReference related boxes
+//
+
+struct ItemReference : public Box, public RefBase {
+ ItemReference(const sp<DataSource> source, uint32_t type, uint32_t itemIdSize) :
+ Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ uint32_t itemId() { return mItemId; }
+
+ void apply(KeyedVector<uint32_t, ImageItem> &itemIdToImageMap) const {
+ ssize_t imageIndex = itemIdToImageMap.indexOfKey(mItemId);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ return;
+ }
+
+ ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
+
+ if (type() == FOURCC('d', 'i', 'm', 'g')) {
+ ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
+ if (!image.dimgRefs.empty()) {
+ ALOGW("dimgRefs if not clean!");
+ }
+ image.dimgRefs.appendVector(mRefs);
+ } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
+ for (size_t i = 0; i < mRefs.size(); i++) {
+ imageIndex = itemIdToImageMap.indexOfKey(mRefs[i]);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ continue;
+ }
+ ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
+ ImageItem &image = itemIdToImageMap.editValueAt(imageIndex);
+ if (!image.thumbnails.empty()) {
+ ALOGW("already has thumbnails!");
+ }
+ image.thumbnails.push_back(mItemId);
+ }
+ } else {
+ ALOGW("ignoring unsupported ref type 0x%x", type());
+ }
+ }
+
+private:
+ uint32_t mItemId;
+ uint32_t mRefIdSize;
+ Vector<uint32_t> mRefs;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
+};
+
+status_t ItemReference::parse(off64_t offset, size_t size) {
+ if (size < mRefIdSize + 2) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
+ return ERROR_IO;
+ }
+ offset += mRefIdSize;
+
+ uint16_t count;
+ if (!source()->getUInt16(offset, &count)) {
+ return ERROR_IO;
+ }
+ offset += 2;
+ size -= (mRefIdSize + 2);
+
+ if (size < count * mRefIdSize) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ uint32_t refItemId;
+ if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
+ return ERROR_IO;
+ }
+ offset += mRefIdSize;
+ mRefs.push_back(refItemId);
+ ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
+ }
+
+ return OK;
+}
+
+struct IrefBox : public FullBox {
+ IrefBox(const sp<DataSource> source, Vector<sp<ItemReference> > *itemRefs) :
+ FullBox(source, FOURCC('i', 'r', 'e', 'f')), mRefIdSize(0), mItemRefs(itemRefs) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ uint32_t mRefIdSize;
+ Vector<sp<ItemReference> > *mItemRefs;
+};
+
+status_t IrefBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ mRefIdSize = (version() == 0) ? 2 : 4;
+ return parseChunks(offset, size);
+}
+
+status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
+
+ status_t err = itemRef->parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ mItemRefs->push_back(itemRef);
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemProperty related boxes
+//
+
+struct AssociationEntry {
+ uint32_t itemId;
+ bool essential;
+ uint16_t index;
+};
+
+struct ItemProperty : public RefBase {
+ ItemProperty() {}
+
+ virtual void attachTo(ImageItem &/*image*/) const {
+ ALOGW("Unrecognized property");
+ }
+ virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
+ ALOGW("Unrecognized property");
+ return OK;
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
+};
+
+struct IspeBox : public FullBox, public ItemProperty {
+ IspeBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('i', 's', 'p', 'e')), mWidth(0), mHeight(0) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.width = mWidth;
+ image.height = mHeight;
+ }
+
+private:
+ uint32_t mWidth;
+ uint32_t mHeight;
+};
+
+status_t IspeBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (size < 8) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32(offset, &mWidth)
+ || !source()->getUInt32(offset + 4, &mHeight)) {
+ return ERROR_IO;
+ }
+ ALOGV("property ispe: %dx%d", mWidth, mHeight);
+
+ return OK;
+}
+
+struct HvccBox : public Box, public ItemProperty {
+ HvccBox(const sp<DataSource> source) :
+ Box(source, FOURCC('h', 'v', 'c', 'C')) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.hvcc = mHVCC;
+ }
+
+private:
+ sp<ABuffer> mHVCC;
+};
+
+status_t HvccBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ mHVCC = new ABuffer(size);
+
+ if (mHVCC->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ ALOGV("property hvcC");
+
+ return OK;
+}
+
+struct IrotBox : public Box, public ItemProperty {
+ IrotBox(const sp<DataSource> source) :
+ Box(source, FOURCC('i', 'r', 'o', 't')), mAngle(0) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.rotation = mAngle * 90;
+ }
+
+private:
+ uint8_t mAngle;
+};
+
+status_t IrotBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ if (size < 1) {
+ return ERROR_MALFORMED;
+ }
+ if (source()->readAt(offset, &mAngle, 1) != 1) {
+ return ERROR_IO;
+ }
+ mAngle &= 0x3;
+ ALOGV("property irot: %d", mAngle);
+
+ return OK;
+}
+
+struct ColrBox : public Box, public ItemProperty {
+ ColrBox(const sp<DataSource> source) :
+ Box(source, FOURCC('c', 'o', 'l', 'r')) {}
+
+ status_t parse(off64_t offset, size_t size) override;
+
+ void attachTo(ImageItem &image) const override {
+ image.icc = mICCData;
+ }
+
+private:
+ sp<ABuffer> mICCData;
+};
+
+status_t ColrBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t colour_type;
+ if (!source()->getUInt32(offset, &colour_type)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+ if (colour_type == FOURCC('n', 'c', 'l', 'x')) {
+ return OK;
+ }
+ if ((colour_type != FOURCC('r', 'I', 'C', 'C')) &&
+ (colour_type != FOURCC('p', 'r', 'o', 'f'))) {
+ return ERROR_MALFORMED;
+ }
+
+ mICCData = new ABuffer(size);
+ if (mICCData->data() == NULL) {
+ ALOGE("b/28471206");
+ return NO_MEMORY;
+ }
+
+ if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ ALOGV("property Colr: size %zd", size);
+ return OK;
+}
+
+struct IpmaBox : public FullBox {
+ IpmaBox(const sp<DataSource> source, Vector<AssociationEntry> *associations) :
+ FullBox(source, FOURCC('i', 'p', 'm', 'a')), mAssociations(associations) {}
+
+ status_t parse(off64_t offset, size_t size);
+private:
+ Vector<AssociationEntry> *mAssociations;
+};
+
+status_t IpmaBox::parse(off64_t offset, size_t size) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t entryCount;
+ if (!source()->getUInt32(offset, &entryCount)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+
+ for (size_t k = 0; k < entryCount; ++k) {
+ uint32_t itemId = 0;
+ size_t itemIdSize = (version() < 1) ? 2 : 4;
+
+ if (size < itemIdSize + 1) {
+ return ERROR_MALFORMED;
+ }
+
+ if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
+ return ERROR_IO;
+ }
+ offset += itemIdSize;
+ size -= itemIdSize;
+
+ uint8_t associationCount;
+ if (!source()->readAt(offset, &associationCount, 1)) {
+ return ERROR_IO;
+ }
+ offset++;
+ size--;
+
+ for (size_t i = 0; i < associationCount; ++i) {
+ size_t propIndexSize = (flags() & 1) ? 2 : 1;
+ if (size < propIndexSize) {
+ return ERROR_MALFORMED;
+ }
+ uint16_t propIndex;
+ if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
+ return ERROR_IO;
+ }
+ offset += propIndexSize;
+ size -= propIndexSize;
+ uint16_t bitmask = (1 << (8 * propIndexSize - 1));
+ AssociationEntry entry = {
+ .itemId = itemId,
+ .essential = !!(propIndex & bitmask),
+ .index = (uint16_t) (propIndex & ~bitmask)
+ };
+
+ ALOGV("item id %d associated to property %d (essential %d)",
+ itemId, entry.index, entry.essential);
+
+ mAssociations->push_back(entry);
+ }
+ }
+
+ return OK;
+}
+
+struct IpcoBox : public Box {
+ IpcoBox(const sp<DataSource> source, Vector<sp<ItemProperty> > *properties) :
+ Box(source, FOURCC('i', 'p', 'c', 'o')), mItemProperties(properties) {}
+
+ status_t parse(off64_t offset, size_t size);
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<sp<ItemProperty> > *mItemProperties;
+};
+
+status_t IpcoBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+ // push dummy as the index is 1-based
+ mItemProperties->push_back(new ItemProperty());
+ return parseChunks(offset, size);
+}
+
+status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ sp<ItemProperty> itemProperty;
+ switch(type) {
+ case FOURCC('h', 'v', 'c', 'C'):
+ {
+ itemProperty = new HvccBox(source());
+ break;
+ }
+ case FOURCC('i', 's', 'p', 'e'):
+ {
+ itemProperty = new IspeBox(source());
+ break;
+ }
+ case FOURCC('i', 'r', 'o', 't'):
+ {
+ itemProperty = new IrotBox(source());
+ break;
+ }
+ case FOURCC('c', 'o', 'l', 'r'):
+ {
+ itemProperty = new ColrBox(source());
+ break;
+ }
+ default:
+ {
+ // push dummy to maintain correct item property index
+ itemProperty = new ItemProperty();
+ break;
+ }
+ }
+ status_t err = itemProperty->parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ mItemProperties->push_back(itemProperty);
+ return OK;
+}
+
+struct IprpBox : public Box {
+ IprpBox(const sp<DataSource> source,
+ Vector<sp<ItemProperty> > *properties,
+ Vector<AssociationEntry> *associations) :
+ Box(source, FOURCC('i', 'p', 'r', 'p')),
+ mProperties(properties), mAssociations(associations) {}
+
+ status_t parse(off64_t offset, size_t size);
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<sp<ItemProperty> > *mProperties;
+ Vector<AssociationEntry> *mAssociations;
+};
+
+status_t IprpBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseChunks(offset, size);
+ if (err != OK) {
+ return err;
+ }
+ return OK;
+}
+
+status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ switch(type) {
+ case FOURCC('i', 'p', 'c', 'o'):
+ {
+ IpcoBox ipcoBox(source(), mProperties);
+ return ipcoBox.parse(offset, size);
+ }
+ case FOURCC('i', 'p', 'm', 'a'):
+ {
+ IpmaBox ipmaBox(source(), mAssociations);
+ return ipmaBox.parse(offset, size);
+ }
+ default:
+ {
+ ALOGW("Unrecognized box.");
+ break;
+ }
+ }
+ return OK;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// ItemInfo related boxes
+//
+struct ItemInfo {
+ uint32_t itemId;
+ uint32_t itemType;
+};
+
+struct InfeBox : public FullBox {
+ InfeBox(const sp<DataSource> source) :
+ FullBox(source, FOURCC('i', 'n', 'f', 'e')) {}
+
+ status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
+
+private:
+ bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
+};
+
+bool InfeBox::parseNullTerminatedString(
+ off64_t *offset, size_t *size, String8 *out) {
+ char tmp[256];
+ size_t len = 0;
+ off64_t newOffset = *offset;
+ off64_t stopOffset = *offset + *size;
+ while (newOffset < stopOffset) {
+ if (!source()->readAt(newOffset++, &tmp[len], 1)) {
+ return false;
+ }
+ if (tmp[len] == 0) {
+ out->append(tmp, len);
+
+ *offset = newOffset;
+ *size = stopOffset - newOffset;
+
+ return true;
+ }
+ if (++len >= sizeof(tmp)) {
+ out->append(tmp, len);
+ len = 0;
+ }
+ }
+ return false;
+}
+
+status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (version() == 0 || version() == 1) {
+ if (size < 4) {
+ return ERROR_MALFORMED;
+ }
+ uint16_t item_id;
+ if (!source()->getUInt16(offset, &item_id)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_id %d", item_id);
+ uint16_t item_protection_index;
+ if (!source()->getUInt16(offset + 2, &item_protection_index)) {
+ return ERROR_IO;
+ }
+ offset += 4;
+ size -= 4;
+
+ String8 item_name;
+ if (!parseNullTerminatedString(&offset, &size, &item_name)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_type;
+ if (!parseNullTerminatedString(&offset, &size, &content_type)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_encoding;
+ if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (version() == 1) {
+ uint32_t extension_type;
+ if (!source()->getUInt32(offset, &extension_type)) {
+ return ERROR_IO;
+ }
+ offset++;
+ size--;
+ // TODO: handle this case
+ }
+ } else { // version >= 2
+ uint32_t item_id;
+ size_t itemIdSize = (version() == 2) ? 2 : 4;
+ if (size < itemIdSize + 6) {
+ return ERROR_MALFORMED;
+ }
+ if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_id %d", item_id);
+ offset += itemIdSize;
+ uint16_t item_protection_index;
+ if (!source()->getUInt16(offset, &item_protection_index)) {
+ return ERROR_IO;
+ }
+ ALOGV("item_protection_index %d", item_protection_index);
+ offset += 2;
+ uint32_t item_type;
+ if (!source()->getUInt32(offset, &item_type)) {
+ return ERROR_IO;
+ }
+
+ itemInfo->itemId = item_id;
+ itemInfo->itemType = item_type;
+
+ char itemTypeString[5];
+ MakeFourCCString(item_type, itemTypeString);
+ ALOGV("item_type %s", itemTypeString);
+ offset += 4;
+ size -= itemIdSize + 6;
+
+ String8 item_name;
+ if (!parseNullTerminatedString(&offset, &size, &item_name)) {
+ return ERROR_MALFORMED;
+ }
+ ALOGV("item_name %s", item_name.c_str());
+
+ if (item_type == FOURCC('m', 'i', 'm', 'e')) {
+ String8 content_type;
+ if (!parseNullTerminatedString(&offset, &size, &content_type)) {
+ return ERROR_MALFORMED;
+ }
+
+ String8 content_encoding;
+ if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
+ return ERROR_MALFORMED;
+ }
+ } else if (item_type == FOURCC('u', 'r', 'i', ' ')) {
+ String8 item_uri_type;
+ if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
+ return ERROR_MALFORMED;
+ }
+ }
+ }
+ return OK;
+}
+
+struct IinfBox : public FullBox {
+ IinfBox(const sp<DataSource> source, Vector<ItemInfo> *itemInfos) :
+ FullBox(source, FOURCC('i', 'i', 'n', 'f')),
+ mItemInfos(itemInfos), mHasGrids(false) {}
+
+ status_t parse(off64_t offset, size_t size);
+
+ bool hasGrids() { return mHasGrids; }
+
+protected:
+ status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
+
+private:
+ Vector<ItemInfo> *mItemInfos;
+ bool mHasGrids;
+};
+
+status_t IinfBox::parse(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ status_t err = parseFullBoxHeader(&offset, &size);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t entryCountSize = version() == 0 ? 2 : 4;
+ if (size < entryCountSize) {
+ return ERROR_MALFORMED;
+ }
+ uint32_t entry_count;
+ if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
+ return ERROR_IO;
+ }
+ ALOGV("entry_count %d", entry_count);
+
+ off64_t stopOffset = offset + size;
+ offset += entryCountSize;
+ for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
+ ALOGV("entry %zu", i);
+ status_t err = parseChunk(&offset);
+ if (err != OK) {
+ return err;
+ }
+ }
+ if (offset != stopOffset) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
+ if (type != FOURCC('i', 'n', 'f', 'e')) {
+ return OK;
+ }
+
+ InfeBox infeBox(source());
+ ItemInfo itemInfo;
+ status_t err = infeBox.parse(offset, size, &itemInfo);
+ if (err != OK) {
+ return err;
+ }
+ mItemInfos->push_back(itemInfo);
+ mHasGrids |= (itemInfo.itemType == FOURCC('g', 'r', 'i', 'd'));
+ return OK;
+}
+
+//////////////////////////////////////////////////////////////////
+
+ItemTable::ItemTable(const sp<DataSource> &source)
+ : mDataSource(source),
+ mPrimaryItemId(0),
+ mIdatOffset(0),
+ mIdatSize(0),
+ mImageItemsValid(false),
+ mCurrentImageIndex(0) {
+ mRequiredBoxes.insert('iprp');
+ mRequiredBoxes.insert('iloc');
+ mRequiredBoxes.insert('pitm');
+ mRequiredBoxes.insert('iinf');
+}
+
+ItemTable::~ItemTable() {}
+
+status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
+ switch(type) {
+ case FOURCC('i', 'l', 'o', 'c'):
+ {
+ return parseIlocBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'i', 'n', 'f'):
+ {
+ return parseIinfBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'p', 'r', 'p'):
+ {
+ return parseIprpBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('p', 'i', 't', 'm'):
+ {
+ return parsePitmBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'd', 'a', 't'):
+ {
+ return parseIdatBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'r', 'e', 'f'):
+ {
+ return parseIrefBox(data_offset, chunk_data_size);
+ }
+ case FOURCC('i', 'p', 'r', 'o'):
+ {
+ ALOGW("ipro box not supported!");
+ break;
+ }
+ default:
+ {
+ ALOGW("unrecognized box type: 0x%x", type);
+ break;
+ }
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IlocBox ilocBox(mDataSource, &mItemLocs);
+ status_t err = ilocBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (ilocBox.hasConstructMethod1()) {
+ mRequiredBoxes.insert('idat');
+ }
+
+ return buildImageItemsIfPossible('iloc');
+}
+
+status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IinfBox iinfBox(mDataSource, &mItemInfos);
+ status_t err = iinfBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ if (iinfBox.hasGrids()) {
+ mRequiredBoxes.insert('iref');
+ }
+
+ return buildImageItemsIfPossible('iinf');
+}
+
+status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ PitmBox pitmBox(mDataSource);
+ status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('pitm');
+}
+
+status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
+ status_t err = iprpBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('iprp');
+}
+
+status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
+ ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ // only remember the offset and size of idat box for later use
+ mIdatOffset = offset;
+ mIdatSize = size;
+
+ return buildImageItemsIfPossible('idat');
+}
+
+status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
+ ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
+
+ IrefBox irefBox(mDataSource, &mItemReferences);
+ status_t err = irefBox.parse(offset, size);
+ if (err != OK) {
+ return err;
+ }
+
+ return buildImageItemsIfPossible('iref');
+}
+
+status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
+ if (mImageItemsValid) {
+ return OK;
+ }
+
+ mBoxesSeen.insert(type);
+
+ // need at least 'iprp', 'iloc', 'pitm', 'iinf';
+ // need 'idat' if any items used construction_method of 2;
+ // need 'iref' if there are grids.
+ if (!std::includes(
+ mBoxesSeen.begin(), mBoxesSeen.end(),
+ mRequiredBoxes.begin(), mRequiredBoxes.end())) {
+ return OK;
+ }
+
+ ALOGV("building image table...");
+
+ for (size_t i = 0; i < mItemInfos.size(); i++) {
+ const ItemInfo &info = mItemInfos[i];
+
+
+ // ignore non-image items
+ if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
+ info.itemType != FOURCC('h', 'v', 'c', '1')) {
+ continue;
+ }
+
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(info.itemId);
+ if (imageIndex >= 0) {
+ ALOGW("ignoring duplicate image item id %d", info.itemId);
+ continue;
+ }
+
+ ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
+ if (ilocIndex < 0) {
+ ALOGE("iloc missing for image item id %d", info.itemId);
+ continue;
+ }
+ const ItemLoc &iloc = mItemLocs[ilocIndex];
+
+ off64_t offset;
+ size_t size;
+ if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
+ return ERROR_MALFORMED;
+ }
+
+ ImageItem image(info.itemType);
+
+ ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
+
+ if (image.isGrid()) {
+ if (size > 12) {
+ return ERROR_MALFORMED;
+ }
+ uint8_t buf[12];
+ if (!mDataSource->readAt(offset, buf, size)) {
+ return ERROR_IO;
+ }
+
+ image.rows = buf[2] + 1;
+ image.columns = buf[3] + 1;
+
+ ALOGV("rows %d, columans %d", image.rows, image.columns);
+ } else {
+ image.offset = offset;
+ image.size = size;
+ }
+ mItemIdToImageMap.add(info.itemId, image);
+ }
+
+ for (size_t i = 0; i < mAssociations.size(); i++) {
+ attachProperty(mAssociations[i]);
+ }
+
+ for (size_t i = 0; i < mItemReferences.size(); i++) {
+ mItemReferences[i]->apply(mItemIdToImageMap);
+ }
+
+ mImageItemsValid = true;
+ return OK;
+}
+
+void ItemTable::attachProperty(const AssociationEntry &association) {
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(association.itemId);
+
+ // ignore non-image items
+ if (imageIndex < 0) {
+ return;
+ }
+
+ uint16_t propertyIndex = association.index;
+ if (propertyIndex >= mItemProperties.size()) {
+ ALOGW("Ignoring invalid property index %d", propertyIndex);
+ return;
+ }
+
+ ALOGV("attach property %d to item id %d)",
+ propertyIndex, association.itemId);
+
+ mItemProperties[propertyIndex]->attachTo(
+ mItemIdToImageMap.editValueAt(imageIndex));
+}
+
+sp<MetaData> ItemTable::getImageMeta() {
+ if (!mImageItemsValid) {
+ return NULL;
+ }
+
+ ssize_t imageIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (imageIndex < 0) {
+ ALOGE("Primary item id %d not found!", mPrimaryItemId);
+ return NULL;
+ }
+
+ ALOGV("primary image index %zu", imageIndex);
+
+ const ImageItem *image = &mItemIdToImageMap[imageIndex];
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+
+ ALOGV("setting image size %dx%d", image->width, image->height);
+ meta->setInt32(kKeyWidth, image->width);
+ meta->setInt32(kKeyHeight, image->height);
+ if (image->rotation != 0) {
+ meta->setInt32(kKeyRotation, image->rotation);
+ }
+ meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+
+ if (!image->thumbnails.empty()) {
+ ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(image->thumbnails[0]);
+ if (thumbnailIndex >= 0) {
+ const ImageItem &thumbnail = mItemIdToImageMap[thumbnailIndex];
+
+ meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
+ meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
+ meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
+ thumbnail.hvcc->data(), thumbnail.hvcc->size());
+ ALOGV("thumbnail meta: %dx%d, index %zd",
+ thumbnail.width, thumbnail.height, thumbnailIndex);
+ } else {
+ ALOGW("Referenced thumbnail does not exist!");
+ }
+ }
+
+ if (image->isGrid()) {
+ ssize_t tileIndex = mItemIdToImageMap.indexOfKey(image->dimgRefs[0]);
+ if (tileIndex < 0) {
+ return NULL;
+ }
+ meta->setInt32(kKeyGridRows, image->rows);
+ meta->setInt32(kKeyGridCols, image->columns);
+
+ image = &mItemIdToImageMap.editValueAt(tileIndex);
+ }
+
+ if (image->hvcc == NULL) {
+ ALOGE("hvcc is missing!");
+ return NULL;
+ }
+ meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
+
+ if (image->icc != NULL) {
+ meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
+ }
+ return meta;
+}
+
+uint32_t ItemTable::countImages() const {
+ return mImageItemsValid ? mItemIdToImageMap.size() : 0;
+}
+
+status_t ItemTable::findPrimaryImage(uint32_t *imageIndex) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t index = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (index < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ *imageIndex = index;
+ return OK;
+}
+
+status_t ItemTable::findThumbnail(uint32_t *imageIndex) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ ssize_t primaryIndex = mItemIdToImageMap.indexOfKey(mPrimaryItemId);
+ if (primaryIndex < 0) {
+ ALOGE("Primary item id %d not found!", mPrimaryItemId);
+ return ERROR_MALFORMED;
+ }
+
+ const ImageItem &primaryImage = mItemIdToImageMap[primaryIndex];
+ if (primaryImage.thumbnails.empty()) {
+ ALOGW("Using primary in place of thumbnail.");
+ *imageIndex = primaryIndex;
+ return OK;
+ }
+
+ ssize_t thumbnailIndex = mItemIdToImageMap.indexOfKey(
+ primaryImage.thumbnails[0]);
+ if (thumbnailIndex < 0) {
+ ALOGE("Thumbnail item id %d not found!", primaryImage.thumbnails[0]);
+ return ERROR_MALFORMED;
+ }
+
+ *imageIndex = thumbnailIndex;
+ return OK;
+}
+
+status_t ItemTable::getImageOffsetAndSize(
+ uint32_t *imageIndex, off64_t *offset, size_t *size) {
+ if (!mImageItemsValid) {
+ return INVALID_OPERATION;
+ }
+
+ if (imageIndex != NULL) {
+ if (*imageIndex >= mItemIdToImageMap.size()) {
+ ALOGE("Bad image index!");
+ return BAD_VALUE;
+ }
+ mCurrentImageIndex = *imageIndex;
+ }
+
+ ImageItem &image = mItemIdToImageMap.editValueAt(mCurrentImageIndex);
+ if (image.isGrid()) {
+ uint32_t tileItemId;
+ status_t err = image.getNextTileItemId(&tileItemId, imageIndex != NULL);
+ if (err != OK) {
+ return err;
+ }
+ ssize_t tileImageIndex = mItemIdToImageMap.indexOfKey(tileItemId);
+ if (tileImageIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+ *offset = mItemIdToImageMap[tileImageIndex].offset;
+ *size = mItemIdToImageMap[tileImageIndex].size;
+ } else {
+ if (imageIndex == NULL) {
+ // For single images, we only allow it to be read once, after that
+ // it's EOS. New image index must be requested each time.
+ return ERROR_END_OF_STREAM;
+ }
+ *offset = mItemIdToImageMap[mCurrentImageIndex].offset;
+ *size = mItemIdToImageMap[mCurrentImageIndex].size;
+ }
+
+ return OK;
+}
+
+} // namespace heif
+
+} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index a0d9db0..5ef0f56 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -28,6 +28,7 @@
#include "include/MPEG4Extractor.h"
#include "include/SampleTable.h"
+#include "include/ItemTable.h"
#include "include/ESDS.h"
#include <media/stagefright/foundation/ABitReader.h>
@@ -73,7 +74,8 @@
const sp<SampleTable> &sampleTable,
Vector<SidxEntry> &sidx,
const Trex *trex,
- off64_t firstMoofOffset);
+ off64_t firstMoofOffset,
+ const sp<ItemTable> &itemTable);
virtual status_t init();
virtual status_t start(MetaData *params = NULL);
@@ -135,6 +137,9 @@
uint8_t *mSrcBuffer;
+ bool mIsHEIF;
+ sp<ItemTable> mItemTable;
+
size_t parseNALSize(const uint8_t *data) const;
status_t parseChunk(off64_t *offset);
status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
@@ -340,6 +345,7 @@
mInitCheck(NO_INIT),
mHeaderTimescale(0),
mIsQT(false),
+ mIsHEIF(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData),
@@ -483,7 +489,8 @@
status_t err;
bool sawMoovOrSidx = false;
- while (!(sawMoovOrSidx && (mMdatFound || mMoofFound))) {
+ while (!((sawMoovOrSidx && (mMdatFound || mMoofFound)) ||
+ (mIsHEIF && (mItemTable != NULL) && mItemTable->isValid()))) {
off64_t orig_offset = offset;
err = parseChunk(&offset, 0);
@@ -535,6 +542,29 @@
mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
free(buf);
}
+
+ if (mIsHEIF) {
+ sp<MetaData> meta = mItemTable->getImageMeta();
+ if (meta == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ Track *track = mLastTrack;
+ if (track != NULL) {
+ ALOGW("track is set before metadata is fully processed");
+ } else {
+ track = new Track;
+ track->next = NULL;
+ mFirstTrack = mLastTrack = track;
+ }
+
+ track->meta = meta;
+ track->meta->setInt32(kKeyTrackID, 0);
+ track->includes_expensive_metadata = false;
+ track->skipTrack = false;
+ track->timescale = 0;
+ }
+
return mInitCheck;
}
@@ -1993,6 +2023,28 @@
break;
}
+ case FOURCC('i', 'l', 'o', 'c'):
+ case FOURCC('i', 'i', 'n', 'f'):
+ case FOURCC('i', 'p', 'r', 'p'):
+ case FOURCC('p', 'i', 't', 'm'):
+ case FOURCC('i', 'd', 'a', 't'):
+ case FOURCC('i', 'r', 'e', 'f'):
+ case FOURCC('i', 'p', 'r', 'o'):
+ {
+ if (mIsHEIF) {
+ if (mItemTable == NULL) {
+ mItemTable = new ItemTable(mDataSource);
+ }
+ status_t err = mItemTable->parse(
+ chunk_type, data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
+ }
+ *offset += chunk_size;
+ break;
+ }
+
case FOURCC('m', 'e', 'a', 'n'):
case FOURCC('n', 'a', 'm', 'e'):
case FOURCC('d', 'a', 't', 'a'):
@@ -2340,6 +2392,7 @@
off64_t stop_offset = *offset + chunk_size;
uint32_t numCompatibleBrands = (chunk_data_size - 8) / 4;
+ std::set<uint32_t> brandSet;
for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
if (i == 1) {
// Skip this index, it refers to the minorVersion,
@@ -2353,10 +2406,15 @@
}
brand = ntohl(brand);
- if (brand == FOURCC('q', 't', ' ', ' ')) {
- mIsQT = true;
- break;
- }
+ brandSet.insert(brand);
+ }
+
+ if (brandSet.count(FOURCC('q', 't', ' ', ' ')) > 0) {
+ mIsQT = true;
+ } else if (brandSet.count(FOURCC('m', 'i', 'f', '1')) > 0
+ && brandSet.count(FOURCC('h', 'e', 'i', 'c')) > 0) {
+ mIsHEIF = true;
+ ALOGV("identified HEIF image");
}
*offset = stop_offset;
@@ -3305,7 +3363,7 @@
sp<MPEG4Source> source = new MPEG4Source(this,
track->meta, mDataSource, track->timescale, track->sampleTable,
- mSidxEntries, trex, mMoofOffset);
+ mSidxEntries, trex, mMoofOffset, mItemTable);
if (source->init() != OK) {
return NULL;
}
@@ -3694,7 +3752,8 @@
const sp<SampleTable> &sampleTable,
Vector<SidxEntry> &sidx,
const Trex *trex,
- off64_t firstMoofOffset)
+ off64_t firstMoofOffset,
+ const sp<ItemTable> &itemTable)
: mOwner(owner),
mFormat(format),
mDataSource(dataSource),
@@ -3719,7 +3778,9 @@
mGroup(NULL),
mBuffer(NULL),
mWantsNALFragments(false),
- mSrcBuffer(NULL) {
+ mSrcBuffer(NULL),
+ mIsHEIF(itemTable != NULL),
+ mItemTable(itemTable) {
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
@@ -4494,77 +4555,93 @@
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
- uint32_t findFlags = 0;
- switch (mode) {
- case ReadOptions::SEEK_PREVIOUS_SYNC:
- findFlags = SampleTable::kFlagBefore;
- break;
- case ReadOptions::SEEK_NEXT_SYNC:
- findFlags = SampleTable::kFlagAfter;
- break;
- case ReadOptions::SEEK_CLOSEST_SYNC:
- case ReadOptions::SEEK_CLOSEST:
- findFlags = SampleTable::kFlagClosest;
- break;
- default:
- CHECK(!"Should not be here.");
- break;
- }
+ if (mIsHEIF) {
+ CHECK(mSampleTable == NULL);
+ CHECK(mItemTable != NULL);
- uint32_t sampleIndex;
- status_t err = mSampleTable->findSampleAtTime(
- seekTimeUs, 1000000, mTimescale,
- &sampleIndex, findFlags);
-
- if (mode == ReadOptions::SEEK_CLOSEST) {
- // We found the closest sample already, now we want the sync
- // sample preceding it (or the sample itself of course), even
- // if the subsequent sync sample is closer.
- findFlags = SampleTable::kFlagBefore;
- }
-
- uint32_t syncSampleIndex;
- if (err == OK) {
- err = mSampleTable->findSyncSampleNear(
- sampleIndex, &syncSampleIndex, findFlags);
- }
-
- uint32_t sampleTime;
- if (err == OK) {
- err = mSampleTable->getMetaDataForSample(
- sampleIndex, NULL, NULL, &sampleTime);
- }
-
- if (err != OK) {
- if (err == ERROR_OUT_OF_RANGE) {
- // An attempt to seek past the end of the stream would
- // normally cause this ERROR_OUT_OF_RANGE error. Propagating
- // this all the way to the MediaPlayer would cause abnormal
- // termination. Legacy behaviour appears to be to behave as if
- // we had seeked to the end of stream, ending normally.
- err = ERROR_END_OF_STREAM;
+ status_t err;
+ if (seekTimeUs >= 0) {
+ err = mItemTable->findPrimaryImage(&mCurrentSampleIndex);
+ } else {
+ err = mItemTable->findThumbnail(&mCurrentSampleIndex);
}
- ALOGV("end of stream");
- return err;
- }
+ if (err != OK) {
+ return err;
+ }
+ } else {
+ uint32_t findFlags = 0;
+ switch (mode) {
+ case ReadOptions::SEEK_PREVIOUS_SYNC:
+ findFlags = SampleTable::kFlagBefore;
+ break;
+ case ReadOptions::SEEK_NEXT_SYNC:
+ findFlags = SampleTable::kFlagAfter;
+ break;
+ case ReadOptions::SEEK_CLOSEST_SYNC:
+ case ReadOptions::SEEK_CLOSEST:
+ findFlags = SampleTable::kFlagClosest;
+ break;
+ default:
+ CHECK(!"Should not be here.");
+ break;
+ }
- if (mode == ReadOptions::SEEK_CLOSEST) {
- targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
- }
+ uint32_t sampleIndex;
+ status_t err = mSampleTable->findSampleAtTime(
+ seekTimeUs, 1000000, mTimescale,
+ &sampleIndex, findFlags);
+
+ if (mode == ReadOptions::SEEK_CLOSEST) {
+ // We found the closest sample already, now we want the sync
+ // sample preceding it (or the sample itself of course), even
+ // if the subsequent sync sample is closer.
+ findFlags = SampleTable::kFlagBefore;
+ }
+
+ uint32_t syncSampleIndex;
+ if (err == OK) {
+ err = mSampleTable->findSyncSampleNear(
+ sampleIndex, &syncSampleIndex, findFlags);
+ }
+
+ uint32_t sampleTime;
+ if (err == OK) {
+ err = mSampleTable->getMetaDataForSample(
+ sampleIndex, NULL, NULL, &sampleTime);
+ }
+
+ if (err != OK) {
+ if (err == ERROR_OUT_OF_RANGE) {
+ // An attempt to seek past the end of the stream would
+ // normally cause this ERROR_OUT_OF_RANGE error. Propagating
+ // this all the way to the MediaPlayer would cause abnormal
+ // termination. Legacy behaviour appears to be to behave as if
+ // we had seeked to the end of stream, ending normally.
+ err = ERROR_END_OF_STREAM;
+ }
+ ALOGV("end of stream");
+ return err;
+ }
+
+ if (mode == ReadOptions::SEEK_CLOSEST) {
+ targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
+ }
#if 0
- uint32_t syncSampleTime;
- CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
- syncSampleIndex, NULL, NULL, &syncSampleTime));
+ uint32_t syncSampleTime;
+ CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
+ syncSampleIndex, NULL, NULL, &syncSampleTime));
- ALOGI("seek to time %lld us => sample at time %lld us, "
- "sync sample at time %lld us",
- seekTimeUs,
- sampleTime * 1000000ll / mTimescale,
- syncSampleTime * 1000000ll / mTimescale);
+ ALOGI("seek to time %lld us => sample at time %lld us, "
+ "sync sample at time %lld us",
+ seekTimeUs,
+ sampleTime * 1000000ll / mTimescale,
+ syncSampleTime * 1000000ll / mTimescale);
#endif
- mCurrentSampleIndex = syncSampleIndex;
+ mCurrentSampleIndex = syncSampleIndex;
+ }
+
if (mBuffer != NULL) {
mBuffer->release();
mBuffer = NULL;
@@ -4581,9 +4658,19 @@
if (mBuffer == NULL) {
newBuffer = true;
- status_t err =
- mSampleTable->getMetaDataForSample(
+ status_t err;
+ if (!mIsHEIF) {
+ err = mSampleTable->getMetaDataForSample(
mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
+ } else {
+ err = mItemTable->getImageOffsetAndSize(
+ options && options->getSeekTo(&seekTimeUs, &mode) ?
+ &mCurrentSampleIndex : NULL, &offset, &size);
+
+ cts = stts = 0;
+ isSyncSample = 0;
+ ALOGV("image offset %lld, size %zu", (long long)offset, size);
+ }
if (err != OK) {
return err;
@@ -5153,7 +5240,8 @@
|| !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
|| !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
|| !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)
- || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) {
+ || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
+ || !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)) {
*mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
*confidence = 0.4;
@@ -5182,6 +5270,8 @@
FOURCC('3', 'g', '2', 'a'), // 3GPP2
FOURCC('3', 'g', '2', 'b'),
+ FOURCC('m', 'i', 'f', '1'), // HEIF image
+ FOURCC('h', 'e', 'i', 'c'), // HEIF image
};
for (size_t i = 0;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 0281563..f36ff97 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -137,19 +137,164 @@
return OK;
}
+static VideoFrame *allocVideoFrame(
+ const sp<MetaData> &trackMeta, int32_t width, int32_t height, int32_t bpp, bool metaOnly) {
+ int32_t rotationAngle;
+ if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
+ rotationAngle = 0; // By default, no rotation
+ }
+
+ uint32_t type;
+ const void *iccData;
+ size_t iccSize;
+ if (!trackMeta->findData(kKeyIccProfile, &type, &iccData, &iccSize)){
+ iccData = NULL;
+ iccSize = 0;
+ }
+
+ int32_t sarWidth, sarHeight;
+ int32_t displayWidth, displayHeight;
+ if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
+ && trackMeta->findInt32(kKeySARHeight, &sarHeight)
+ && sarHeight != 0) {
+ displayWidth = (width * sarWidth) / sarHeight;
+ displayHeight = height;
+ } else if (trackMeta->findInt32(kKeyDisplayWidth, &displayWidth)
+ && trackMeta->findInt32(kKeyDisplayHeight, &displayHeight)
+ && displayWidth > 0 && displayHeight > 0
+ && width > 0 && height > 0) {
+ ALOGV("found display size %dx%d", displayWidth, displayHeight);
+ } else {
+ displayWidth = width;
+ displayHeight = height;
+ }
+
+ return new VideoFrame(width, height, displayWidth, displayHeight,
+ rotationAngle, bpp, !metaOnly, iccData, iccSize);
+}
+
+static bool getDstColorFormat(android_pixel_format_t colorFormat,
+ OMX_COLOR_FORMATTYPE *omxColorFormat, int32_t *bpp) {
+ switch (colorFormat) {
+ case HAL_PIXEL_FORMAT_RGB_565:
+ {
+ *omxColorFormat = OMX_COLOR_Format16bitRGB565;
+ *bpp = 2;
+ return true;
+ }
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ {
+ *omxColorFormat = OMX_COLOR_Format32BitRGBA8888;
+ *bpp = 4;
+ return true;
+ }
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ {
+ *omxColorFormat = OMX_COLOR_Format32bitBGRA8888;
+ *bpp = 4;
+ return true;
+ }
+ default:
+ {
+ ALOGE("Unsupported color format: %d", colorFormat);
+ break;
+ }
+ }
+ return false;
+}
+
static VideoFrame *extractVideoFrame(
const AString &componentName,
const sp<MetaData> &trackMeta,
const sp<IMediaSource> &source,
int64_t frameTimeUs,
int seekMode,
- int /*colorFormat*/,
- bool /*metaOnly*/) {
-
+ int colorFormat,
+ bool metaOnly) {
sp<MetaData> format = source->getFormat();
+ MediaSource::ReadOptions::SeekMode mode =
+ static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
+ if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
+ seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
+ ALOGE("Unknown seek mode: %d", seekMode);
+ return NULL;
+ }
+
+ int32_t dstBpp;
+ OMX_COLOR_FORMATTYPE dstFormat;
+ if (!getDstColorFormat(
+ (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
+ return NULL;
+ }
+
+ if (metaOnly) {
+ int32_t width, height;
+ CHECK(trackMeta->findInt32(kKeyWidth, &width));
+ CHECK(trackMeta->findInt32(kKeyHeight, &height));
+ return allocVideoFrame(trackMeta, width, height, dstBpp, true);
+ }
+
+ MediaSource::ReadOptions options;
+ sp<MetaData> overrideMeta;
+ if (frameTimeUs < 0) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ int64_t thumbNailTime;
+ int32_t thumbnailWidth, thumbnailHeight;
+
+ // if we have a stand-alone thumbnail, set up the override meta,
+ // and set seekTo time to -1.
+ if (trackMeta->findInt32(kKeyThumbnailWidth, &thumbnailWidth)
+ && trackMeta->findInt32(kKeyThumbnailHeight, &thumbnailHeight)
+ && trackMeta->findData(kKeyThumbnailHVCC, &type, &data, &size)){
+ overrideMeta = new MetaData(*trackMeta);
+ overrideMeta->setInt32(kKeyWidth, thumbnailWidth);
+ overrideMeta->setInt32(kKeyHeight, thumbnailHeight);
+ overrideMeta->setData(kKeyHVCC, type, data, size);
+ thumbNailTime = -1ll;
+ ALOGV("thumbnail: %dx%d", thumbnailWidth, thumbnailHeight);
+ } else if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
+ || thumbNailTime < 0) {
+ thumbNailTime = 0;
+ }
+
+ options.setSeekTo(thumbNailTime, mode);
+ } else {
+ options.setSeekTo(frameTimeUs, mode);
+ }
+
+ int32_t gridRows = 1, gridCols = 1;
+ int32_t numTiles = 1, tilesDecoded = 0;
+ if (overrideMeta == NULL) {
+ // check if we're dealing with a tiled heif
+ if (trackMeta->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
+ && trackMeta->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
+ int32_t width, height;
+ CHECK(trackMeta->findInt32(kKeyWidth, &width));
+ CHECK(trackMeta->findInt32(kKeyHeight, &height));
+
+ if ((width % gridCols == 0) && (height % gridRows == 0)) {
+ width /= gridCols;
+ height /= gridRows;
+ numTiles = gridCols * gridRows;
+
+ ALOGV("tile: %dx%d, numTiles %d", width, height, numTiles);
+
+ overrideMeta = new MetaData(*trackMeta);
+ overrideMeta->setInt32(kKeyWidth, width);
+ overrideMeta->setInt32(kKeyHeight, height);
+ }
+ }
+ if (overrideMeta == NULL) {
+ gridRows = gridCols = numTiles = 1;
+ overrideMeta = trackMeta;
+ }
+ }
+
sp<AMessage> videoFormat;
- if (convertMetaDataToMessage(trackMeta, &videoFormat) != OK) {
+ if (convertMetaDataToMessage(overrideMeta, &videoFormat) != OK) {
ALOGE("b/23680780");
ALOGW("Failed to convert meta data to message");
return NULL;
@@ -162,7 +307,8 @@
// input and output ports, if seeking to a sync frame. NOTE: This request may
// fail if component requires more than that for decoding.
bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST);
- if (!isSeekingClosest) {
+ bool decodeSingleFrame = !isSeekingClosest && (numTiles == 1);
+ if (decodeSingleFrame) {
videoFormat->setInt32("android._num-input-buffers", 1);
videoFormat->setInt32("android._num-output-buffers", 1);
}
@@ -192,30 +338,6 @@
return NULL;
}
- MediaSource::ReadOptions options;
- if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
- seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
-
- ALOGE("Unknown seek mode: %d", seekMode);
- decoder->release();
- return NULL;
- }
-
- MediaSource::ReadOptions::SeekMode mode =
- static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
-
- int64_t thumbNailTime;
- if (frameTimeUs < 0) {
- if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
- || thumbNailTime < 0) {
- thumbNailTime = 0;
- }
- options.setSeekTo(thumbNailTime, mode);
- } else {
- thumbNailTime = -1;
- options.setSeekTo(frameTimeUs, mode);
- }
-
err = source->start();
if (err != OK) {
ALOGW("source failed to start: %d (%s)", err, asString(err));
@@ -260,6 +382,8 @@
bool firstSample = true;
int64_t targetTimeUs = -1ll;
+ VideoFrame *frame = NULL;
+
do {
size_t inputIndex = -1;
int64_t ptsUs = 0ll;
@@ -284,6 +408,9 @@
if (err != OK) {
ALOGW("Input Error or EOS");
haveMoreInputs = false;
+ if (err == ERROR_END_OF_STREAM) {
+ err = OK;
+ }
break;
}
if (firstSample && isSeekingClosest) {
@@ -295,6 +422,7 @@
if (mediaBuffer->range_length() > codecBuffer->capacity()) {
ALOGE("buffer size (%zu) too large for codec input size (%zu)",
mediaBuffer->range_length(), codecBuffer->capacity());
+ haveMoreInputs = false;
err = BAD_VALUE;
} else {
codecBuffer->setRange(0, mediaBuffer->range_length());
@@ -303,19 +431,20 @@
memcpy(codecBuffer->data(),
(const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
mediaBuffer->range_length());
- if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) {
- // Only need to decode one IDR frame, unless we're seeking with CLOSEST
- // option, in which case we need to actually decode to targetTimeUs.
- haveMoreInputs = false;
- flags |= MediaCodec::BUFFER_FLAG_EOS;
- }
}
mediaBuffer->release();
break;
}
- if (err == OK && inputIndex < inputBuffers.size()) {
+ if (haveMoreInputs && inputIndex < inputBuffers.size()) {
+ if (isAvcOrHevc && IsIDR(codecBuffer) && decodeSingleFrame) {
+ // Only need to decode one IDR frame, unless we're seeking with CLOSEST
+ // option, in which case we need to actually decode to targetTimeUs.
+ haveMoreInputs = false;
+ flags |= MediaCodec::BUFFER_FLAG_EOS;
+ }
+
ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
codecBuffer->size(), ptsUs, flags);
err = decoder->queueInputBuffer(
@@ -354,11 +483,70 @@
} else if (err == OK) {
// If we're seeking with CLOSEST option and obtained a valid targetTimeUs
// from the extractor, decode to the specified frame. Otherwise we're done.
- done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
- if (!done) {
- err = decoder->releaseOutputBuffer(index);
+ sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
+
+ int32_t width, height;
+ CHECK(outputFormat != NULL);
+ CHECK(outputFormat->findInt32("width", &width));
+ CHECK(outputFormat->findInt32("height", &height));
+
+ int32_t crop_left, crop_top, crop_right, crop_bottom;
+ if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
+ crop_left = crop_top = 0;
+ crop_right = width - 1;
+ crop_bottom = height - 1;
}
+
+ if (frame == NULL) {
+ frame = allocVideoFrame(
+ overrideMeta,
+ (crop_right - crop_left + 1) * gridCols,
+ (crop_bottom - crop_top + 1) * gridRows,
+ dstBpp,
+ false /*metaOnly*/);
+ }
+
+ int32_t srcFormat;
+ CHECK(outputFormat->findInt32("color-format", &srcFormat));
+
+ ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat);
+
+ int32_t dstLeft, dstTop, dstRight, dstBottom;
+ if (numTiles == 1) {
+ dstLeft = crop_left;
+ dstTop = crop_top;
+ dstRight = crop_right;
+ dstBottom = crop_bottom;
+ } else {
+ dstLeft = tilesDecoded % gridCols * width;
+ dstTop = tilesDecoded / gridCols * height;
+ dstRight = dstLeft + width - 1;
+ dstBottom = dstTop + height - 1;
+ }
+
+ if (converter.isValid()) {
+ err = converter.convert(
+ (const uint8_t *)videoFrameBuffer->data(),
+ width, height,
+ crop_left, crop_top, crop_right, crop_bottom,
+ frame->mData,
+ frame->mWidth,
+ frame->mHeight,
+ dstLeft, dstTop, dstRight, dstBottom);
+ } else {
+ ALOGE("Unable to convert from format 0x%08x to 0x%08x",
+ srcFormat, dstFormat);
+
+ err = ERROR_UNSUPPORTED;
+ }
+
+ done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
+ if (numTiles > 1) {
+ tilesDecoded++;
+ done &= (tilesDecoded >= numTiles);
+ }
+ err = decoder->releaseOutputBuffer(index);
} else {
ALOGW("Received error %d (%s) instead of output", err, asString(err));
done = true;
@@ -368,95 +556,11 @@
}
} while (err == OK && !done);
- if (err != OK || size <= 0 || outputFormat == NULL) {
- ALOGE("Failed to decode thumbnail frame");
- source->stop();
- decoder->release();
- return NULL;
- }
-
- ALOGV("successfully decoded video frame.");
- sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
-
- if (thumbNailTime >= 0) {
- if (timeUs != thumbNailTime) {
- AString mime;
- CHECK(outputFormat->findString("mime", &mime));
-
- ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
- (long long)thumbNailTime, (long long)timeUs, mime.c_str());
- }
- }
-
- int32_t width, height;
- CHECK(outputFormat->findInt32("width", &width));
- CHECK(outputFormat->findInt32("height", &height));
-
- int32_t crop_left, crop_top, crop_right, crop_bottom;
- if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
- crop_left = crop_top = 0;
- crop_right = width - 1;
- crop_bottom = height - 1;
- }
-
- int32_t rotationAngle;
- if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
- rotationAngle = 0; // By default, no rotation
- }
-
- VideoFrame *frame = new VideoFrame;
- frame->mWidth = crop_right - crop_left + 1;
- frame->mHeight = crop_bottom - crop_top + 1;
- frame->mDisplayWidth = frame->mWidth;
- frame->mDisplayHeight = frame->mHeight;
- frame->mSize = frame->mWidth * frame->mHeight * 2;
- frame->mData = new uint8_t[frame->mSize];
- frame->mRotationAngle = rotationAngle;
-
- int32_t sarWidth, sarHeight;
- if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
- && trackMeta->findInt32(kKeySARHeight, &sarHeight)
- && sarHeight != 0) {
- frame->mDisplayWidth = (frame->mDisplayWidth * sarWidth) / sarHeight;
- } else {
- int32_t width, height;
- if (trackMeta->findInt32(kKeyDisplayWidth, &width)
- && trackMeta->findInt32(kKeyDisplayHeight, &height)
- && frame->mDisplayWidth > 0 && frame->mDisplayHeight > 0
- && width > 0 && height > 0) {
- frame->mDisplayWidth = width;
- frame->mDisplayHeight = height;
- }
- }
-
- int32_t srcFormat;
- CHECK(outputFormat->findInt32("color-format", &srcFormat));
-
- ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
-
- if (converter.isValid()) {
- err = converter.convert(
- (const uint8_t *)videoFrameBuffer->data(),
- width, height,
- crop_left, crop_top, crop_right, crop_bottom,
- frame->mData,
- frame->mWidth,
- frame->mHeight,
- 0, 0, frame->mWidth - 1, frame->mHeight - 1);
- } else {
- ALOGE("Unable to convert from format 0x%08x to RGB565", srcFormat);
-
- err = ERROR_UNSUPPORTED;
- }
-
- videoFrameBuffer.clear();
source->stop();
- decoder->releaseOutputBuffer(index);
decoder->release();
if (err != OK) {
- ALOGE("Colorconverter failed to convert frame.");
-
+ ALOGE("failed to get video frame (err %d)", err);
delete frame;
frame = NULL;
}
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index 6e04c1e..1daaf49 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_aacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftAAC2.cpp",
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 1a7ffca..4b478ea 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_aacenc",
+ vendor_available: true,
srcs: [
"basic_op/basicop2.c",
@@ -111,6 +112,10 @@
cc_library_shared {
name: "libstagefright_soft_aacenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAACEncoder2.cpp"],
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index c5ac558..5177593 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_amrnb_common",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"src/add.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index 996183b..a61fb57 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrnbdec",
+ vendor_available: true,
srcs: [
"src/a_refl.cpp",
@@ -55,13 +56,20 @@
// ],
//},
- shared_libs: ["libstagefright_amrnb_common"],
+ shared_libs: [
+ "libstagefright_amrnb_common",
+ "liblog",
+ ],
}
//###############################################################################
cc_library_shared {
name: "libstagefright_soft_amrdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMR.cpp"],
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index af0f8c2..04ed07f 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrnbenc",
+ vendor_available: true,
srcs: [
"src/amrencode.cpp",
@@ -83,6 +84,10 @@
cc_library_shared {
name: "libstagefright_soft_amrnbenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMRNBEncoder.cpp"],
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index e261c04..b932ccc 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrwbdec",
+ vendor_available: true,
srcs: [
"src/agc2_amr_wb.cpp",
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 5c5a122..d337cde 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_amrwbenc",
+ vendor_available: true,
srcs: [
"src/autocorr.c",
@@ -144,6 +145,10 @@
cc_library_shared {
name: "libstagefright_soft_amrwbenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftAMRWBEncoder.cpp"],
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 6b996a7..3fa8d7f 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_avcdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libavcdec"],
srcs: ["SoftAVCDec.cpp"],
@@ -12,7 +16,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index 49021a9..6c4311b 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_avcenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libavcenc"],
srcs: ["SoftAVCEnc.cpp"],
@@ -13,7 +17,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index b1af17b..326207b 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -27,7 +27,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
#include <OMX_IndexExt.h>
#include <OMX_VideoExt.h>
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index 021e6af..3726922 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -1,5 +1,9 @@
cc_library {
name: "libstagefright_enc_common",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["cmnMemory.c"],
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 6ac264d..5652594 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_flacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftFlacDecoder.cpp",
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index d1413f6..6197157 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -22,7 +22,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
@@ -32,5 +32,9 @@
static_libs: ["libFLAC"],
name: "libstagefright_soft_flacenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
}
diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp
index b78b689..0e6f468 100644
--- a/media/libstagefright/codecs/g711/dec/Android.bp
+++ b/media/libstagefright/codecs/g711/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_g711dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftG711.cpp"],
@@ -9,7 +13,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp
index 8e86ad6..7be86a4 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.bp
+++ b/media/libstagefright/codecs/gsm/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_gsmdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftGSM.cpp"],
@@ -23,7 +27,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp
index cd75c97..3fd1652 100644
--- a/media/libstagefright/codecs/hevcdec/Android.bp
+++ b/media/libstagefright/codecs/hevcdec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_hevcdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libhevcdec"],
srcs: ["SoftHEVC.cpp"],
@@ -22,7 +26,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 04ea075..2619131 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -1,5 +1,7 @@
cc_library_static {
name: "libstagefright_m4vh263dec",
+ vendor_available: true,
+ shared_libs: ["liblog"],
srcs: [
"src/adaptive_smooth_no_mmx.cpp",
@@ -66,6 +68,10 @@
cc_library_shared {
name: "libstagefright_soft_mpeg4dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMPEG4.cpp"],
@@ -87,7 +93,7 @@
static_libs: ["libstagefright_m4vh263dec"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index da5b162..919b9d4 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_m4vh263enc",
+ vendor_available: true,
srcs: [
"src/bitstream_io.cpp",
@@ -52,6 +53,10 @@
cc_library_shared {
name: "libstagefright_soft_mpeg4enc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMPEG4Encoder.cpp"],
@@ -75,7 +80,7 @@
static_libs: ["libstagefright_m4vh263enc"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libutils",
"liblog",
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index 6d4cb69..7b90a01 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -29,7 +29,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/Utils.h>
#include "SoftMPEG4Encoder.h"
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index 0d0a2c6..b2e8f9b 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -1,5 +1,6 @@
cc_library_static {
name: "libstagefright_mp3dec",
+ vendor_available: true,
srcs: [
"src/pvmp3_normalize.cpp",
@@ -77,6 +78,10 @@
cc_library_shared {
name: "libstagefright_soft_mp3dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftMP3.cpp"],
@@ -102,7 +107,7 @@
},
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index 0144581..ed51797 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_mpeg2dec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
static_libs: ["libmpeg2dec"],
srcs: ["SoftMPEG2.cpp"],
@@ -12,7 +16,7 @@
],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index c4242c2..249ab92 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vpxdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftVPX.cpp"],
@@ -11,7 +15,7 @@
static_libs: ["libvpx"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index 114c1be..0284719 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vpxenc",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"SoftVPXEncoder.cpp",
@@ -26,7 +30,7 @@
static_libs: ["libvpx"],
shared_libs: [
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index 5d9c4c8..2bedbda 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_opusdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftOpus.cpp"],
@@ -10,7 +14,7 @@
shared_libs: [
"libopus",
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp
index c64027b..1bd75c6 100644
--- a/media/libstagefright/codecs/raw/Android.bp
+++ b/media/libstagefright/codecs/raw/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_rawdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftRaw.cpp"],
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp
index 1a4de60..fedfb67 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.bp
+++ b/media/libstagefright/codecs/vorbis/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_soft_vorbisdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["SoftVorbis.cpp"],
@@ -10,7 +14,7 @@
shared_libs: [
"libvorbisidec",
- "libmedia",
+ "libmedia_omx",
"libstagefright_omx",
"libstagefright_foundation",
"libutils",
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 284c25f..1b9fe0f 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,5 +1,9 @@
cc_library_shared {
name: "libstagefright_flacdec",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"FLACDecoder.cpp",
@@ -31,4 +35,5 @@
"libstagefright_foundation",
"libutils",
],
+ header_libs: ["libmedia_headers"],
}
diff --git a/media/libstagefright/include/ItemTable.h b/media/libstagefright/include/ItemTable.h
new file mode 100644
index 0000000..5a6af5e
--- /dev/null
+++ b/media/libstagefright/include/ItemTable.h
@@ -0,0 +1,98 @@
+/*
+ * 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 ITEM_TABLE_H_
+#define ITEM_TABLE_H_
+
+#include <set>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MetaData;
+
+namespace heif {
+
+struct AssociationEntry;
+struct ImageItem;
+struct ItemLoc;
+struct ItemInfo;
+struct ItemProperty;
+struct ItemReference;
+
+/*
+ * ItemTable keeps track of all image items (including coded images, grids and
+ * tiles) inside a HEIF still image (ISO/IEC FDIS 23008-12.2:2017(E)).
+ */
+
+class ItemTable : public RefBase {
+public:
+ explicit ItemTable(const sp<DataSource> &source);
+
+ status_t parse(uint32_t type, off64_t offset, size_t size);
+
+ bool isValid() { return mImageItemsValid; }
+ sp<MetaData> getImageMeta();
+ uint32_t countImages() const;
+ status_t findPrimaryImage(uint32_t *imageIndex);
+ status_t findThumbnail(uint32_t *thumbnailIndex);
+ status_t getImageOffsetAndSize(
+ uint32_t *imageIndex, off64_t *offset, size_t *size);
+
+protected:
+ ~ItemTable();
+
+private:
+ sp<DataSource> mDataSource;
+
+ KeyedVector<uint32_t, ItemLoc> mItemLocs;
+ Vector<ItemInfo> mItemInfos;
+ Vector<AssociationEntry> mAssociations;
+ Vector<sp<ItemProperty> > mItemProperties;
+ Vector<sp<ItemReference> > mItemReferences;
+
+ uint32_t mPrimaryItemId;
+ off64_t mIdatOffset;
+ size_t mIdatSize;
+
+ std::set<uint32_t> mRequiredBoxes;
+ std::set<uint32_t> mBoxesSeen;
+
+ bool mImageItemsValid;
+ uint32_t mCurrentImageIndex;
+ KeyedVector<uint32_t, ImageItem> mItemIdToImageMap;
+
+ status_t parseIlocBox(off64_t offset, size_t size);
+ status_t parseIinfBox(off64_t offset, size_t size);
+ status_t parsePitmBox(off64_t offset, size_t size);
+ status_t parseIprpBox(off64_t offset, size_t size);
+ status_t parseIdatBox(off64_t offset, size_t size);
+ status_t parseIrefBox(off64_t offset, size_t size);
+
+ void attachProperty(const AssociationEntry &association);
+ status_t buildImageItemsIfPossible(uint32_t type);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ItemTable);
+};
+
+} // namespace heif
+} // namespace android
+
+#endif // ITEM_TABLE_H_
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index f847119..4a4c538 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -28,11 +28,14 @@
#include <utils/String8.h>
namespace android {
-
struct AMessage;
class DataSource;
class SampleTable;
class String8;
+namespace heif {
+class ItemTable;
+}
+using heif::ItemTable;
struct SidxEntry {
size_t mSize;
@@ -97,6 +100,7 @@
status_t mInitCheck;
uint32_t mHeaderTimescale;
bool mIsQT;
+ bool mIsHEIF;
Track *mFirstTrack, *mLastTrack;
@@ -134,6 +138,8 @@
SINF *mFirstSINF;
bool mIsDrm;
+ sp<ItemTable> mItemTable;
+
status_t parseDrmSINF(off64_t *offset, off64_t data_offset);
status_t parseTrackHeader(off64_t data_offset, off64_t data_size);
diff --git a/media/libstagefright/include/media/stagefright/DataSource.h b/media/libstagefright/include/media/stagefright/DataSource.h
index 63eccea..bd863ba 100644
--- a/media/libstagefright/include/media/stagefright/DataSource.h
+++ b/media/libstagefright/include/media/stagefright/DataSource.h
@@ -73,6 +73,11 @@
bool getUInt32(off64_t offset, uint32_t *x);
bool getUInt64(off64_t offset, uint64_t *x);
+ // read either int<N> or int<2N> into a uint<2N>_t, size is the int size in bytes.
+ bool getUInt16Var(off64_t offset, uint16_t *x, size_t size);
+ bool getUInt32Var(off64_t offset, uint32_t *x, size_t size);
+ bool getUInt64Var(off64_t offset, uint64_t *x, size_t size);
+
// Reads in "count" entries of type T into vector *x.
// Returns true if "count" entries can be read.
// If fewer than "count" entries can be read, return false. In this case,
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 9676b97..65ad3e4 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -38,6 +38,8 @@
kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation
kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width
kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height
+ kKeyThumbnailWidth = 'thbW', // int32_t, thumbnail width
+ kKeyThumbnailHeight = 'thbH', // int32_t, thumbnail height
// a rectangle, if absent assumed to be (0, 0, width - 1, height - 1)
kKeyCropRect = 'crop',
@@ -58,6 +60,7 @@
kKeyAACProfile = 'aacp', // int32_t
kKeyAVCC = 'avcc', // raw data
kKeyHVCC = 'hvcc', // raw data
+ kKeyThumbnailHVCC = 'thvc', // raw data
kKeyD263 = 'd263', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
@@ -209,6 +212,10 @@
// color Matrix, value defined by ColorAspects.MatrixCoeffs.
kKeyTemporalLayerId = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer)
kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded
+
+ kKeyGridRows = 'rows', // int32_t, HEIF grid rows
+ kKeyGridCols = 'clms', // int32_t, HEIF grid columns
+ kKeyIccProfile = 'prof', // raw data
};
enum {
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 3027cdd..bb05740 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_omx",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"FrameDropper.cpp",
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index 8e22f9e..ab893de 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_xmlparser",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"MediaCodecsXmlParser.cpp",
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 970d734..68dcaff 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -31,6 +31,7 @@
#include "SharedMemoryProxy.h"
#include "utility/AAudioUtilities.h"
+using android::base::unique_fd;
using namespace android;
using namespace aaudio;
@@ -69,11 +70,6 @@
AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
}
- if (mAudioDataFileDescriptor != -1) {
- ::close(mAudioDataFileDescriptor);
- mAudioDataFileDescriptor = -1;
- }
-
return AAudioServiceStreamBase::close();
}
@@ -193,7 +189,13 @@
? audio_channel_count_from_out_mask(config.channel_mask)
: audio_channel_count_from_in_mask(config.channel_mask);
- mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
+ // AAudio creates a copy of this FD and retains ownership of the copy.
+ // Assume that AudioFlinger will close the original shared_memory_fd.
+ mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
+ if (mAudioDataFileDescriptor.get() == -1) {
+ ALOGE("AAudioServiceStreamMMAP::open() - could not dup shared_memory_fd");
+ return AAUDIO_ERROR_INTERNAL; // TODO review
+ }
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
mSampleRate = config.sample_rate;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index e6f8fad..e631fd3 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -19,6 +19,7 @@
#include <atomic>
+#include <android-base/unique_fd.h>
#include <media/audiohal/StreamHalInterface.h>
#include <media/MmapStreamCallback.h>
#include <media/MmapStreamInterface.h>
@@ -33,6 +34,7 @@
#include "TimestampScheduler.h"
#include "utility/MonotonicCounter.h"
+
namespace aaudio {
/**
@@ -133,9 +135,9 @@
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
int32_t mPreviousFrameCounter = 0; // from HAL
- int mAudioDataFileDescriptor = -1;
int64_t mHardwareTimeOffsetNanos = 0; // TODO get from HAL
+
// Interface to the AudioFlinger MMAP support.
android::sp<android::MmapStreamInterface> mMmapStream;
struct audio_mmap_buffer_info mMmapBufferinfo;
@@ -143,6 +145,7 @@
audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
android::AudioClient mServiceClient;
bool mInService = false;
+ android::base::unique_fd mAudioDataFileDescriptor;
};
} // namespace aaudio
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index 6b3fb4c..83b25b3 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -35,11 +35,6 @@
munmap(mSharedMemory, mSharedMemorySizeInBytes);
mSharedMemory = nullptr;
}
- if (mFileDescriptor != -1) {
- ALOGV("SharedRingBuffer: LEAK? close(mFileDescriptor = %d)\n", mFileDescriptor);
- close(mFileDescriptor);
- mFileDescriptor = -1;
- }
}
aaudio_result_t SharedRingBuffer::allocate(fifo_frames_t bytesPerFrame,
@@ -49,17 +44,17 @@
// Create shared memory large enough to hold the data and the read and write counters.
mDataMemorySizeInBytes = bytesPerFrame * capacityInFrames;
mSharedMemorySizeInBytes = mDataMemorySizeInBytes + (2 * (sizeof(fifo_counter_t)));
- mFileDescriptor = ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes);
- ALOGV("SharedRingBuffer::allocate() LEAK? mFileDescriptor = %d\n", mFileDescriptor);
- if (mFileDescriptor < 0) {
+ mFileDescriptor.reset(ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes));
+ if (mFileDescriptor.get() == -1) {
ALOGE("SharedRingBuffer::allocate() ashmem_create_region() failed %d", errno);
return AAUDIO_ERROR_INTERNAL;
}
+ ALOGV("SharedRingBuffer::allocate() mFileDescriptor = %d\n", mFileDescriptor.get());
- int err = ashmem_set_prot_region(mFileDescriptor, PROT_READ|PROT_WRITE); // TODO error handling?
+ int err = ashmem_set_prot_region(mFileDescriptor.get(), PROT_READ|PROT_WRITE); // TODO error handling?
if (err < 0) {
ALOGE("SharedRingBuffer::allocate() ashmem_set_prot_region() failed %d", errno);
- close(mFileDescriptor);
+ mFileDescriptor.reset();
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
@@ -67,10 +62,10 @@
mSharedMemory = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
PROT_READ|PROT_WRITE,
MAP_SHARED,
- mFileDescriptor, 0);
+ mFileDescriptor.get(), 0);
if (mSharedMemory == MAP_FAILED) {
ALOGE("SharedRingBuffer::allocate() mmap() failed %d", errno);
- close(mFileDescriptor);
+ mFileDescriptor.reset();
return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
}
diff --git a/services/oboeservice/SharedRingBuffer.h b/services/oboeservice/SharedRingBuffer.h
index a2c3766..79169bc 100644
--- a/services/oboeservice/SharedRingBuffer.h
+++ b/services/oboeservice/SharedRingBuffer.h
@@ -17,6 +17,7 @@
#ifndef AAUDIO_SHARED_RINGBUFFER_H
#define AAUDIO_SHARED_RINGBUFFER_H
+#include <android-base/unique_fd.h>
#include <stdint.h>
#include <cutils/ashmem.h>
#include <sys/mman.h>
@@ -51,12 +52,12 @@
}
private:
- int mFileDescriptor = -1;
- android::FifoBuffer *mFifoBuffer = nullptr;
- uint8_t *mSharedMemory = nullptr;
- int32_t mSharedMemorySizeInBytes = 0;
- int32_t mDataMemorySizeInBytes = 0;
- android::fifo_frames_t mCapacityInFrames = 0;
+ android::base::unique_fd mFileDescriptor;
+ android::FifoBuffer *mFifoBuffer = nullptr;
+ uint8_t *mSharedMemory = nullptr;
+ int32_t mSharedMemorySizeInBytes = 0;
+ int32_t mDataMemorySizeInBytes = 0;
+ android::fifo_frames_t mCapacityInFrames = 0;
};
} /* namespace aaudio */