Merge "mediaplayer: add precise argument to seek function"
diff --git a/Android.bp b/Android.bp
index edd3d04..53f8bec 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
from: "include/camera/ndk/",
to: "camera",
srcs: ["include/camera/ndk/**/*.h"],
+ license: "NOTICE",
}
ndk_headers {
@@ -10,6 +11,7 @@
from: "include/ndk/",
to: "media",
srcs: ["include/ndk/**/*.h"],
+ license: "NOTICE",
}
subdirs = [
diff --git a/camera/Android.mk b/camera/Android.mk
index 1a3382f..c9c98e9 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -58,8 +58,6 @@
libutils \
liblog \
libbinder \
- libhardware \
- libui \
libgui \
libcamera_metadata \
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 755ec8e..8308095 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -44,4 +44,5 @@
* @param lastFrameNumber Frame number of the last frame of the streaming request.
*/
oneway void onRepeatingRequestError(in long lastFrameNumber);
+ oneway void onRequestQueueEmpty();
}
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index c0d75f3..888862a 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -25,7 +25,6 @@
libcutils \
libutils \
libbinder \
- libcamera_client
LOCAL_MODULE:= cameraserver
LOCAL_32_BIT_ONLY := true
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 7d78e2b..229b159 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1347,6 +1347,12 @@
}
binder::Status
+CameraDevice::ServiceCallback::onRequestQueueEmpty() {
+ // onRequestQueueEmpty not yet implemented in NDK
+ return binder::Status::ok();
+}
+
+binder::Status
CameraDevice::ServiceCallback::onRepeatingRequestError(int64_t lastFrameNumber) {
binder::Status ret = binder::Status::ok();
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 051462b..eb8028b 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -74,6 +74,7 @@
binder::Status onResultReceived(const CameraMetadata& metadata,
const CaptureResultExtras& resultExtras) override;
binder::Status onPrepared(int streamId) override;
+ binder::Status onRequestQueueEmpty() override;
binder::Status onRepeatingRequestError(int64_t lastFrameNumber) override;
private:
const wp<CameraDevice> mDevice;
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 828a758..b91e0f3 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -151,6 +151,7 @@
SENT_RESULT,
UNINITIALIZED,
REPEATING_REQUEST_ERROR,
+ REQUEST_QUEUE_EMPTY,
};
protected:
@@ -225,6 +226,14 @@
return binder::Status::ok();
}
+ virtual binder::Status onRequestQueueEmpty() {
+ Mutex::Autolock l(mLock);
+ mLastStatus = REQUEST_QUEUE_EMPTY;
+ mStatusesHit.push_back(mLastStatus);
+ mStatusCondition.broadcast();
+ return binder::Status::ok();
+ }
+
// Test helper functions:
bool hadError() const {
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 1a4bf08..8fe1dd4 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -230,7 +230,6 @@
mCodec->signalResume();
(new AMessage(kWhatSeek, this))->post(5000000ll);
- } else if (what == CodecBase::kWhatOutputFormatChanged) {
} else if (what == CodecBase::kWhatShutdownCompleted) {
mDecodeLooper->unregisterHandler(mCodec->id());
diff --git a/drm/libmediadrm/Android.mk b/drm/libmediadrm/Android.mk
index c822433..270f291 100644
--- a/drm/libmediadrm/Android.mk
+++ b/drm/libmediadrm/Android.mk
@@ -14,13 +14,11 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
- libcrypto \
libcutils \
libdl \
liblog \
libmedia \
libstagefright \
- libstagefright_foundation \
libutils
LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall
diff --git a/drm/libmediadrm/Drm.cpp b/drm/libmediadrm/Drm.cpp
index 9ab08db..07e9414 100644
--- a/drm/libmediadrm/Drm.cpp
+++ b/drm/libmediadrm/Drm.cpp
@@ -334,6 +334,7 @@
return -EINVAL;
}
+ setListener(NULL);
delete mPlugin;
mPlugin = NULL;
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index b2699fa..0533ba6 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -330,8 +330,8 @@
static status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle);
- static status_t stopAudioSource(audio_io_handle_t handle);
+ audio_patch_handle_t *handle);
+ static status_t stopAudioSource(audio_patch_handle_t handle);
static status_t setMasterMono(bool mono);
static status_t getMasterMono(bool *mono);
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index ef15a0c..5637dd5 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -164,8 +164,8 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle) = 0;
- virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
+ audio_patch_handle_t *handle) = 0;
+ virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0;
virtual status_t setMasterMono(bool mono) = 0;
virtual status_t getMasterMono(bool *mono) = 0;
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index b902cf5..839945c 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,7 +19,7 @@
#define ANDROID_IOMX_H_
#include <binder/IInterface.h>
-#include <ui/GraphicBuffer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <utils/List.h>
#include <utils/String8.h>
@@ -39,6 +39,7 @@
class IOMXNode;
class IOMXObserver;
class NativeHandle;
+class OMXBuffer;
struct omx_message;
class IOMX : public IInterface {
@@ -108,23 +109,6 @@
virtual status_t getGraphicBufferUsage(
OMX_U32 port_index, OMX_U32* usage) = 0;
- // Use |params| as an OMX buffer, but limit the size of the OMX buffer to |allottedSize|.
- virtual status_t useBuffer(
- OMX_U32 port_index, const sp<IMemory> ¶ms,
- buffer_id *buffer, OMX_U32 allottedSize) = 0;
-
- virtual status_t useGraphicBuffer(
- OMX_U32 port_index,
- const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
-
- virtual status_t updateGraphicBufferInMeta(
- OMX_U32 port_index,
- const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) = 0;
-
- virtual status_t updateNativeHandleInMeta(
- OMX_U32 port_index,
- const sp<NativeHandle> &nativeHandle, buffer_id buffer) = 0;
-
virtual status_t setInputSurface(
const sp<IOMXBufferSource> &bufferSource) = 0;
@@ -137,34 +121,37 @@
OMX_U32 port_index, size_t size, buffer_id *buffer,
void **buffer_data, sp<NativeHandle> *native_handle) = 0;
+ // Instructs the component to use the buffer passed in via |omxBuf| on the
+ // specified port. Returns in |*buffer| the buffer id that the component
+ // assigns to this buffer. |omxBuf| must be one of:
+ // 1) OMXBuffer::sPreset for meta-mode,
+ // 2) type kBufferTypeANWBuffer for non-meta-graphic buffer mode,
+ // 3) type kBufferTypeSharedMem for bytebuffer mode.
+ virtual status_t useBuffer(
+ OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) = 0;
+
+ // Frees the buffer on the specified port with buffer id |buffer|.
virtual status_t freeBuffer(
OMX_U32 port_index, buffer_id buffer) = 0;
- // Calls OMX_FillBuffer on buffer, and passes |fenceFd| to component if it supports
- // fences. Otherwise, it waits on |fenceFd| before calling OMX_FillBuffer.
- // Takes ownership of |fenceFd| even if this call fails.
- virtual status_t fillBuffer(buffer_id buffer, int fenceFd = -1) = 0;
+ // Calls OMX_FillBuffer on buffer. Passes |fenceFd| to component if it
+ // supports fences. Otherwise, it waits on |fenceFd| before calling
+ // OMX_FillBuffer. Takes ownership of |fenceFd| even if this call fails.
+ // If the port is in metadata mode, the buffer will be updated to point
+ // to the new buffer passed in via |omxBuf| before OMX_FillBuffer is called.
+ // Otherwise info in the |omxBuf| is not used.
+ virtual status_t fillBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd = -1) = 0;
- // Calls OMX_EmptyBuffer on buffer (after updating buffer header with |range_offset|,
- // |range_length|, |flags| and |timestamp|). Passes |fenceFd| to component if it
- // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer.
- // Takes ownership of |fenceFd| even if this call fails.
+ // Calls OMX_EmptyBuffer on buffer. Passes |fenceFd| to component if it
+ // supports fences. Otherwise, it waits on |fenceFd| before calling
+ // OMX_EmptyBuffer. Takes ownership of |fenceFd| even if this call fails.
+ // If the port is in metadata mode, the buffer will be updated to point
+ // to the new buffer passed in via |omxBuf| before OMX_EmptyBuffer is called.
virtual status_t emptyBuffer(
- buffer_id buffer,
- OMX_U32 range_offset, OMX_U32 range_length,
+ buffer_id buffer, const OMXBuffer &omxBuf,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0;
- // Calls OMX_EmptyBuffer on buffer (after updating buffer header with metadata of
- // |graphicBuffer|, |flags| and |timestamp|). Passes |fenceFd| to component if it
- // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer.
- // Takes ownership of |fenceFd| even if this call fails. If |origTimestamp| >= 0,
- // timestamp on the filled buffer corresponding to this frame will be modified to
- // |origTimestamp| after it's filled.
- virtual status_t emptyGraphicBuffer(
- buffer_id buffer,
- const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
- OMX_TICKS timestamp, int fenceFd) = 0;
-
virtual status_t getExtensionIndex(
const char *parameter_name,
OMX_INDEXTYPE *index) = 0;
diff --git a/include/media/MediaCodecBuffer.h b/include/media/MediaCodecBuffer.h
index 2df81dd..05aaa14 100644
--- a/include/media/MediaCodecBuffer.h
+++ b/include/media/MediaCodecBuffer.h
@@ -58,6 +58,8 @@
sp<AMessage> meta();
sp<AMessage> format();
+ virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format);
+
private:
MediaCodecBuffer() = delete;
diff --git a/include/media/OMXBuffer.h b/include/media/OMXBuffer.h
new file mode 100644
index 0000000..0322b73
--- /dev/null
+++ b/include/media/OMXBuffer.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016, 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 _OMXBUFFER_H_
+#define _OMXBUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <media/IOMX.h>
+#include <system/window.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+class IMemory;
+class MediaCodecBuffer;
+class NativeHandle;
+class OMXNodeInstance;
+
+class OMXBuffer {
+public:
+ // sPreset is used in places where we are referring to a pre-registered
+ // buffer on a port. It has type kBufferTypePreset and mRangeLength of 0.
+ static OMXBuffer sPreset;
+
+ // Default constructor, constructs a buffer of type kBufferTypeInvalid.
+ OMXBuffer();
+
+ // Constructs a buffer of type kBufferTypePreset with mRangeLength set to
+ // |codecBuffer|'s size (or 0 if |codecBuffer| is NULL).
+ OMXBuffer(const sp<MediaCodecBuffer> &codecBuffer);
+
+ // Constructs a buffer of type kBufferTypeSharedMem.
+ OMXBuffer(const sp<IMemory> &mem, size_t allottedSize = 0);
+
+ // Constructs a buffer of type kBufferTypeANWBuffer.
+ OMXBuffer(const sp<GraphicBuffer> &gbuf);
+
+ // Constructs a buffer of type kBufferTypeNativeHandle.
+ OMXBuffer(const sp<NativeHandle> &handle);
+
+ // Parcelling/Un-parcelling.
+ status_t writeToParcel(Parcel *parcel) const;
+ status_t readFromParcel(const Parcel *parcel);
+
+ ~OMXBuffer();
+
+private:
+ friend class OMXNodeInstance;
+
+ enum BufferType {
+ kBufferTypeInvalid = 0,
+ kBufferTypePreset,
+ kBufferTypeSharedMem,
+ kBufferTypeANWBuffer,
+ kBufferTypeNativeHandle,
+ };
+
+ BufferType mBufferType;
+
+ // kBufferTypePreset
+ // If the port is operating in byte buffer mode, mRangeLength is the valid
+ // range length. Otherwise the range info should also be ignored.
+ OMX_U32 mRangeLength;
+
+ // kBufferTypeSharedMem
+ sp<IMemory> mMem;
+ OMX_U32 mAllottedSize;
+
+ // kBufferTypeANWBuffer
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // kBufferTypeNativeHandle
+ sp<NativeHandle> mNativeHandle;
+
+ OMXBuffer(const OMXBuffer &);
+ OMXBuffer &operator=(const OMXBuffer &);
+};
+
+} // namespace android
+
+#endif // _OMXBUFFER_H_
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index cdfe2c9..13ceeb6 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -81,7 +81,7 @@
friend struct ACodec;
Vector<IOMX::buffer_id> mBufferIDs;
- Vector<sp<MediaCodecBuffer>> mBuffers;
+ Vector<sp<MediaCodecBuffer> > mBuffers;
PortDescription();
void addBuffer(IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer);
@@ -188,7 +188,6 @@
sp<RefBase> mCodecRef; // and a reference to the IMemory
sp<GraphicBuffer> mGraphicBuffer;
- sp<NativeHandle> mNativeHandle;
int mFenceFd;
FrameRenderTracker::Info *mRenderInfo;
@@ -200,8 +199,6 @@
// Log error, if the current fence is not a read/write fence.
void checkReadFence(const char *dbg);
void checkWriteFence(const char *dbg);
-
- sp<MediaCodecBuffer> alloc(const sp<AMessage> &format);
};
static const char *_asString(BufferInfo::Status s);
@@ -333,6 +330,8 @@
uint32_t portIndex, IOMX::buffer_id bufferID,
ssize_t *index = NULL);
+ status_t fillBuffer(BufferInfo *info);
+
status_t setComponentRole(bool isEncoder, const char *mime);
status_t configureCodec(const char *mime, const sp<AMessage> &msg);
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 8fc410d..f20c2cd 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -87,9 +87,10 @@
int64_t mStartTimeUs;
int16_t mMaxAmplitude;
int64_t mPrevSampleTimeUs;
- int64_t mFirstSampleTimeUs;
int64_t mInitialReadTimeUs;
int64_t mNumFramesReceived;
+ int64_t mNumFramesSkipped;
+ int64_t mNumFramesLost;
int64_t mNumClientOwnedBuffers;
List<MediaBuffer * > mBuffersReceived;
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 1295b59..d8c43a4 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -45,7 +45,6 @@
kWhatEOS = 'eos ',
kWhatShutdownCompleted = 'scom',
kWhatFlushCompleted = 'fcom',
- kWhatOutputFormatChanged = 'outC',
kWhatError = 'erro',
kWhatComponentAllocated = 'cAll',
kWhatComponentConfigured = 'cCon',
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 19d7047..89def5d 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -64,14 +64,15 @@
};
static const pid_t kNoPid = -1;
+ static const uid_t kNoUid = -1;
static sp<MediaCodec> CreateByType(
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err = NULL,
- pid_t pid = kNoPid);
+ pid_t pid = kNoPid, uid_t uid = kNoUid);
static sp<MediaCodec> CreateByComponentName(
const sp<ALooper> &looper, const AString &name, status_t *err = NULL,
- pid_t pid = kNoPid);
+ pid_t pid = kNoPid, uid_t uid = kNoUid);
static sp<PersistentSurface> CreatePersistentInputSurface();
@@ -246,7 +247,7 @@
kFlagIsSecure = 64,
kFlagSawMediaServerDie = 128,
kFlagIsEncoder = 256,
- kFlagGatherCodecSpecificData = 512,
+ // 512 skipped
kFlagIsAsync = 1024,
kFlagIsComponentAllocated = 2048,
kFlagPushBlankBuffersOnShutdown = 4096,
@@ -258,7 +259,6 @@
sp<MediaCodecBuffer> mSecureData;
sp<IMemory> mSharedEncryptedBuffer;
sp<AMessage> mNotify;
- sp<AMessage> mFormat;
bool mOwnedByClient;
};
@@ -287,6 +287,7 @@
};
State mState;
+ uid_t mUid;
bool mReleasedByResourceManager;
sp<ALooper> mLooper;
sp<ALooper> mCodecLooper;
@@ -329,6 +330,7 @@
List<size_t> mAvailPortBuffers[2];
Vector<BufferInfo> mPortBuffers[2];
+ Vector<sp<MediaCodecBuffer>> mPortBufferArrays[2];
int32_t mDequeueInputTimeoutGeneration;
sp<AReplyToken> mDequeueInputReplyID;
@@ -345,7 +347,7 @@
bool mHaveInputSurface;
bool mHavePendingInputBuffers;
- MediaCodec(const sp<ALooper> &looper, pid_t pid);
+ MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 6aa87e8..0e39431 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -144,7 +144,6 @@
void postFillThisBuffer(BufferInfo *info);
void postDrainThisBuffer(BufferInfo *info);
void postEOS();
- void sendFormatChange();
void requestFillEmptyInput();
void processBuffers();
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index 5ce1798..91cc902 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -13,7 +13,6 @@
liblog \
libmedia \
libmedialogservice \
- libnbaio \
libradioservice \
libsoundtriggerservice \
libutils
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 80f78b6..4b0f6a2 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,3 +5,4 @@
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
ioprio rt 4
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ onrestart restart audio-hal-2-0
\ No newline at end of file
diff --git a/media/img_utils/src/Android.mk b/media/img_utils/src/Android.mk
index c1f64ca..4c6fe70 100644
--- a/media/img_utils/src/Android.mk
+++ b/media/img_utils/src/Android.mk
@@ -35,11 +35,8 @@
LOCAL_SHARED_LIBRARIES := \
liblog \
- libexpat \
libutils \
libcutils \
- libcamera_metadata \
- libcamera_client
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../include \
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 8aed146..2dfdfde 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2349,8 +2349,12 @@
case EQ_PARAM_BAND_LEVEL:
param2 = *pParamTemp;
- if (param2 >= FIVEBAND_NUMBANDS) {
+ if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
status = -EINVAL;
+ if (param2 < 0) {
+ android_errorWriteLog(0x534e4554, "32438598");
+ ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d", param2);
+ }
break;
}
*(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, param2);
@@ -2360,8 +2364,12 @@
case EQ_PARAM_CENTER_FREQ:
param2 = *pParamTemp;
- if (param2 >= FIVEBAND_NUMBANDS) {
+ if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
status = -EINVAL;
+ if (param2 < 0) {
+ android_errorWriteLog(0x534e4554, "32436341");
+ ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d", param2);
+ }
break;
}
*(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, param2);
@@ -2371,8 +2379,12 @@
case EQ_PARAM_BAND_FREQ_RANGE:
param2 = *pParamTemp;
- if (param2 >= FIVEBAND_NUMBANDS) {
+ if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
status = -EINVAL;
+ if (param2 < 0) {
+ android_errorWriteLog(0x534e4554, "32247948");
+ ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+ }
break;
}
EqualizerGetBandFreqRange(pContext, param2, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index ca96098..ba31cfa 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -65,6 +65,7 @@
MediaProfiles.cpp \
MediaResource.cpp \
MediaResourcePolicy.cpp \
+ OMXBuffer.cpp \
IEffect.cpp \
IEffectClient.cpp \
AudioEffect.cpp \
@@ -76,7 +77,7 @@
LOCAL_SHARED_LIBRARIES := \
libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \
libcamera_client libstagefright_foundation \
- libgui libdl libaudioutils libnbaio
+ libgui libdl libaudioutils
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 33974e0..d45b12f 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -1175,14 +1175,14 @@
status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle)
+ audio_patch_handle_t *handle)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->startAudioSource(source, attributes, handle);
}
-status_t AudioSystem::stopAudioSource(audio_io_handle_t handle)
+status_t AudioSystem::stopAudioSource(audio_patch_handle_t handle)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 946da8a..2fb2da6 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -738,7 +738,7 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle)
+ audio_patch_handle_t *handle)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -755,11 +755,11 @@
if (status != NO_ERROR) {
return status;
}
- *handle = (audio_io_handle_t)reply.readInt32();
+ *handle = (audio_patch_handle_t)reply.readInt32();
return status;
}
- virtual status_t stopAudioSource(audio_io_handle_t handle)
+ virtual status_t stopAudioSource(audio_patch_handle_t handle)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -1332,7 +1332,7 @@
data.read(&source, sizeof(struct audio_port_config));
audio_attributes_t attributes;
data.read(&attributes, sizeof(audio_attributes_t));
- audio_io_handle_t handle = {};
+ audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
status_t status = startAudioSource(&source, &attributes, &handle);
reply->writeInt32(status);
reply->writeInt32(handle);
@@ -1341,7 +1341,7 @@
case STOP_AUDIO_SOURCE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t handle = (audio_io_handle_t)data.readInt32();
+ audio_patch_handle_t handle = (audio_patch_handle_t) data.readInt32();
status_t status = stopAudioSource(handle);
reply->writeInt32(status);
return NO_ERROR;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 4bd1ab8..1a6d6b8 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -26,8 +26,8 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/openmax/OMX_IndexExt.h>
#include <utils/NativeHandle.h>
+#include <media/OMXBuffer.h>
-#include <gui/IGraphicBufferProducer.h>
#include <android/IGraphicBufferSource.h>
#include <android/IOMXBufferSource.h>
@@ -45,7 +45,6 @@
SET_CONFIG,
ENABLE_NATIVE_BUFFERS,
USE_BUFFER,
- USE_GRAPHIC_BUFFER,
CREATE_INPUT_SURFACE,
SET_INPUT_SURFACE,
STORE_META_DATA_IN_BUFFERS,
@@ -54,13 +53,10 @@
FREE_BUFFER,
FILL_BUFFER,
EMPTY_BUFFER,
- EMPTY_GRAPHIC_BUFFER,
GET_EXTENSION_INDEX,
OBSERVER_ON_MSG,
GET_GRAPHIC_BUFFER_USAGE,
- UPDATE_GRAPHIC_BUFFER_IN_META,
CONFIGURE_VIDEO_TUNNEL_MODE,
- UPDATE_NATIVE_HANDLE_IN_META,
DISPATCH_MESSAGE,
SET_QUIRKS,
};
@@ -255,16 +251,19 @@
}
virtual status_t useBuffer(
- OMX_U32 port_index, const sp<IMemory> ¶ms,
- buffer_id *buffer, OMX_U32 allottedSize) {
+ OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) {
Parcel data, reply;
data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
data.writeInt32(port_index);
- data.writeStrongBinder(IInterface::asBinder(params));
- data.writeInt32(allottedSize);
+
+ status_t err = omxBuf.writeToParcel(&data);
+ if (err != OK) {
+ return err;
+ }
+
remote()->transact(USE_BUFFER, data, &reply);
- status_t err = reply.readInt32();
+ err = reply.readInt32();
if (err != OK) {
*buffer = 0;
@@ -276,59 +275,6 @@
return err;
}
-
- virtual status_t useGraphicBuffer(
- OMX_U32 port_index,
- const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
- Parcel data, reply;
- data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
- data.writeInt32(port_index);
- data.write(*graphicBuffer);
- remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
-
- status_t err = reply.readInt32();
- if (err != OK) {
- *buffer = 0;
-
- return err;
- }
-
- *buffer = (buffer_id)reply.readInt32();
-
- return err;
- }
-
- virtual status_t updateGraphicBufferInMeta(
- OMX_U32 port_index,
- const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) {
- Parcel data, reply;
- data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
- data.writeInt32(port_index);
- data.write(*graphicBuffer);
- data.writeInt32((int32_t)buffer);
- remote()->transact(UPDATE_GRAPHIC_BUFFER_IN_META, data, &reply);
-
- status_t err = reply.readInt32();
- return err;
- }
-
- virtual status_t updateNativeHandleInMeta(
- OMX_U32 port_index,
- const sp<NativeHandle> &nativeHandle, buffer_id buffer) {
- Parcel data, reply;
- data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
- data.writeInt32(port_index);
- data.writeInt32(nativeHandle != NULL);
- if (nativeHandle != NULL) {
- data.writeNativeHandle(nativeHandle->handle());
- }
- data.writeInt32((int32_t)buffer);
- remote()->transact(UPDATE_NATIVE_HANDLE_IN_META, data, &reply);
-
- status_t err = reply.readInt32();
- return err;
- }
-
virtual status_t setInputSurface(
const sp<IOMXBufferSource> &bufferSource) {
Parcel data, reply;
@@ -439,10 +385,15 @@
return reply.readInt32();
}
- virtual status_t fillBuffer(buffer_id buffer, int fenceFd) {
+ virtual status_t fillBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) {
Parcel data, reply;
data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
data.writeInt32((int32_t)buffer);
+ status_t err = omxBuf.writeToParcel(&data);
+ if (err != OK) {
+ return err;
+ }
data.writeInt32(fenceFd >= 0);
if (fenceFd >= 0) {
data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
@@ -453,14 +404,15 @@
}
virtual status_t emptyBuffer(
- buffer_id buffer,
- OMX_U32 range_offset, OMX_U32 range_length,
+ buffer_id buffer, const OMXBuffer &omxBuf,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Parcel data, reply;
data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
data.writeInt32((int32_t)buffer);
- data.writeInt32(range_offset);
- data.writeInt32(range_length);
+ status_t err = omxBuf.writeToParcel(&data);
+ if (err != OK) {
+ return err;
+ }
data.writeInt32(flags);
data.writeInt64(timestamp);
data.writeInt32(fenceFd >= 0);
@@ -472,25 +424,6 @@
return reply.readInt32();
}
- virtual status_t emptyGraphicBuffer(
- buffer_id buffer,
- const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
- OMX_TICKS timestamp, int fenceFd) {
- Parcel data, reply;
- data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
- data.writeInt32((int32_t)buffer);
- data.write(*graphicBuffer);
- data.writeInt32(flags);
- data.writeInt64(timestamp);
- data.writeInt32(fenceFd >= 0);
- if (fenceFd >= 0) {
- data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
- }
- remote()->transact(EMPTY_GRAPHIC_BUFFER, data, &reply);
-
- return reply.readInt32();
- }
-
virtual status_t getExtensionIndex(
const char *parameter_name,
OMX_INDEXTYPE *index) {
@@ -770,18 +703,15 @@
CHECK_OMX_INTERFACE(IOMXNode, data, reply);
OMX_U32 port_index = data.readInt32();
- sp<IMemory> params =
- interface_cast<IMemory>(data.readStrongBinder());
- OMX_U32 allottedSize = data.readInt32();
- if (params == NULL) {
- ALOGE("b/26392700");
- reply->writeInt32(INVALID_OPERATION);
- return NO_ERROR;
+ OMXBuffer omxBuf;
+ status_t err = omxBuf.readFromParcel(&data);
+ if (err != OK) {
+ return err;
}
buffer_id buffer;
- status_t err = useBuffer(port_index, params, &buffer, allottedSize);
+ err = useBuffer(port_index, omxBuf, &buffer);
reply->writeInt32(err);
if (err == OK) {
@@ -791,60 +721,6 @@
return NO_ERROR;
}
- case USE_GRAPHIC_BUFFER:
- {
- CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
- OMX_U32 port_index = data.readInt32();
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
- data.read(*graphicBuffer);
-
- buffer_id buffer;
- status_t err = useGraphicBuffer(
- port_index, graphicBuffer, &buffer);
- reply->writeInt32(err);
-
- if (err == OK) {
- reply->writeInt32((int32_t)buffer);
- }
-
- return NO_ERROR;
- }
-
- case UPDATE_GRAPHIC_BUFFER_IN_META:
- {
- CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
- OMX_U32 port_index = data.readInt32();
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
- data.read(*graphicBuffer);
- buffer_id buffer = (buffer_id)data.readInt32();
-
- status_t err = updateGraphicBufferInMeta(
- port_index, graphicBuffer, buffer);
- reply->writeInt32(err);
-
- return NO_ERROR;
- }
-
- case UPDATE_NATIVE_HANDLE_IN_META:
- {
- CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
- OMX_U32 port_index = data.readInt32();
- native_handle *handle = NULL;
- if (data.readInt32()) {
- handle = data.readNativeHandle();
- }
- buffer_id buffer = (buffer_id)data.readInt32();
-
- status_t err = updateNativeHandleInMeta(
- port_index, NativeHandle::create(handle, true /* ownshandle */), buffer);
- reply->writeInt32(err);
-
- return NO_ERROR;
- }
-
case SET_INPUT_SURFACE:
{
CHECK_OMX_INTERFACE(IOMXNode, data, reply);
@@ -956,9 +832,17 @@
CHECK_OMX_INTERFACE(IOMXNode, data, reply);
buffer_id buffer = (buffer_id)data.readInt32();
+
+ OMXBuffer omxBuf;
+ status_t err = omxBuf.readFromParcel(&data);
+ if (err != OK) {
+ return err;
+ }
+
bool haveFence = data.readInt32();
int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
- reply->writeInt32(fillBuffer(buffer, fenceFd));
+
+ reply->writeInt32(fillBuffer(buffer, omxBuf, fenceFd));
return NO_ERROR;
}
@@ -968,31 +852,17 @@
CHECK_OMX_INTERFACE(IOMXNode, data, reply);
buffer_id buffer = (buffer_id)data.readInt32();
- OMX_U32 range_offset = data.readInt32();
- OMX_U32 range_length = data.readInt32();
+ OMXBuffer omxBuf;
+ status_t err = omxBuf.readFromParcel(&data);
+ if (err != OK) {
+ return err;
+ }
OMX_U32 flags = data.readInt32();
OMX_TICKS timestamp = data.readInt64();
bool haveFence = data.readInt32();
int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
reply->writeInt32(emptyBuffer(
- buffer, range_offset, range_length, flags, timestamp, fenceFd));
-
- return NO_ERROR;
- }
-
- case EMPTY_GRAPHIC_BUFFER:
- {
- CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
- buffer_id buffer = (buffer_id)data.readInt32();
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
- data.read(*graphicBuffer);
- OMX_U32 flags = data.readInt32();
- OMX_TICKS timestamp = data.readInt64();
- bool haveFence = data.readInt32();
- int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
- reply->writeInt32(emptyGraphicBuffer(
- buffer, graphicBuffer, flags, timestamp, fenceFd));
+ buffer, omxBuf, flags, timestamp, fenceFd));
return NO_ERROR;
}
diff --git a/media/libmedia/MediaCodecBuffer.cpp b/media/libmedia/MediaCodecBuffer.cpp
index 2d255be..2af31d0 100644
--- a/media/libmedia/MediaCodecBuffer.cpp
+++ b/media/libmedia/MediaCodecBuffer.cpp
@@ -80,4 +80,8 @@
return mFormat;
}
+sp<MediaCodecBuffer> MediaCodecBuffer::clone(const sp<AMessage> &format) {
+ return new MediaCodecBuffer(format, mBuffer);
+}
+
} // namespace android
diff --git a/media/libmedia/OMXBuffer.cpp b/media/libmedia/OMXBuffer.cpp
new file mode 100644
index 0000000..0931872
--- /dev/null
+++ b/media/libmedia/OMXBuffer.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXBuffer"
+
+#include <media/MediaCodecBuffer.h>
+#include <media/OMXBuffer.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/NativeHandle.h>
+
+namespace android {
+
+//static
+OMXBuffer OMXBuffer::sPreset(static_cast<sp<MediaCodecBuffer> >(NULL));
+
+OMXBuffer::OMXBuffer()
+ : mBufferType(kBufferTypeInvalid) {
+}
+
+OMXBuffer::OMXBuffer(const sp<MediaCodecBuffer>& codecBuffer)
+ : mBufferType(kBufferTypePreset),
+ mRangeLength(codecBuffer != NULL ? codecBuffer->size() : 0) {
+}
+
+OMXBuffer::OMXBuffer(const sp<IMemory> &mem, size_t allottedSize)
+ : mBufferType(kBufferTypeSharedMem),
+ mMem(mem),
+ mAllottedSize(allottedSize ? : mem->size()) {
+}
+
+OMXBuffer::OMXBuffer(const sp<GraphicBuffer> &gbuf)
+ : mBufferType(kBufferTypeANWBuffer),
+ mGraphicBuffer(gbuf) {
+}
+
+OMXBuffer::OMXBuffer(const sp<NativeHandle> &handle)
+ : mBufferType(kBufferTypeNativeHandle),
+ mNativeHandle(handle) {
+}
+
+OMXBuffer::~OMXBuffer() {
+}
+
+status_t OMXBuffer::writeToParcel(Parcel *parcel) const {
+ parcel->writeInt32(mBufferType);
+
+ switch(mBufferType) {
+ case kBufferTypePreset:
+ {
+ return parcel->writeUint32(mRangeLength);
+ }
+
+ case kBufferTypeSharedMem:
+ {
+ status_t err = parcel->writeStrongBinder(IInterface::asBinder(mMem));
+ if (err != NO_ERROR) {
+ return err;
+ }
+ return parcel->writeUint32(mAllottedSize);
+ }
+
+ case kBufferTypeANWBuffer:
+ {
+ return parcel->write(*mGraphicBuffer);
+ }
+
+ case kBufferTypeNativeHandle:
+ {
+ return parcel->writeNativeHandle(mNativeHandle->handle());
+ }
+
+ default:
+ return BAD_VALUE;
+ }
+ return BAD_VALUE;
+}
+
+status_t OMXBuffer::readFromParcel(const Parcel *parcel) {
+ BufferType bufferType = (BufferType) parcel->readInt32();
+
+ switch(bufferType) {
+ case kBufferTypePreset:
+ {
+ mRangeLength = parcel->readUint32();
+ break;
+ }
+
+ case kBufferTypeSharedMem:
+ {
+ sp<IMemory> params = interface_cast<IMemory>(parcel->readStrongBinder());
+
+ mMem = params;
+ mAllottedSize = parcel->readUint32();
+ break;
+ }
+
+ case kBufferTypeANWBuffer:
+ {
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+ status_t err = parcel->read(*buffer);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mGraphicBuffer = buffer;
+ break;
+ }
+
+ case kBufferTypeNativeHandle:
+ {
+ sp<NativeHandle> handle = NativeHandle::create(
+ parcel->readNativeHandle(), true /* ownsHandle */);
+
+ mNativeHandle = handle;
+ break;
+ }
+
+ default:
+ return BAD_VALUE;
+ }
+
+ mBufferType = bufferType;
+ return OK;
+}
+
+} // namespace android
+
+
+
+
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 5270dea..97e7404 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -19,7 +19,6 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
- libcamera_client \
libcrypto \
libcutils \
libdrmframework \
@@ -29,14 +28,12 @@
libmedia \
libmediautils \
libmemunreachable \
- libsonivox \
libstagefright \
libstagefright_foundation \
libstagefright_httplive \
libstagefright_omx \
libstagefright_wfd \
libutils \
- libvorbisidec \
LOCAL_STATIC_LIBRARIES := \
libstagefright_nuplayer \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 4554472..c0a5682 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -665,8 +665,8 @@
} else {
timeUs = mVideoLastDequeueTimeUs;
}
- readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
- readBuffer(counterpartType, -1, NULL, !formatChange);
+ readBuffer(trackType, timeUs, false /* precise */, &actualTimeUs, formatChange);
+ readBuffer(counterpartType, -1, false /* precise */, NULL, !formatChange);
ALOGV("timeUs %lld actualTimeUs %lld", (long long)timeUs, (long long)actualTimeUs);
break;
@@ -759,7 +759,7 @@
CHECK(msg->findInt64("timeUs", &timeUs));
int64_t subTimeUs;
- readBuffer(type, timeUs, &subTimeUs);
+ readBuffer(type, timeUs, false /* precise */, &subTimeUs);
int64_t delayUs = subTimeUs - timeUs;
if (msg->what() == kWhatFetchSubtitleData) {
@@ -790,7 +790,7 @@
}
int64_t nextSubTimeUs;
- readBuffer(type, -1, &nextSubTimeUs);
+ readBuffer(type, -1, false /* precise */, &nextSubTimeUs);
sp<ABuffer> buffer;
status_t dequeueStatus = packets->dequeueAccessUnit(&buffer);
@@ -1186,9 +1186,10 @@
return INVALID_OPERATION;
}
-status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
+status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs, bool precise) {
sp<AMessage> msg = new AMessage(kWhatSeek, this);
msg->setInt64("seekTimeUs", seekTimeUs);
+ msg->setInt32("precise", precise);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
@@ -1201,10 +1202,12 @@
void NuPlayer::GenericSource::onSeek(const sp<AMessage>& msg) {
int64_t seekTimeUs;
+ int32_t precise;
CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
+ CHECK(msg->findInt32("precise", &precise));
sp<AMessage> response = new AMessage;
- status_t err = doSeek(seekTimeUs);
+ status_t err = doSeek(seekTimeUs, precise);
response->setInt32("err", err);
sp<AReplyToken> replyID;
@@ -1212,7 +1215,7 @@
response->postReply(replyID);
}
-status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
+status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs, bool precise) {
mBufferingMonitor->updateDequeuedBufferTime(-1ll);
// If the Widevine source is stopped, do not attempt to read any
@@ -1222,10 +1225,12 @@
}
if (mVideoTrack.mSource != NULL) {
int64_t actualTimeUs;
- readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);
+ readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, precise, &actualTimeUs);
- seekTimeUs = actualTimeUs;
- mVideoLastDequeueTimeUs = seekTimeUs;
+ if (!precise) {
+ seekTimeUs = actualTimeUs;
+ }
+ mVideoLastDequeueTimeUs = actualTimeUs;
}
if (mAudioTrack.mSource != NULL) {
@@ -1250,7 +1255,8 @@
sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer(
MediaBuffer* mb,
media_track_type trackType,
- int64_t /* seekTimeUs */,
+ int64_t seekTimeUs,
+ bool precise,
int64_t *actualTimeUs) {
bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO;
size_t outLength = mb->range_length();
@@ -1288,15 +1294,11 @@
CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs));
meta->setInt64("timeUs", timeUs);
-#if 0
- // Temporarily disable pre-roll till we have a full solution to handle
- // both single seek and continous seek gracefully.
- if (seekTimeUs > timeUs) {
+ if (precise && seekTimeUs > timeUs) {
sp<AMessage> extra = new AMessage;
extra->setInt64("resume-at-mediaTimeUs", seekTimeUs);
meta->setMessage("extra", extra);
}
-#endif
if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
int32_t layerId;
@@ -1372,7 +1374,8 @@
}
void NuPlayer::GenericSource::readBuffer(
- media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
+ media_track_type trackType, int64_t seekTimeUs, bool precise,
+ int64_t *actualTimeUs, bool formatChange) {
// Do not read data if Widevine source is stopped
if (mStopRead) {
return;
@@ -1466,7 +1469,7 @@
queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
sp<ABuffer> buffer = mediaBufferToABuffer(
- mbuf, trackType, seekTimeUs,
+ mbuf, trackType, seekTimeUs, precise,
numBuffers == 0 ? actualTimeUs : NULL);
track->mPackets->queueAccessUnit(buffer);
formatChange = false;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 0957778..f9db6a5 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -71,7 +71,7 @@
virtual sp<AMessage> getTrackInfo(size_t trackIndex) const;
virtual ssize_t getSelectedTrack(media_track_type type) const;
virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
- virtual status_t seekTo(int64_t seekTimeUs);
+ virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers);
@@ -258,7 +258,7 @@
status_t doSelectTrack(size_t trackIndex, bool select, int64_t timeUs);
void onSeek(const sp<AMessage>& msg);
- status_t doSeek(int64_t seekTimeUs);
+ status_t doSeek(int64_t seekTimeUs, bool precise);
void onPrepareAsync();
@@ -278,13 +278,15 @@
MediaBuffer *mbuf,
media_track_type trackType,
int64_t seekTimeUs,
+ bool precise,
int64_t *actualTimeUs = NULL);
void postReadBuffer(media_track_type trackType);
void onReadBuffer(const sp<AMessage>& msg);
void readBuffer(
media_track_type trackType,
- int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
+ int64_t seekTimeUs = -1ll, bool precise = false,
+ int64_t *actualTimeUs = NULL, bool formatChange = false);
void queueDiscontinuityIfNeeded(
bool seeking, bool formatChange, media_track_type trackType, Track *track);
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index ebba93c..1a6a233 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -214,8 +214,8 @@
return (err == OK || err == BAD_VALUE) ? (status_t)OK : err;
}
-status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) {
- return mLiveSession->seekTo(seekTimeUs);
+status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs, bool precise) {
+ return mLiveSession->seekTo(seekTimeUs, precise);
}
void NuPlayer::HTTPLiveSource::pollForRawData(
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index 574937d..be3b4f0 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -47,7 +47,7 @@
virtual sp<AMessage> getTrackInfo(size_t trackIndex) const;
virtual ssize_t getSelectedTrack(media_track_type /* type */) const;
virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
- virtual status_t seekTo(int64_t seekTimeUs);
+ virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
protected:
virtual ~HTTPLiveSource();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 651cb67..cbc32b1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1644,7 +1644,7 @@
} else {
mSource->setOffloadAudio(false /* offload */);
- *decoder = new Decoder(notify, mSource, mPID, mRenderer);
+ *decoder = new Decoder(notify, mSource, mPID, mUID, mRenderer);
}
} else {
sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
@@ -1652,7 +1652,7 @@
notify->setInt32("generation", mVideoDecoderGeneration);
*decoder = new Decoder(
- notify, mSource, mPID, mRenderer, mSurface, mCCDecoder);
+ notify, mSource, mPID, mUID, mRenderer, mSurface, mCCDecoder);
// enable FRC if high-quality AV sync is requested, even if not
// directly queuing to display, as this will even improve textureview
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 822f154..6974207 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -58,6 +58,7 @@
const sp<AMessage> ¬ify,
const sp<Source> &source,
pid_t pid,
+ uid_t uid,
const sp<Renderer> &renderer,
const sp<Surface> &surface,
const sp<CCDecoder> &ccDecoder)
@@ -67,6 +68,7 @@
mRenderer(renderer),
mCCDecoder(ccDecoder),
mPid(pid),
+ mUid(uid),
mSkipRenderingUntilMediaTimeUs(-1ll),
mNumFramesTotal(0ll),
mNumInputFramesDropped(0ll),
@@ -266,7 +268,7 @@
ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
mCodec = MediaCodec::CreateByType(
- mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid);
+ mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
int32_t secure = 0;
if (format->findInt32("secure", &secure) && secure != 0) {
if (mCodec != NULL) {
@@ -275,7 +277,7 @@
mCodec->release();
ALOGI("[%s] creating", mComponentName.c_str());
mCodec = MediaCodec::CreateByComponentName(
- mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid);
+ mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid, mUid);
}
}
if (mCodec == NULL) {
@@ -755,7 +757,7 @@
status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
sp<ABuffer> accessUnit;
- bool dropAccessUnit;
+ bool dropAccessUnit = true;
do {
status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 8186862..a576ae5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -29,6 +29,7 @@
Decoder(const sp<AMessage> ¬ify,
const sp<Source> &source,
pid_t pid,
+ uid_t uid,
const sp<Renderer> &renderer = NULL,
const sp<Surface> &surface = NULL,
const sp<CCDecoder> &ccDecoder = NULL);
@@ -85,6 +86,7 @@
Vector<size_t> mDequeuedInputBuffers;
const pid_t mPid;
+ const uid_t mUid;
int64_t mSkipRenderingUntilMediaTimeUs;
int64_t mNumFramesTotal;
int64_t mNumInputFramesDropped;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 3a96138..b62fbfd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -104,7 +104,7 @@
return INVALID_OPERATION;
}
- virtual status_t seekTo(int64_t /* seekTimeUs */) {
+ virtual status_t seekTo(int64_t /* seekTimeUs */, bool /* precise */ = false) {
return INVALID_OPERATION;
}
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 7ce909d..f430f03 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -279,10 +279,11 @@
return OK;
}
-status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
+status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs, bool precise) {
sp<AMessage> msg = new AMessage(kWhatPerformSeek, this);
msg->setInt32("generation", ++mSeekGeneration);
msg->setInt64("timeUs", seekTimeUs);
+ msg->setInt32("precise", precise);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
@@ -465,8 +466,11 @@
}
int64_t seekTimeUs;
+ int32_t precise;
CHECK(msg->findInt64("timeUs", &seekTimeUs));
+ CHECK(msg->findInt32("precise", &precise));
+ // TODO: add "precise" to performSeek.
performSeek(seekTimeUs);
return;
} else if (msg->what() == kWhatPollBuffering) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index c7834ef..f2d9cc0 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -49,7 +49,7 @@
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
virtual status_t getDuration(int64_t *durationUs);
- virtual status_t seekTo(int64_t seekTimeUs);
+ virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libnbaio/Android.bp b/media/libnbaio/Android.bp
index 45ad16f..615b541 100644
--- a/media/libnbaio/Android.bp
+++ b/media/libnbaio/Android.bp
@@ -22,7 +22,6 @@
// static_libs: ["libsndfile"],
shared_libs: [
- "libaudiohal",
"libaudioutils",
"libbinder",
"libcutils",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ed29859..094f5cc 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -44,6 +44,7 @@
#include <media/stagefright/PersistentSurface.h>
#include <media/stagefright/SurfaceUtils.h>
#include <media/hardware/HardwareAPI.h>
+#include <media/OMXBuffer.h>
#include <OMX_AudioExt.h>
#include <OMX_VideoExt.h>
@@ -796,7 +797,7 @@
status_t err;
if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
- if (storingMetadataInDecodedBuffers()) {
+ if (storingMetadataInDecodedBuffers() && !mLegacyAdaptiveExperiment) {
err = allocateOutputMetadataBuffers();
} else {
err = allocateOutputBuffersFromNativeWindow();
@@ -866,7 +867,8 @@
size_t totalSize = def.nBufferCountActual * (alignedSize + alignedConvSize);
mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
- const sp<AMessage> &format = portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
+ const sp<AMessage> &format =
+ portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
for (OMX_U32 i = 0; i < def.nBufferCountActual && err == OK; ++i) {
sp<IMemory> mem = mDealer[portIndex]->allocate(bufSize);
if (mem == NULL || mem->pointer() == NULL) {
@@ -892,8 +894,8 @@
: new SecureBuffer(format, native_handle, bufSize);
info.mCodecData = info.mData;
} else {
- err = mOMXNode->useBuffer(
- portIndex, mem, &info.mBufferID, allottedSize);
+ err = mOMXNode->useBuffer(portIndex,
+ OMXBuffer(mem, allottedSize), &info.mBufferID);
}
if (mem != NULL) {
@@ -1084,6 +1086,11 @@
}
status_t ACodec::allocateOutputBuffersFromNativeWindow() {
+ // This method only handles the non-metadata mode, or legacy metadata mode
+ // (where the headers for each buffer id will be fixed). Non-legacy metadata
+ // mode shouldn't go through this path.
+ CHECK(!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment);
+
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
&bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
@@ -1091,10 +1098,8 @@
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
- if (!storingMetadataInDecodedBuffers()) {
- static_cast<Surface*>(mNativeWindow.get())
- ->getIGraphicBufferProducer()->allowAllocation(true);
- }
+ static_cast<Surface*>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(true);
ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
"output port",
@@ -1117,11 +1122,19 @@
info.mIsReadFence = false;
info.mRenderInfo = NULL;
info.mGraphicBuffer = graphicBuffer;
+
+ // TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
+ // OMX doesn't use the shared memory buffer, but some code still
+ // access info.mData. Create an ABuffer as a placeholder.
+ if (storingMetadataInDecodedBuffers()) {
+ info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
+ info.mCodecData = info.mData;
+ }
+
mBuffers[kPortIndexOutput].push(info);
IOMX::buffer_id bufferId;
- err = mOMXNode->useGraphicBuffer(
- kPortIndexOutput, graphicBuffer, &bufferId);
+ err = mOMXNode->useBuffer(kPortIndexOutput, graphicBuffer, &bufferId);
if (err != 0) {
ALOGE("registering GraphicBuffer %u with OMX IL component failed: "
"%d", i, err);
@@ -1138,9 +1151,9 @@
OMX_U32 cancelStart;
OMX_U32 cancelEnd;
- if (err != 0) {
+ if (err != 0 || storingMetadataInDecodedBuffers()) {
// If an error occurred while dequeuing we need to cancel any buffers
- // that were dequeued.
+ // that were dequeued. Also cancel all if we're in legacy metadata mode.
cancelStart = 0;
cancelEnd = mBuffers[kPortIndexOutput].size();
} else {
@@ -1159,19 +1172,23 @@
}
}
- if (!storingMetadataInDecodedBuffers()) {
- static_cast<Surface*>(mNativeWindow.get())
- ->getIGraphicBufferProducer()->allowAllocation(false);
+ static_cast<Surface*>(mNativeWindow.get())
+ ->getIGraphicBufferProducer()->allowAllocation(false);
+
+ if (storingMetadataInDecodedBuffers()) {
+ mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
}
return err;
}
status_t ACodec::allocateOutputMetadataBuffers() {
+ CHECK(storingMetadataInDecodedBuffers() && !mLegacyAdaptiveExperiment);
+
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
&bufferCount, &bufferSize, &minUndequeuedBuffers,
- mLegacyAdaptiveExperiment /* preregister */);
+ false /* preregister */);
if (err != 0)
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
@@ -1184,7 +1201,6 @@
size_t totalSize = bufferCount * align(bufSize, MemoryDealer::getAllocationAlignment());
mDealer[kPortIndexOutput] = new MemoryDealer(totalSize, "ACodec");
- // Dequeue buffers and send them to OMX
for (OMX_U32 i = 0; i < bufferCount; i++) {
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
@@ -1205,57 +1221,13 @@
info.mCodecData = info.mData;
info.mCodecRef = mem;
- err = mOMXNode->useBuffer(
- kPortIndexOutput, mem, &info.mBufferID, mem->size());
+ err = mOMXNode->useBuffer(kPortIndexOutput, mem, &info.mBufferID);
mBuffers[kPortIndexOutput].push(info);
ALOGV("[%s] allocated meta buffer with ID %u (pointer = %p)",
mComponentName.c_str(), info.mBufferID, mem->pointer());
}
- if (mLegacyAdaptiveExperiment) {
- // preallocate and preregister buffers
- static_cast<Surface *>(mNativeWindow.get())
- ->getIGraphicBufferProducer()->allowAllocation(true);
-
- ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
- "output port",
- mComponentName.c_str(), bufferCount, bufferSize);
-
- // Dequeue buffers then cancel them all
- for (OMX_U32 i = 0; i < bufferCount; i++) {
- BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
-
- ANativeWindowBuffer *buf;
- int fenceFd;
- err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
- if (err != 0) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
- break;
- }
-
- sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
- mOMXNode->updateGraphicBufferInMeta(
- kPortIndexOutput, graphicBuffer, info->mBufferID);
- info->mStatus = BufferInfo::OWNED_BY_US;
- info->setWriteFence(fenceFd, "allocateOutputMetadataBuffers for legacy");
- info->mGraphicBuffer = graphicBuffer;
- }
-
- for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) {
- BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
- if (info->mStatus == BufferInfo::OWNED_BY_US) {
- status_t error = cancelBufferToNativeWindow(info);
- if (err == OK) {
- err = error;
- }
- }
- }
-
- static_cast<Surface*>(mNativeWindow.get())
- ->getIGraphicBufferProducer()->allowAllocation(false);
- }
-
mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
return err;
}
@@ -1275,13 +1247,7 @@
--mMetadataBuffersToSubmit;
info->checkWriteFence("submitOutputMetadataBuffer");
- status_t err = mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
- info->mFenceFd = -1;
- if (err == OK) {
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- }
-
- return err;
+ return fillBuffer(info);
}
status_t ACodec::waitForFence(int fd, const char *dbg ) {
@@ -1466,6 +1432,12 @@
// while loop above does not complete
CHECK(storingMetadataInDecodedBuffers());
+ if (storingMetadataInDecodedBuffers() && mLegacyAdaptiveExperiment) {
+ // If we're here while running legacy experiment, we dequeued some
+ // unrecognized buffers, and the experiment can't continue.
+ ALOGE("Legacy experiment failed, drop back to metadata mode");
+ mLegacyAdaptiveExperiment = false;
+ }
// discard buffer in LRU info and replace with new buffer
oldest->mGraphicBuffer = new GraphicBuffer(buf, false);
oldest->mStatus = BufferInfo::OWNED_BY_US;
@@ -1473,25 +1445,22 @@
mRenderTracker.untrackFrame(oldest->mRenderInfo);
oldest->mRenderInfo = NULL;
- mOMXNode->updateGraphicBufferInMeta(
- kPortIndexOutput, oldest->mGraphicBuffer, oldest->mBufferID);
-
if (mOutputMetadataType == kMetadataBufferTypeGrallocSource) {
VideoGrallocMetadata *grallocMeta =
- reinterpret_cast<VideoGrallocMetadata *>(oldest->mData->data());
+ reinterpret_cast<VideoGrallocMetadata *>(oldest->mCodecData->base());
ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
(unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
mDequeueCounter - oldest->mDequeuedAt,
(void *)(uintptr_t)grallocMeta->pHandle,
- oldest->mGraphicBuffer->handle, oldest->mData->data());
+ oldest->mGraphicBuffer->handle, oldest->mCodecData->base());
} else if (mOutputMetadataType == kMetadataBufferTypeANWBuffer) {
VideoNativeMetadata *nativeMeta =
- reinterpret_cast<VideoNativeMetadata *>(oldest->mData->data());
+ reinterpret_cast<VideoNativeMetadata *>(oldest->mCodecData->base());
ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
(unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
mDequeueCounter - oldest->mDequeuedAt,
(void *)(uintptr_t)nativeMeta->pBuffer,
- oldest->mGraphicBuffer->getNativeBuffer(), oldest->mData->data());
+ oldest->mGraphicBuffer->getNativeBuffer(), oldest->mCodecData->base());
}
updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
@@ -1541,9 +1510,9 @@
// there should not be any fences in the metadata
MetadataBufferType type =
portIndex == kPortIndexOutput ? mOutputMetadataType : mInputMetadataType;
- if (type == kMetadataBufferTypeANWBuffer && info->mData != NULL
- && info->mData->size() >= sizeof(VideoNativeMetadata)) {
- int fenceFd = ((VideoNativeMetadata *)info->mData->data())->nFenceFd;
+ if (type == kMetadataBufferTypeANWBuffer && info->mCodecData != NULL
+ && info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
+ int fenceFd = ((VideoNativeMetadata *)info->mCodecData->base())->nFenceFd;
if (fenceFd >= 0) {
ALOGW("unreleased fence (%d) in %s metadata buffer %zu",
fenceFd, portIndex == kPortIndexInput ? "input" : "output", i);
@@ -1598,6 +1567,23 @@
return NULL;
}
+status_t ACodec::fillBuffer(BufferInfo *info) {
+ status_t err;
+ if (!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment) {
+ err = mOMXNode->fillBuffer(
+ info->mBufferID, OMXBuffer::sPreset, info->mFenceFd);
+ } else {
+ err = mOMXNode->fillBuffer(
+ info->mBufferID, info->mGraphicBuffer, info->mFenceFd);
+ }
+
+ info->mFenceFd = -1;
+ if (err == OK) {
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ }
+ return err;
+}
+
status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
const char *role = GetComponentRole(isEncoder, mime);
@@ -2190,7 +2176,6 @@
// NOTE: both mBaseOutputFormat and mOutputFormat are outputFormat to signal first frame.
mBaseOutputFormat = outputFormat;
- // trigger a kWhatOutputFormatChanged msg on first buffer
mLastOutputFormat.clear();
err = getPortFormat(kPortIndexInput, inputFormat);
@@ -5188,11 +5173,6 @@
mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay, mEncoderPadding, channelCount);
}
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatOutputFormatChanged);
- notify->setMessage("format", mOutputFormat);
- notify->post();
-
// mLastOutputFormat is not used when tunneled; doing this just to stay consistent
mLastOutputFormat = mOutputFormat;
}
@@ -5572,8 +5552,8 @@
notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
notify->setInt32("buffer-id", info->mBufferID);
- info->mData->meta()->clear();
- notify->setObject("buffer", info->mData);
+ notify->setObject("buffer", info->mData->clone(mCodec->mInputFormat));
+ info->mData.clear();
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec);
reply->setInt32("buffer-id", info->mBufferID);
@@ -5606,8 +5586,6 @@
mCodec->mComponentName.c_str(), err);
eos = true;
}
-
- buffer.clear();
} else {
buffer = static_cast<MediaCodecBuffer *>(obj.get());
}
@@ -5628,6 +5606,7 @@
}
info->mStatus = BufferInfo::OWNED_BY_US;
+ info->mData = buffer;
switch (mode) {
case KEEP_BUFFERS:
@@ -5672,11 +5651,12 @@
flags |= OMX_BUFFERFLAG_EOS;
}
- if (buffer != info->mCodecData) {
+ size_t size = buffer->size();
+ if (buffer->base() != info->mCodecData->base()) {
ALOGV("[%s] Needs to copy input data for buffer %u. (%p != %p)",
mCodec->mComponentName.c_str(),
bufferID,
- buffer.get(), info->mCodecData.get());
+ buffer->base(), info->mCodecData->base());
sp<DataConverter> converter = mCodec->mConverter[kPortIndexInput];
if (converter == NULL || isCSD) {
@@ -5687,6 +5667,9 @@
mCodec->signalError(OMX_ErrorUndefined, err);
return;
}
+ size = info->mCodecData->size();
+ } else {
+ info->mCodecData->setRange(0, size);
}
if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
@@ -5729,25 +5712,29 @@
status_t err2 = OK;
switch (metaType) {
case kMetadataBufferTypeInvalid:
+ {
+ err2 = mCodec->mOMXNode->emptyBuffer(
+ bufferID, info->mCodecData, flags, timeUs, info->mFenceFd);
+ }
break;
#ifndef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
case kMetadataBufferTypeNativeHandleSource:
if (info->mCodecData->size() >= sizeof(VideoNativeHandleMetadata)) {
VideoNativeHandleMetadata *vnhmd =
(VideoNativeHandleMetadata*)info->mCodecData->base();
- err2 = mCodec->mOMXNode->updateNativeHandleInMeta(
- mCodec->kPortIndexInput,
- NativeHandle::create(vnhmd->pHandle, false /* ownsHandle */),
- bufferID);
+ sp<NativeHandle> handle = NativeHandle::create(
+ vnhmd->pHandle, false /* ownsHandle */);
+ err2 = mCodec->mOMXNode->emptyBuffer(
+ bufferID, handle, flags, timeUs, info->mFenceFd);
}
break;
case kMetadataBufferTypeANWBuffer:
if (info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
VideoNativeMetadata *vnmd = (VideoNativeMetadata*)info->mCodecData->base();
- err2 = mCodec->mOMXNode->updateGraphicBufferInMeta(
- mCodec->kPortIndexInput,
- new GraphicBuffer(vnmd->pBuffer, false /* keepOwnership */),
- bufferID);
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ vnmd->pBuffer, false /* keepOwnership */);
+ err2 = mCodec->mOMXNode->emptyBuffer(
+ bufferID, graphicBuffer, flags, timeUs, info->mFenceFd);
}
break;
#endif
@@ -5759,21 +5746,14 @@
break;
}
- if (err2 == OK) {
- err2 = mCodec->mOMXNode->emptyBuffer(
- bufferID,
- 0,
- info->mCodecData->size(),
- flags,
- timeUs,
- info->mFenceFd);
- }
info->mFenceFd = -1;
if (err2 != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
return;
}
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ // Hold the reference while component is using the buffer.
+ info->mData = buffer;
if (!eos && err == OK) {
getMoreInputDataIfPossible();
@@ -5798,12 +5778,7 @@
info->checkReadFence("onInputBufferFilled");
status_t err2 = mCodec->mOMXNode->emptyBuffer(
- bufferID,
- 0,
- 0,
- OMX_BUFFERFLAG_EOS,
- 0,
- info->mFenceFd);
+ bufferID, OMXBuffer::sPreset, OMX_BUFFERFLAG_EOS, 0, info->mFenceFd);
info->mFenceFd = -1;
if (err2 != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
@@ -5927,20 +5902,17 @@
ALOGV("[%s] calling fillBuffer %u",
mCodec->mComponentName.c_str(), info->mBufferID);
- err = mCodec->mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
- info->mFenceFd = -1;
+ err = mCodec->fillBuffer(info);
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
return true;
}
-
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
break;
}
sp<AMessage> reply =
new AMessage(kWhatOutputBufferDrained, mCodec);
- sp<MediaCodecBuffer> buffer = info->mData;
+ sp<MediaCodecBuffer> buffer = info->mData->clone(mCodec->mOutputFormat);
if (mCodec->mOutputFormat != mCodec->mLastOutputFormat && rangeLength > 0) {
// pretend that output format has changed on the first frame (we used to do this)
@@ -5971,7 +5943,7 @@
buffer->meta()->setPointer("handle", handle);
buffer->meta()->setInt32("rangeOffset", rangeOffset);
buffer->meta()->setInt32("rangeLength", rangeLength);
- } else if (buffer == info->mCodecData) {
+ } else if (buffer->base() == info->mCodecData->base()) {
buffer->setRange(rangeOffset, rangeLength);
} else {
info->mCodecData->setRange(rangeOffset, rangeLength);
@@ -6000,6 +5972,7 @@
notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
notify->setInt32("buffer-id", info->mBufferID);
notify->setObject("buffer", buffer);
+ info->mData.clear();
notify->setInt32("flags", flags);
reply->setInt32("buffer-id", info->mBufferID);
@@ -6056,6 +6029,7 @@
mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
return;
}
+ info->mData = buffer;
android_native_rect_t crop;
if (msg->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)
@@ -6155,12 +6129,8 @@
ALOGV("[%s] calling fillBuffer %u",
mCodec->mComponentName.c_str(), info->mBufferID);
info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS");
- status_t err = mCodec->mOMXNode->fillBuffer(
- info->mBufferID, info->mFenceFd);
- info->mFenceFd = -1;
- if (err == OK) {
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- } else {
+ status_t err = mCodec->fillBuffer(info);
+ if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
}
}
@@ -6960,14 +6930,11 @@
ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID);
info->checkWriteFence("submitRegularOutputBuffers");
- status_t err = mCodec->mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
- info->mFenceFd = -1;
+ status_t err = mCodec->fillBuffer(info);
if (err != OK) {
failed = true;
break;
}
-
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
if (failed) {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 2c741db..4eacff5 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -72,6 +72,8 @@
$(TOP)/external/flac/include \
$(TOP)/external/tremolo \
$(TOP)/external/libvpx/libwebm \
+ $(TOP)/external/icu/icu4c/source/common \
+ $(TOP)/external/icu/icu4c/source/i18n \
$(TOP)/system/netd/include \
$(call include-path-for, audio-utils)
@@ -84,23 +86,15 @@
libdrmframework \
libexpat \
libgui \
- libicui18n \
- libicuuc \
liblog \
libmedia \
libmediautils \
libnetd_client \
- libopus \
libsonivox \
- libssl \
libstagefright_omx \
- libstagefright_yuv \
- libsync \
libui \
libutils \
libvorbisidec \
- libz \
- libpowermanager
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
@@ -118,8 +112,6 @@
libmedia_helper \
LOCAL_SHARED_LIBRARIES += \
- libstagefright_enc_common \
- libstagefright_avc_common \
libstagefright_foundation \
libdl \
libRScpp \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 790c6da..4ccd2d0 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -60,9 +60,10 @@
mStartTimeUs(0),
mMaxAmplitude(0),
mPrevSampleTimeUs(0),
- mFirstSampleTimeUs(-1ll),
mInitialReadTimeUs(0),
mNumFramesReceived(0),
+ mNumFramesSkipped(0),
+ mNumFramesLost(0),
mNumClientOwnedBuffers(0) {
ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
sampleRate, outSampleRate, channelCount);
@@ -277,12 +278,8 @@
}
if (mSampleRate != mOutSampleRate) {
- if (mFirstSampleTimeUs < 0) {
- mFirstSampleTimeUs = timeUs;
- }
- timeUs = mFirstSampleTimeUs + (timeUs - mFirstSampleTimeUs)
- * (int64_t)mSampleRate / (int64_t)mOutSampleRate;
- buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate;
+ buffer->meta_data()->setInt64(kKeyTime, timeUs);
}
*out = buffer;
@@ -300,11 +297,27 @@
}
status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) {
- int64_t timeUs = systemTime() / 1000ll;
- // Estimate the real sampling time of the 1st sample in this buffer
- // from AudioRecord's latency. (Apply this adjustment first so that
- // the start time logic is not affected.)
- timeUs -= mRecord->latency() * 1000LL;
+ int64_t timeUs, position, timeNs;
+ ExtendedTimestamp ts;
+ ExtendedTimestamp::Location location;
+ const int32_t usPerSec = 1000000;
+
+ if (mRecord->getTimestamp(&ts) == OK &&
+ ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC,
+ &location) == OK) {
+ // Use audio timestamp.
+ timeUs = timeNs / 1000 -
+ (position - mNumFramesSkipped -
+ mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
+ } else {
+ // This should not happen in normal case.
+ ALOGW("Failed to get audio timestamp, fallback to use systemclock");
+ timeUs = systemTime() / 1000ll;
+ // Estimate the real sampling time of the 1st sample in this buffer
+ // from AudioRecord's latency. (Apply this adjustment first so that
+ // the start time logic is not affected.)
+ timeUs -= mRecord->latency() * 1000LL;
+ }
ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
Mutex::Autolock autoLock(mLock);
@@ -313,10 +326,15 @@
return OK;
}
+ const size_t bufferSize = audioBuffer.size;
+
// Drop retrieved and previously lost audio data.
if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
(void) mRecord->getInputFramesLost();
- ALOGV("Drop audio data at %" PRId64 "/%" PRId64 " us", timeUs, mStartTimeUs);
+ int64_t receievedFrames = bufferSize / mRecord->frameSize();
+ ALOGV("Drop audio data(%" PRId64 " frames) at %" PRId64 "/%" PRId64 " us",
+ receievedFrames, timeUs, mStartTimeUs);
+ mNumFramesSkipped += receievedFrames;
return OK;
}
@@ -325,11 +343,7 @@
// Initial delay
if (mStartTimeUs > 0) {
mStartTimeUs = timeUs - mStartTimeUs;
- } else {
- // Assume latency is constant.
- mStartTimeUs += mRecord->latency() * 1000;
}
-
mPrevSampleTimeUs = mStartTimeUs;
}
@@ -359,6 +373,7 @@
MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
memset(lostAudioBuffer->data(), 0, bufferSize);
lostAudioBuffer->set_range(0, bufferSize);
+ mNumFramesLost += bufferSize / mRecord->frameSize();
queueInputBuffer_l(lostAudioBuffer, timeUs);
}
@@ -367,7 +382,6 @@
return OK;
}
- const size_t bufferSize = audioBuffer.size;
MediaBuffer *buffer = new MediaBuffer(bufferSize);
memcpy((uint8_t *) buffer->data(),
audioBuffer.i16, audioBuffer.size);
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index 4ed7911..81fe0fe 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -34,7 +34,11 @@
mMemory(mem) {
}
-SecureBuffer::SecureBuffer(const sp<AMessage> &format, void *ptr, size_t size)
+sp<MediaCodecBuffer> SharedMemoryBuffer::clone(const sp<AMessage> &format) {
+ return new SharedMemoryBuffer(format, mMemory);
+}
+
+SecureBuffer::SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size)
: MediaCodecBuffer(format, new ABuffer(nullptr, size)),
mPointer(ptr) {
}
@@ -46,6 +50,12 @@
mHandle(handle) {
}
+sp<MediaCodecBuffer> SecureBuffer::clone(const sp<AMessage> &format) {
+ return (mHandle == nullptr)
+ ? new SecureBuffer(format, mPointer, capacity())
+ : new SecureBuffer(format, mHandle, capacity());
+}
+
void *SecureBuffer::getDestinationPointer() {
return (void *)(mHandle == nullptr ? mPointer : mHandle->handle());
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 408ad7a..990d4b7 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -111,6 +111,11 @@
}
static int32_t getColorFormat(const char* colorFormat) {
+ if (!colorFormat) {
+ ALOGE("Invalid color format");
+ return -1;
+ }
+
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
return OMX_COLOR_FormatYUV420Planar;
}
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index 1af7dc1..1b88e5d 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -73,6 +73,10 @@
class FLACParser : public RefBase {
public:
+ enum {
+ kMaxChannels = 8,
+ };
+
explicit FLACParser(
const sp<DataSource> &dataSource,
// If metadata pointers aren't provided, we don't fill them
@@ -122,7 +126,7 @@
// media buffers
size_t mMaxBufferSize;
MediaBufferGroup *mGroup;
- void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels);
+ void (*mCopy)(short *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
// handle to underlying libFLAC parser
FLAC__StreamDecoder *mDecoder;
@@ -139,7 +143,7 @@
bool mWriteRequested;
bool mWriteCompleted;
FLAC__FrameHeader mWriteHeader;
- const FLAC__int32 * const *mWriteBuffer;
+ FLAC__int32 const * mWriteBuffer[kMaxChannels];
// most recent error reported by libFLAC parser
FLAC__StreamDecoderErrorStatus mErrorStatus;
@@ -323,7 +327,7 @@
mWriteRequested = false;
// FLAC parser doesn't free or realloc buffer until next frame or finish
mWriteHeader = frame->header;
- mWriteBuffer = buffer;
+ memmove(mWriteBuffer, buffer, sizeof(const FLAC__int32 * const) * getChannels());
mWriteCompleted = true;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
} else {
@@ -382,7 +386,7 @@
static void copyMono8(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -392,7 +396,7 @@
static void copyStereo8(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -401,7 +405,7 @@
}
}
-static void copyMultiCh8(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh8(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
{
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
@@ -412,7 +416,7 @@
static void copyMono16(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -422,7 +426,7 @@
static void copyStereo16(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -431,7 +435,7 @@
}
}
-static void copyMultiCh16(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh16(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
{
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
@@ -444,7 +448,7 @@
static void copyMono24(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -454,7 +458,7 @@
static void copyStereo24(
short *dst,
- const int *const *src,
+ const int * src[FLACParser::kMaxChannels],
unsigned nSamples,
unsigned /* nChannels */) {
for (unsigned i = 0; i < nSamples; ++i) {
@@ -463,7 +467,7 @@
}
}
-static void copyMultiCh24(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh24(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
{
for (unsigned i = 0; i < nSamples; ++i) {
for (unsigned c = 0; c < nChannels; ++c) {
@@ -474,7 +478,7 @@
static void copyTrespass(
short * /* dst */,
- const int *const * /* src */,
+ const int *[FLACParser::kMaxChannels] /* src */,
unsigned /* nSamples */,
unsigned /* nChannels */) {
TRESPASS();
@@ -499,7 +503,6 @@
mStreamInfoValid(false),
mWriteRequested(false),
mWriteCompleted(false),
- mWriteBuffer(NULL),
mErrorStatus((FLAC__StreamDecoderErrorStatus) -1)
{
ALOGV("FLACParser::FLACParser");
@@ -555,7 +558,7 @@
}
if (mStreamInfoValid) {
// check channel count
- if (getChannels() == 0 || getChannels() > 8) {
+ if (getChannels() == 0 || getChannels() > kMaxChannels) {
ALOGE("unsupported channel count %u", getChannels());
return NO_INIT;
}
@@ -591,7 +594,7 @@
static const struct {
unsigned mChannels;
unsigned mBitsPerSample;
- void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels);
+ void (*mCopy)(short *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
} table[] = {
{ 1, 8, copyMono8 },
{ 2, 8, copyStereo8 },
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 5ab5f3e..e617b24 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -173,8 +173,9 @@
// static
sp<MediaCodec> MediaCodec::CreateByType(
- const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid) {
- sp<MediaCodec> codec = new MediaCodec(looper, pid);
+ const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
+ uid_t uid) {
+ sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
if (err != NULL) {
@@ -185,8 +186,8 @@
// static
sp<MediaCodec> MediaCodec::CreateByComponentName(
- const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid) {
- sp<MediaCodec> codec = new MediaCodec(looper, pid);
+ const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) {
+ sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
if (err != NULL) {
@@ -234,7 +235,7 @@
return new PersistentSurface(bufferProducer, bufferSource);
}
-MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid)
+MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
: mState(UNINITIALIZED),
mReleasedByResourceManager(false),
mLooper(looper),
@@ -256,6 +257,11 @@
mDequeueOutputReplyID(0),
mHaveInputSurface(false),
mHavePendingInputBuffers(false) {
+ if (uid == kNoUid) {
+ mUid = IPCThreadState::self()->getCallingUid();
+ } else {
+ mUid = uid;
+ }
}
MediaCodec::~MediaCodec() {
@@ -929,9 +935,8 @@
return INVALID_OPERATION;
}
- // by the time buffers array is initialized, crypto is set
*buffer = info.mData;
- *format = info.mFormat;
+ *format = info.mData->format();
return OK;
}
@@ -1323,6 +1328,7 @@
|| portIndex == kPortIndexOutput);
mPortBuffers[portIndex].clear();
+ mPortBufferArrays[portIndex].clear();
Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
@@ -1349,16 +1355,14 @@
BufferInfo info;
info.mBufferID = portDesc->bufferIDAt(i);
info.mOwnedByClient = false;
- info.mData = portDesc->bufferAt(i);
-
+ sp<MediaCodecBuffer> buffer = portDesc->bufferAt(i);
if (portIndex == kPortIndexInput && mCrypto != NULL) {
- sp<IMemory> mem = mDealer->allocate(info.mData->capacity());
- info.mSecureData = info.mData;
- info.mData = new SharedMemoryBuffer(mInputFormat, mem);
- info.mSharedEncryptedBuffer = mem;
+ info.mSharedEncryptedBuffer = mDealer->allocate(buffer->capacity());
+ buffer = new SharedMemoryBuffer(
+ mInputFormat, info.mSharedEncryptedBuffer);
}
-
buffers->push_back(info);
+ mPortBufferArrays[portIndex].push_back(buffer);
}
if (portIndex == kPortIndexOutput) {
@@ -1382,62 +1386,6 @@
break;
}
- case CodecBase::kWhatOutputFormatChanged:
- {
- CHECK(msg->findMessage("format", &mOutputFormat));
-
- ALOGV("[%s] output format changed to: %s",
- mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
-
- if (mSoftRenderer == NULL &&
- mSurface != NULL &&
- (mFlags & kFlagUsesSoftwareRenderer)) {
- AString mime;
- CHECK(mOutputFormat->findString("mime", &mime));
-
- // TODO: propagate color aspects to software renderer to allow better
- // color conversion to RGB. For now, just mark dataspace for YUV
- // rendering.
- int32_t dataSpace;
- if (mOutputFormat->findInt32("android._dataspace", &dataSpace)) {
- ALOGD("[%s] setting dataspace on output surface to #%x",
- mComponentName.c_str(), dataSpace);
- int err = native_window_set_buffers_data_space(
- mSurface.get(), (android_dataspace)dataSpace);
- ALOGW_IF(err != 0, "failed to set dataspace on surface (%d)", err);
- }
-
- if (mime.startsWithIgnoreCase("video/")) {
- mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees);
- }
- }
-
- if (mFlags & kFlagIsEncoder) {
- // Before we announce the format change we should
- // collect codec specific data and amend the output
- // format as necessary.
- mFlags |= kFlagGatherCodecSpecificData;
- } else if (mFlags & kFlagIsAsync) {
- onOutputFormatChanged();
- } else {
- mFlags |= kFlagOutputFormatChanged;
- postActivityNotificationIfPossible();
- }
-
- // Notify mCrypto of video resolution changes
- if (mCrypto != NULL) {
- int32_t left, top, right, bottom, width, height;
- if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
- mCrypto->notifyResolution(right - left + 1, bottom - top + 1);
- } else if (mOutputFormat->findInt32("width", &width)
- && mOutputFormat->findInt32("height", &height)) {
- mCrypto->notifyResolution(width, height);
- }
- }
-
- break;
- }
-
case CodecBase::kWhatOutputFramesRendered:
{
// ignore these in all states except running, and check that we have a
@@ -1528,27 +1476,65 @@
CHECK(msg->findInt32("flags", &omxFlags));
buffer->meta()->setInt32("omxFlags", omxFlags);
+ if (mOutputFormat != buffer->format()) {
+ mOutputFormat = buffer->format();
+ ALOGV("[%s] output format changed to: %s",
+ mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
- if (mFlags & kFlagGatherCodecSpecificData) {
- // This is the very first output buffer after a
- // format change was signalled, it'll either contain
- // the one piece of codec specific data we can expect
- // or there won't be codec specific data.
- if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
- status_t err =
- amendOutputFormatWithCodecSpecificData(buffer);
+ if (mSoftRenderer == NULL &&
+ mSurface != NULL &&
+ (mFlags & kFlagUsesSoftwareRenderer)) {
+ AString mime;
+ CHECK(mOutputFormat->findString("mime", &mime));
- if (err != OK) {
- ALOGE("Codec spit out malformed codec "
- "specific data!");
+ // TODO: propagate color aspects to software renderer to allow better
+ // color conversion to RGB. For now, just mark dataspace for YUV
+ // rendering.
+ int32_t dataSpace;
+ if (mOutputFormat->findInt32("android._dataspace", &dataSpace)) {
+ ALOGD("[%s] setting dataspace on output surface to #%x",
+ mComponentName.c_str(), dataSpace);
+ int err = native_window_set_buffers_data_space(
+ mSurface.get(), (android_dataspace)dataSpace);
+ ALOGW_IF(err != 0, "failed to set dataspace on surface (%d)", err);
+ }
+
+ if (mime.startsWithIgnoreCase("video/")) {
+ mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees);
}
}
- mFlags &= ~kFlagGatherCodecSpecificData;
+ if (mFlags & kFlagIsEncoder) {
+ // Before we announce the format change we should
+ // collect codec specific data and amend the output
+ // format as necessary.
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ status_t err =
+ amendOutputFormatWithCodecSpecificData(buffer);
+
+ if (err != OK) {
+ ALOGE("Codec spit out malformed codec "
+ "specific data!");
+ }
+ }
+ }
+
if (mFlags & kFlagIsAsync) {
onOutputFormatChanged();
} else {
mFlags |= kFlagOutputFormatChanged;
+ postActivityNotificationIfPossible();
+ }
+
+ // Notify mCrypto of video resolution changes
+ if (mCrypto != NULL) {
+ int32_t left, top, right, bottom, width, height;
+ if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
+ mCrypto->notifyResolution(right - left + 1, bottom - top + 1);
+ } else if (mOutputFormat->findInt32("width", &width)
+ && mOutputFormat->findInt32("height", &height)) {
+ mCrypto->notifyResolution(width, height);
+ }
}
}
@@ -2170,12 +2156,10 @@
// createInputSurface(), or persistent set by setInputSurface()),
// give the client an empty input buffers array.
if (portIndex != kPortIndexInput || !mHaveInputSurface) {
- const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+ const Vector<sp<MediaCodecBuffer>> &srcBuffers = mPortBufferArrays[portIndex];
for (size_t i = 0; i < srcBuffers.size(); ++i) {
- const BufferInfo &info = srcBuffers.itemAt(i);
-
- dstBuffers->push_back(info.mData);
+ dstBuffers->push_back(srcBuffers[i]);
}
}
@@ -2345,7 +2329,6 @@
mFlags &= ~kFlagOutputBuffersChanged;
mFlags &= ~kFlagStickyError;
mFlags &= ~kFlagIsEncoder;
- mFlags &= ~kFlagGatherCodecSpecificData;
mFlags &= ~kFlagIsAsync;
mStickyError = OK;
@@ -2387,12 +2370,15 @@
if (info->mNotify != NULL) {
sp<AMessage> msg = info->mNotify;
info->mNotify = NULL;
+ msg->setObject("buffer", (portIndex == kPortIndexInput && mCrypto != NULL)
+ ? info->mSecureData : info->mData);
if (isReclaim && info->mOwnedByClient) {
ALOGD("port %d buffer %zu still owned by client when codec is reclaimed",
portIndex, i);
} else {
- // TODO: clear memory reference.
info->mOwnedByClient = false;
+ info->mData.clear();
+ info->mSecureData.clear();
}
if (portIndex == kPortIndexInput) {
@@ -2404,6 +2390,7 @@
}
mAvailPortBuffers[portIndex].clear();
+ mPortBufferArrays[portIndex].clear();
}
size_t MediaCodec::updateBuffers(
@@ -2412,6 +2399,9 @@
uint32_t bufferID;
CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
@@ -2422,8 +2412,13 @@
CHECK(info->mNotify == NULL);
CHECK(msg->findMessage("reply", &info->mNotify));
- info->mFormat =
- (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat;
+ if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ info->mSecureData = buffer;
+ info->mData = new SharedMemoryBuffer(
+ buffer->format(), info->mSharedEncryptedBuffer);
+ } else {
+ info->mData = buffer;
+ }
mAvailPortBuffers[portIndex].push_back(i);
return i;
@@ -2556,13 +2551,14 @@
if (flags & BUFFER_FLAG_CODECCONFIG) {
buffer->meta()->setInt32("csd", true);
}
- // TODO: release buffer reference.
// synchronization boundary for getBufferAndFormat
{
Mutex::Autolock al(mBufferLock);
info->mOwnedByClient = false;
}
+ info->mData.clear();
+ info->mSecureData.clear();
reply->setObject("buffer", buffer);
reply->post();
@@ -2634,7 +2630,7 @@
if (mSoftRenderer != NULL) {
std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
info->mData->data(), info->mData->size(),
- mediaTimeUs, renderTimeNs, NULL, info->mFormat);
+ mediaTimeUs, renderTimeNs, NULL, info->mData->format());
// if we are running, notify rendered frames
if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
@@ -2649,7 +2645,7 @@
}
info->mNotify->setObject("buffer", info->mData);
- // TODO: release buffer reference.
+ info->mData.clear();
info->mNotify->post();
info->mNotify.clear();
@@ -2675,13 +2671,13 @@
info->mOwnedByClient = true;
// set image-data
- if (info->mFormat != NULL) {
+ if (info->mData->format() != NULL) {
sp<ABuffer> imageData;
- if (info->mFormat->findBuffer("image-data", &imageData)) {
+ if (info->mData->format()->findBuffer("image-data", &imageData)) {
info->mData->meta()->setBuffer("image-data", imageData);
}
int32_t left, top, right, bottom;
- if (info->mFormat->findRect("crop", &left, &top, &right, &bottom)) {
+ if (info->mData->format()->findRect("crop", &left, &top, &right, &bottom)) {
info->mData->meta()->setRect("crop-rect", left, top, right, bottom);
}
}
@@ -2921,10 +2917,10 @@
}
if (mState == CONFIGURED && !mBatteryStatNotified) {
- BatteryNotifier::getInstance().noteStartVideo();
+ BatteryNotifier::getInstance().noteStartVideo(mUid);
mBatteryStatNotified = true;
} else if (mState == UNINITIALIZED && mBatteryStatNotified) {
- BatteryNotifier::getInstance().noteStopVideo();
+ BatteryNotifier::getInstance().noteStopVideo(mUid);
mBatteryStatNotified = false;
}
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 93643c2..d3b34b7 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -1169,7 +1169,7 @@
}
}
- if (flags & kPreferSoftwareCodecs) {
+ if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) {
matches->sort(compareSoftwareCodecsFirst);
}
}
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index ad27856..e3270ed 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -51,7 +51,8 @@
for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
TrackInfo *info = &mSelectedTracks.editItemAt(i);
- CHECK_EQ((status_t)OK, info->mSource->stop());
+ status_t err = info->mSource->stop();
+ ALOGE_IF(err != OK, "error %d stopping track %zu", err, i);
}
mSelectedTracks.clear();
diff --git a/media/libstagefright/codec2/Android.mk b/media/libstagefright/codec2/Android.mk
new file mode 100644
index 0000000..a463205
--- /dev/null
+++ b/media/libstagefright/codec2/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ C2.cpp \
+
+LOCAL_C_INCLUDES += \
+ $(TOP)/frameworks/av/media/libstagefright/codec2/include \
+ $(TOP)/frameworks/native/include/media/hardware \
+
+LOCAL_MODULE:= libstagefright_codec2
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+
+include $(BUILD_SHARED_LIBRARY)
+
+################################################################################
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codec2/C2.cpp b/media/libstagefright/codec2/C2.cpp
new file mode 100644
index 0000000..a51b073
--- /dev/null
+++ b/media/libstagefright/codec2/C2.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <C2.h>
+#include <C2Buffer.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2Work.h>
+
+namespace android {
+
+/**
+ * There is nothing here yet. This library is built to see what symbols and methods get
+ * defined as part of the API include files.
+ *
+ * Going forward, the Codec2 library will contain utility methods that are useful for
+ * Codec2 clients.
+ */
+
+} // namespace android
+
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
new file mode 100644
index 0000000..7d00a03
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 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 C2_H_
+#define C2_H_
+
+#include <string>
+#include <vector>
+#include <list>
+
+#ifdef __ANDROID__
+
+#include <utils/Errors.h> // for status_t
+#include <utils/Timers.h> // for nsecs_t
+
+namespace android {
+
+#else
+
+#include <errno.h>
+typedef int64_t nsecs_t;
+
+enum {
+ GRALLOC_USAGE_SW_READ_OFTEN,
+ GRALLOC_USAGE_RENDERSCRIPT,
+ GRALLOC_USAGE_HW_TEXTURE,
+ GRALLOC_USAGE_HW_COMPOSER,
+ GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ GRALLOC_USAGE_PROTECTED,
+ GRALLOC_USAGE_SW_WRITE_OFTEN,
+ GRALLOC_USAGE_HW_RENDER,
+};
+
+#endif
+
+/** \mainpage Codec2
+ *
+ * Codec2 is a frame-based data processing API used by android.
+ *
+ * The framework accesses components via the \ref API.
+ */
+
+/** \ingroup API
+ *
+ * The Codec2 API defines the operation of data processing components and their interaction with
+ * the rest of the system.
+ *
+ * Coding Conventions
+ *
+ * Mitigating Binary Compatibility.
+ *
+ * While full binary compatibility is not a goal of the API (due to our use of STL), we try to
+ * mitigate binary breaks by adhering to the following conventions:
+ *
+ * - at most one vtable with placeholder virtual methods
+ * - all optional/placeholder virtual methods returning a status_t, with C2_NOT_IMPLEMENTED not
+ * requiring any update to input/output arguments.
+ * - limiting symbol export of inline methods
+ * - use of pimpl (or shared-pimpl)
+ *
+ * Naming
+ *
+ * - all classes and types prefix with C2
+ * - classes for internal use prefix with _C2
+ * - enum values in global namespace prefix with C2_ all caps
+ * - enum values inside classes have no C2_ prefix as class already has it
+ * - supporting two kinds of enum naming: all-caps and kCamelCase
+ * \todo revisit kCamelCase for param-type
+ *
+ * Aspects
+ *
+ * Aspects define certain common behavior across a group of objects.
+ * - classes whose name matches _C2.*Aspect
+ * - only protected constructors
+ * - no desctructor and copiable
+ * - all methods are inline or static (this is opposite of the interface paradigm where all methods
+ * are virtual, which would not work due to the at most one vtable rule.)
+ * - only private variables (this prevents subclasses interfering with the aspects.)
+ */
+
+/// \defgroup types Common Types
+/// @{
+
+/**
+ * C2String: basic string implementation
+ */
+typedef std::string C2String;
+typedef const char *C2StringLiteral;
+
+/**
+ * C2Error: status codes used.
+ */
+typedef int32_t C2Error;
+enum {
+#ifndef __ANDROID__
+ OK = 0,
+ BAD_VALUE = -EINVAL,
+ BAD_INDEX = -EOVERFLOW,
+ UNKNOWN_TRANSACTION = -EBADMSG,
+ ALREADY_EXISTS = -EEXIST,
+ NAME_NOT_FOUND = -ENOENT,
+ INVALID_OPERATION = -ENOSYS,
+ NO_MEMORY = -ENOMEM,
+ PERMISSION_DENIED = -EPERM,
+ TIMED_OUT = -ETIMEDOUT,
+ UNKNOWN_ERROR = -EINVAL,
+#endif
+
+ C2_OK = OK, ///< operation completed successfully
+
+ // bad input
+ C2_BAD_VALUE = BAD_VALUE, ///< argument has invalid value (user error)
+ C2_BAD_INDEX = BAD_INDEX, ///< argument uses invalid index (user error)
+ C2_UNSUPPORTED = UNKNOWN_TRANSACTION, ///< argument/index is value but not supported \todo is this really BAD_INDEX/VALUE?
+
+ // bad sequencing of events
+ C2_DUPLICATE = ALREADY_EXISTS, ///< object already exists
+ C2_NOT_FOUND = NAME_NOT_FOUND, ///< object not found
+ C2_BAD_STATE = INVALID_OPERATION, ///< operation is not permitted in the current state
+
+ // bad environment
+ C2_NO_MEMORY = NO_MEMORY, ///< not enough memory to complete operation
+ C2_NO_PERMISSION = PERMISSION_DENIED, ///< missing permission to complete operation
+ C2_TIMED_OUT = TIMED_OUT, ///< operation did not complete within timeout
+
+ // bad versioning
+ C2_NOT_IMPLEMENTED = UNKNOWN_TRANSACTION, ///< operation is not implemented (optional only) \todo for now reuse error code
+
+ // unknown fatal
+ C2_CORRUPTED = UNKNOWN_ERROR, ///< some unexpected error prevented the operation
+};
+
+/// @}
+
+/// \defgroup utils Utilities
+/// @{
+
+#define C2_DO_NOT_COPY(type, args...) \
+ type args& operator=(const type args&) = delete; \
+ type(const type args&) = delete; \
+
+#define C2_PURE __attribute__((pure))
+#define C2_CONST __attribute__((const))
+#define C2_HIDE __attribute__((visibility("hidden")))
+#define C2_INTERNAL __attribute__((internal_linkage))
+
+#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
+ inline bool operator!=(const type &other) { return !(*this == other); } \
+ inline bool operator<=(const type &other) { return (*this == other) || (*this < other); } \
+ inline bool operator>=(const type &other) { return !(*this < other); } \
+ inline bool operator>(const type &other) { return !(*this < other) && !(*this == other); }
+
+#define DEFINE_FIELD_BASED_COMPARISON_OPERATORS(type, field) \
+ inline bool operator<(const type &other) const { return field < other.field; } \
+ inline bool operator==(const type &other) const { return field == other.field; } \
+ DEFINE_OTHER_COMPARISON_OPERATORS(type)
+
+/// \cond INTERNAL
+
+/// \defgroup utils_internal
+/// @{
+
+template<typename... T> struct c2_types;
+
+/** specialization for a single type */
+template<typename T>
+struct c2_types<T> {
+ typedef typename std::decay<T>::type wide_type;
+ typedef wide_type narrow_type;
+ typedef wide_type mintype;
+};
+
+/** specialization for two types */
+template<typename T, typename U>
+struct c2_types<T, U> {
+ static_assert(std::is_floating_point<T>::value == std::is_floating_point<U>::value,
+ "mixing floating point and non-floating point types is disallowed");
+ static_assert(std::is_signed<T>::value == std::is_signed<U>::value,
+ "mixing signed and unsigned types is disallowed");
+
+ typedef typename std::decay<
+ decltype(true ? std::declval<T>() : std::declval<U>())>::type wide_type;
+ typedef typename std::decay<
+ typename std::conditional<sizeof(T) < sizeof(U), T, U>::type>::type narrow_type;
+ typedef typename std::conditional<
+ std::is_signed<T>::value, wide_type, narrow_type>::type mintype;
+};
+
+/// @}
+
+/// \endcond
+
+/**
+ * Type support utility class. Only supports similar classes, such as:
+ * - all floating point
+ * - all unsigned/all signed
+ * - all pointer
+ */
+template<typename T, typename U, typename... V>
+struct c2_types<T, U, V...> {
+ /** Common type that accommodates all template parameter types. */
+ typedef typename c2_types<typename c2_types<T, U>::wide_type, V...>::wide_type wide_type;
+ /** Narrowest type of the template parameter types. */
+ typedef typename c2_types<typename c2_types<T, U>::narrow_type, V...>::narrow_type narrow_type;
+ /** Type that accommodates the minimum value for any input for the template parameter types. */
+ typedef typename c2_types<typename c2_types<T, U>::mintype, V...>::mintype mintype;
+};
+
+/**
+ * \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::wide_type c2_max(const T a, const U b) {
+ typedef typename c2_types<T, U>::wide_type wide_type;
+ return ({ wide_type a_(a), b_(b); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ * Finds the maximum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::max where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the largest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::wide_type c2_max(const T a, const U b, const V ... c) {
+ typedef typename c2_types<T, U, V...>::wide_type wide_type;
+ return ({ wide_type a_(a), b_(c2_max(b, c...)); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ * \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::mintype c2_min(const T a, const U b) {
+ typedef typename c2_types<T, U>::wide_type wide_type;
+ return ({
+ wide_type a_(a), b_(b);
+ static_cast<typename c2_types<T, U>::mintype>(a_ < b_ ? a_ : b_);
+ });
+}
+
+/**
+ * Finds the minimum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::min where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the smallest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::mintype c2_min(const T a, const U b, const V ... c) {
+ typedef typename c2_types<U, V...>::mintype rest_type;
+ typedef typename c2_types<T, rest_type>::wide_type wide_type;
+ return ({
+ wide_type a_(a), b_(c2_min(b, c...));
+ static_cast<typename c2_types<T, rest_type>::mintype>(a_ < b_ ? a_ : b_);
+ });
+}
+
+/// @}
+
+#ifdef __ANDROID__
+} // namespace android
+#endif
+
+#endif // C2_H_
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
new file mode 100644
index 0000000..9f6b487
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -0,0 +1,1777 @@
+/*
+ * Copyright (C) 2016 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 C2BUFFER_H_
+#define C2BUFFER_H_
+
+#include <C2.h>
+#include <C2Param.h> // for C2Info
+
+#include <list>
+#include <memory>
+
+typedef int C2Fence;
+
+#ifdef __ANDROID__
+
+// #include <system/window.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h> // TODO: remove
+
+typedef native_handle_t C2Handle;
+
+#else
+
+typedef void* C2Handle;
+
+#endif
+
+namespace android {
+
+/// \defgroup buffer Buffers
+/// @{
+
+/// \defgroup buffer_sync Synchronization
+/// @{
+
+/**
+ * Synchronization is accomplished using event and fence objects.
+ *
+ * These are cross-process extensions of promise/future infrastructure.
+ * Events are analogous to std::promise<void>, whereas fences are to std::shared_future<void>.
+ *
+ * Fences and events are shareable/copyable.
+ *
+ * Fences are used in two scenarios, and all copied instances refer to the same event.
+ * \todo do events need to be copyable or should they be unique?
+ *
+ * acquire sync fence object: signaled when it is safe for the component or client to access
+ * (the contents of) an object.
+ *
+ * release sync fence object: \todo
+ *
+ * Fences can be backed by hardware. Hardware fences are guaranteed to signal NO MATTER WHAT within
+ * a short (platform specific) amount of time; this guarantee is usually less than 15 msecs.
+ */
+
+/**
+ * Fence object used by components and the framework.
+ *
+ * Implements the waiting for an event, analogous to a 'future'.
+ *
+ * To be implemented by vendors if using HW fences.
+ */
+class C2Fence {
+public:
+ /**
+ * Waits for a fence to be signaled with a timeout.
+ *
+ * \todo a mechanism to cancel a wait - for now the only way to do this is to abandon the
+ * event, but fences are shared so canceling a wait will cancel all waits.
+ *
+ * \param timeoutNs the maximum time to wait in nsecs
+ *
+ * \retval C2_OK the fence has been signaled
+ * \retval C2_TIMED_OUT the fence has not been signaled within the timeout
+ * \retval C2_BAD_STATE the fence has been abandoned without being signaled (it will never
+ * be signaled)
+ * \retval C2_NO_PERMISSION no permission to wait for the fence (unexpected - system)
+ * \retval C2_CORRUPTED some unknown error prevented waiting for the fence (unexpected)
+ */
+ C2Error wait(nsecs_t timeoutNs);
+
+ /**
+ * Used to check if this fence is valid (if there is a chance for it to be signaled.)
+ * A fence becomes invalid if the controling event is destroyed without it signaling the fence.
+ *
+ * \return whether this fence is valid
+ */
+ bool valid() const;
+
+ /**
+ * Used to check if this fence has been signaled (is ready).
+ *
+ * \return whether this fence has been signaled
+ */
+ bool ready() const;
+
+ /**
+ * Returns a file descriptor that can be used to wait for this fence in a select system call.
+ * \note The returned file descriptor, if valid, must be closed by the caller.
+ *
+ * This can be used in e.g. poll() system calls. This file becomes readable (POLLIN) when the
+ * fence is signaled, and bad (POLLERR) if the fence is abandoned.
+ *
+ * \return a file descriptor representing this fence (with ownership), or -1 if the fence
+ * has already been signaled (\todo or abandoned).
+ *
+ * \todo this must be compatible with fences used by gralloc
+ */
+ int fd() const;
+
+ /**
+ * Returns whether this fence is a hardware-backed fence.
+ * \return whether this is a hardware fence
+ */
+ bool isHW() const;
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Event object used by components and the framework.
+ *
+ * Implements the signaling of an event, analogous to a 'promise'.
+ *
+ * Hardware backed events do not go through this object, and must be exposed directly as fences
+ * by vendors.
+ */
+class C2Event {
+public:
+ /**
+ * Returns a fence for this event.
+ */
+ C2Fence fence() const;
+
+ /**
+ * Signals (all) associated fence(s).
+ * This has no effect no effect if the event was already signaled or abandoned.
+ *
+ * \retval C2_OK the fence(s) were successfully signaled
+ * \retval C2_BAD_STATE the fence(s) have already been abandoned or merged (caller error)
+ * \retval C2_ALREADY_EXISTS the fence(s) have already been signaled (caller error)
+ * \retval C2_NO_PERMISSION no permission to signal the fence (unexpected - system)
+ * \retval C2_CORRUPTED some unknown error prevented signaling the fence(s) (unexpected)
+ */
+ C2Error fire();
+
+ /**
+ * Trigger this event from the merging of the supplied fences. This means that it will be
+ * abandoned if any of these fences have been abandoned, and it will be fired if all of these
+ * fences have been signaled.
+ *
+ * \retval C2_OK the merging was successfully done
+ * \retval C2_NO_MEMORY not enough memory to perform the merging
+ * \retval C2_ALREADY_EXISTS the fence have already been merged (caller error)
+ * \retval C2_BAD_STATE the fence have already been signaled or abandoned (caller error)
+ * \retval C2_NO_PERMISSION no permission to merge the fence (unexpected - system)
+ * \retval C2_CORRUPTED some unknown error prevented merging the fence(s) (unexpected)
+ */
+ C2Error merge(std::vector<C2Fence> fences);
+
+ /**
+ * Abandons the event and any associated fence(s).
+ * \note Call this to explicitly abandon an event before it is destructed to avoid a warning.
+ *
+ * This has no effect no effect if the event was already signaled or abandoned.
+ *
+ * \retval C2_OK the fence(s) were successfully signaled
+ * \retval C2_BAD_STATE the fence(s) have already been signaled or merged (caller error)
+ * \retval C2_ALREADY_EXISTS the fence(s) have already been abandoned (caller error)
+ * \retval C2_NO_PERMISSION no permission to abandon the fence (unexpected - system)
+ * \retval C2_CORRUPTED some unknown error prevented signaling the fence(s) (unexpected)
+ */
+ C2Error abandon();
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/// \addtogroup buf_internal Internal
+/// @{
+
+/**
+ * Interface for objects that encapsulate an updatable error value.
+ */
+struct _C2InnateError {
+ inline C2Error error() const { return mError; }
+
+protected:
+ _C2InnateError(C2Error error) : mError(error) { }
+
+ C2Error mError; // this error is updatable by the object
+};
+
+/// @}
+
+/**
+ * This is a utility template for objects protected by an acquire fence, so that errors during
+ * acquiring the object are propagated to the object itself.
+ */
+template<typename T>
+class C2Acquirable : public C2Fence {
+public:
+ /**
+ * Acquires the object protected by an acquire fence. Any errors during the mapping will be
+ * passed to the object.
+ *
+ * \return acquired object potentially invalidated if waiting for the fence failed.
+ */
+ T get();
+
+protected:
+ C2Acquirable(C2Error error, C2Fence fence, T t) : C2Fence(fence), mInitialError(error), mT(t) { }
+
+private:
+ C2Error mInitialError;
+ T mT; // TODO: move instead of copy
+};
+
+/// @}
+
+/// \defgroup linear Linear Data Blocks
+/// @{
+
+/**************************************************************************************************
+ LINEAR ASPECTS, BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/**
+ * Common aspect for all objects that have a linear capacity.
+ */
+class _C2LinearCapacityAspect {
+/// \name Linear capacity interface
+/// @{
+public:
+ inline uint32_t capacity() const { return mCapacity; }
+
+protected:
+
+#if UINTPTR_MAX == 0xffffffff
+ static_assert(sizeof(size_t) == sizeof(uint32_t), "size_t is too big");
+#else
+ static_assert(sizeof(size_t) > sizeof(uint32_t), "size_t is too small");
+ // explicitly disable construction from size_t
+ inline explicit _C2LinearCapacityAspect(size_t capacity) = delete;
+#endif
+
+ inline explicit _C2LinearCapacityAspect(uint32_t capacity)
+ : mCapacity(capacity) { }
+
+ inline explicit _C2LinearCapacityAspect(const _C2LinearCapacityAspect *parent)
+ : mCapacity(parent == nullptr ? 0 : parent->capacity()) { }
+
+private:
+ const uint32_t mCapacity;
+/// @}
+};
+
+/**
+ * Aspect for objects that have a linear range.
+ *
+ * This class is copiable.
+ */
+class _C2LinearRangeAspect : public _C2LinearCapacityAspect {
+/// \name Linear range interface
+/// @{
+public:
+ inline uint32_t offset() const { return mOffset; }
+ inline uint32_t size() const { return mSize; }
+
+protected:
+ inline explicit _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent)
+ : _C2LinearCapacityAspect(parent),
+ mOffset(0),
+ mSize(capacity()) { }
+
+ inline _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
+ : _C2LinearCapacityAspect(parent),
+ mOffset(c2_min(offset, capacity())),
+ mSize(c2_min(size, capacity() - mOffset)) { }
+
+ // subsection of the two [offset, offset + size] ranges
+ inline _C2LinearRangeAspect(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
+ : _C2LinearCapacityAspect(parent == nullptr ? 0 : parent->capacity()),
+ mOffset(c2_min(c2_max(offset, parent == nullptr ? 0 : parent->offset()), capacity())),
+ mSize(c2_min(c2_min(size, parent == nullptr ? 0 : parent->size()), capacity() - mOffset)) { }
+
+private:
+ friend class _C2EditableLinearRange;
+ // invariants 0 <= mOffset <= mOffset + mSize <= capacity()
+ uint32_t mOffset;
+ uint32_t mSize;
+/// @}
+};
+
+/**
+ * Aspect for objects that have an editable linear range.
+ *
+ * This class is copiable.
+ */
+class _C2EditableLinearRange : public _C2LinearRangeAspect {
+protected:
+ inline explicit _C2EditableLinearRange(const _C2LinearCapacityAspect *parent)
+ : _C2LinearRangeAspect(parent) { }
+
+ inline _C2EditableLinearRange(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
+ : _C2LinearRangeAspect(parent, offset, size) { }
+
+ // subsection of the two [offset, offset + size] ranges
+ inline _C2EditableLinearRange(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
+ : _C2LinearRangeAspect(parent, offset, size) { }
+
+/// \name Editable linear range interface
+/// @{
+
+ /**
+ * Sets the offset to |offset|, while trying to keep the end of the buffer unchanged (e.g.
+ * size will grow if offset is decreased, and may shrink if offset is increased.) Returns
+ * true if successful, which is equivalent to if 0 <= |offset| <= capacity().
+ *
+ * Note: setting offset and size will yield different result depending on the order of the
+ * operations. Always set offset first to ensure proper size.
+ */
+ inline bool setOffset(uint32_t offset) {
+ if (offset > capacity()) {
+ return false;
+ }
+
+ if (offset > mOffset + mSize) {
+ mSize = 0;
+ } else {
+ mSize = mOffset + mSize - offset;
+ }
+ mOffset = offset;
+ return true;
+ }
+ /**
+ * Sets the size to |size|. Returns true if successful, which is equivalent to
+ * if 0 <= |size| <= capacity() - offset().
+ *
+ * Note: setting offset and size will yield different result depending on the order of the
+ * operations. Always set offset first to ensure proper size.
+ */
+ inline bool setSize(uint32_t size) {
+ if (size > capacity() - mOffset) {
+ return false;
+ } else {
+ mSize = size;
+ return true;
+ }
+ }
+ /**
+ * Sets the offset to |offset| with best effort. Same as setOffset() except that offset will
+ * be clamped to the buffer capacity.
+ *
+ * Note: setting offset and size (even using best effort) will yield different result depending
+ * on the order of the operations. Always set offset first to ensure proper size.
+ */
+ inline void setOffset_be(uint32_t offset) {
+ if (offset > capacity()) {
+ offset = capacity();
+ }
+ if (offset > mOffset + mSize) {
+ mSize = 0;
+ } else {
+ mSize = mOffset + mSize - offset;
+ }
+ mOffset = offset;
+ }
+ /**
+ * Sets the size to |size| with best effort. Same as setSize() except that the selected region
+ * will be clamped to the buffer capacity (e.g. size is clamped to [0, capacity() - offset()]).
+ *
+ * Note: setting offset and size (even using best effort) will yield different result depending
+ * on the order of the operations. Always set offset first to ensure proper size.
+ */
+ inline void setSize_be(uint32_t size) {
+ mSize = std::min(size, capacity() - mOffset);
+ }
+/// @}
+};
+
+// ================================================================================================
+// BLOCKS
+// ================================================================================================
+
+/**
+ * Blocks are sections of allocations. They can be either 1D or 2D.
+ */
+
+class C2LinearAllocation;
+
+class C2Block1D : public _C2LinearRangeAspect {
+public:
+ const C2Handle *handle() const;
+
+protected:
+ C2Block1D(std::shared_ptr<C2LinearAllocation> alloc);
+ C2Block1D(std::shared_ptr<C2LinearAllocation> alloc, size_t offset, size_t size);
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Read view provides read-only access for a linear memory segment.
+ *
+ * This class is copiable.
+ */
+class C2ReadView : public _C2LinearCapacityAspect {
+public:
+ /**
+ * \return pointer to the start of the block or nullptr on error.
+ */
+ const uint8_t *data();
+
+ /**
+ * Returns a portion of this view.
+ *
+ * \param offset the start offset of the portion. \note This is clamped to the capacity of this
+ * view.
+ * \param size the size of the portion. \note This is clamped to the remaining data from offset.
+ *
+ * \return a read view containing a portion of this view
+ */
+ C2ReadView subView(size_t offset, size_t size) const;
+
+ /**
+ * \return error during the creation/mapping of this view.
+ */
+ C2Error error();
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Write view provides read/write access for a linear memory segment.
+ *
+ * This class is copiable. \todo movable only?
+ */
+class C2WriteView : public _C2EditableLinearRange {
+public:
+ /**
+ * Start of the block.
+ *
+ * \return pointer to the start of the block or nullptr on error.
+ */
+ uint8_t *base();
+
+ /**
+ * \return pointer to the block at the current offset or nullptr on error.
+ */
+ uint8_t *data();
+
+ /**
+ * \return error during the creation/mapping of this view.
+ */
+ C2Error error();
+
+private:
+ class Impl;
+ /// \todo should this be unique_ptr to make this movable only - to avoid inconsistent regions
+ /// between copies.
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) linear block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstLinearBlock : public C2Block1D {
+public:
+ /**
+ * Maps this block into memory and returns a read view for it.
+ *
+ * \return a read view for this block.
+ */
+ C2Acquirable<C2ReadView> map() const;
+
+ /**
+ * Returns a portion of this block.
+ *
+ * \param offset the start offset of the portion. \note This is clamped to the capacity of this
+ * block.
+ * \param size the size of the portion. \note This is clamped to the remaining data from offset.
+ *
+ * \return a constant linear block containing a portion of this block
+ */
+ C2ConstLinearBlock subBlock(size_t offset, size_t size) const;
+
+ /**
+ * Returns the acquire fence for this block.
+ *
+ * \return a fence that must be waited on before reading the block.
+ */
+ C2Fence fence() const { return mFence; }
+
+private:
+ C2Fence mFence;
+};
+
+/**
+ * Linear block is a writeable 1D block. Once written, it can be shared in whole or in parts with
+ * consumers/readers as read-only const linear block(s).
+ */
+class C2LinearBlock : public C2Block1D {
+public:
+ /**
+ * Maps this block into memory and returns a write view for it.
+ *
+ * \return a write view for this block.
+ */
+ C2Acquirable<C2WriteView> map();
+
+ /**
+ * Creates a read-only const linear block for a portion of this block; optionally protected
+ * by an acquire fence. There are two ways to use this:
+ *
+ * 1) share ready block after writing data into the block. In this case no fence shall be
+ * supplied, and the block shall not be modified after calling this method.
+ * 2) share block metadata before actually (finishing) writing the data into the block. In
+ * this case a fence must be supplied that will be triggered when the data is written.
+ * The block shall be modified only until firing the event for the fence.
+ */
+ C2ConstLinearBlock share(size_t offset, size_t size, C2Fence fence);
+};
+
+/// @}
+
+/**************************************************************************************************
+ CIRCULAR BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/// \defgroup circular Circular buffer support
+/// @{
+
+/**
+ * Circular blocks can be used to share data between a writer and a reader (and/or other consumers)-
+ * in a memory-efficient way by reusing a section of memory. Circular blocks are a bit more complex
+ * than single reader/single writer schemes to facilitate block-based consuming of data.
+ *
+ * They can operate in two modes:
+ *
+ * 1) one writer that creates blocks to be consumed (this model can be used by components)
+ *
+ * 2) one writer that writes continuously, and one reader that can creates blocks to be consumed
+ * by further recipients (this model is used by the framework, and cannot be used by components.)
+ *
+ * Circular blocks have four segments with running pointers:
+ * - reserved: data reserved and available for the writer
+ * - committed: data committed by the writer and available to the reader (if present)
+ * - used: data used by consumers (if present)
+ * - available: unused data available to be reserved
+ */
+class C2CircularBlock : public C2Block1D {
+ // TODO: add methods
+
+private:
+ size_t mReserved __unused; // end of reserved section
+ size_t mCommitted __unused; // end of committed section
+ size_t mUsed __unused; // end of used section
+ size_t mFree __unused; // end of free section
+};
+
+class _C2CircularBlockSegment : public _C2LinearCapacityAspect {
+public:
+ /**
+ * Returns the available size for this segment.
+ *
+ * \return currently available size for this segment
+ */
+ size_t available() const;
+
+ /**
+ * Reserve some space for this segment from its current start.
+ *
+ * \param size desired space in bytes
+ * \param fence a pointer to an acquire fence. If non-null, the reservation is asynchronous and
+ * a fence will be stored here that will be signaled when the reservation is
+ * complete. If null, the reservation is synchronous.
+ *
+ * \retval C2_OK the space was successfully reserved
+ * \retval C2_NO_MEMORY the space requested cannot be reserved
+ * \retval C2_TIMED_OUT the reservation timed out \todo when?
+ * \retval C2_CORRUPTED some unknown error prevented reserving space. (unexpected)
+ */
+ C2Error reserve(size_t size, C2Fence *fence /* nullable */);
+
+ /**
+ * Abandons a portion of this segment. This will move to the beginning of this segment.
+ *
+ * \note This methods is only allowed if this segment is producing blocks.
+ *
+ * \param size number of bytes to abandon
+ *
+ * \retval C2_OK the data was successfully abandoned
+ * \retval C2_TIMED_OUT the operation timed out (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented abandoning the data (unexpected)
+ */
+ C2Error abandon(size_t size);
+
+ /**
+ * Share a portion as block(s) with consumers (these are moved to the used section).
+ *
+ * \note This methods is only allowed if this segment is producing blocks.
+ * \note Share does not move the beginning of the segment. (\todo add abandon/offset?)
+ *
+ * \param size number of bytes to share
+ * \param fence fence to be used for the section
+ * \param blocks list where the blocks of the section are appended to
+ *
+ * \retval C2_OK the portion was successfully shared
+ * \retval C2_NO_MEMORY not enough memory to share the portion
+ * \retval C2_TIMED_OUT the operation timed out (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented sharing the data (unexpected)
+ */
+ C2Error share(size_t size, C2Fence fence, std::list<C2ConstLinearBlock> &blocks);
+
+ /**
+ * Returns the beginning offset of this segment from the start of this circular block.
+ *
+ * @return beginning offset
+ */
+ size_t begin();
+
+ /**
+ * Returns the end offset of this segment from the start of this circular block.
+ *
+ * @return end offset
+ */
+ size_t end();
+};
+
+/**
+ * A circular write-view is a dynamic mapped view for a segment of a circular block. Care must be
+ * taken when using this view so that only the section owned by the segment is modified.
+ */
+class C2CircularWriteView : public _C2LinearCapacityAspect {
+public:
+ /**
+ * Start of the circular block.
+ * \note the segment does not own this pointer.
+ *
+ * \return pointer to the start of the circular block or nullptr on error.
+ */
+ uint8_t *base();
+
+ /**
+ * \return error during the creation/mapping of this view.
+ */
+ C2Error error();
+};
+
+/**
+ * The writer of a circular buffer.
+ *
+ * Can commit data to a reader (not supported for components) OR share data blocks directly with a
+ * consumer.
+ *
+ * If a component supports outputting data into circular buffers, it must allocate a circular
+ * block and use a circular writer.
+ */
+class C2CircularWriter : public _C2CircularBlockSegment {
+public:
+ /**
+ * Commits a portion of this segment to the next segment. This moves the beginning of the
+ * segment.
+ *
+ * \param size number of bytes to commit to the next segment
+ * \param fence fence used for the commit (the fence must signal before the data is committed)
+ */
+ C2Error commit(size_t size, C2Fence fence);
+
+ /**
+ * Maps this block into memory and returns a write view for it.
+ *
+ * \return a write view for this block.
+ */
+ C2Acquirable<C2CircularWriteView> map();
+};
+
+/// @}
+
+/// \defgroup graphic Graphic Data Blocks
+/// @{
+
+/**
+ * Interface for objects that have a width and height (planar capacity).
+ */
+class _C2PlanarCapacityAspect {
+/// \name Planar capacity interface
+/// @{
+public:
+ inline uint32_t width() const { return mWidth; }
+ inline uint32_t height() const { return mHeight; }
+
+protected:
+ inline _C2PlanarCapacityAspect(uint32_t width, uint32_t height)
+ : mWidth(width), mHeight(height) { }
+
+ inline _C2PlanarCapacityAspect(const _C2PlanarCapacityAspect *parent)
+ : mWidth(parent == nullptr ? 0 : parent->width()),
+ mHeight(parent == nullptr ? 0 : parent->height()) { }
+
+private:
+ const uint32_t mWidth;
+ const uint32_t mHeight;
+/// @}
+};
+
+/**
+ * C2Rect: rectangle type with non-negative coordinates.
+ *
+ * \note This struct has public fields without getters/setters. All methods are inline.
+ */
+struct C2Rect {
+// public:
+ uint32_t mLeft;
+ uint32_t mTop;
+ uint32_t mWidth;
+ uint32_t mHeight;
+
+ inline C2Rect(uint32_t width, uint32_t height)
+ : C2Rect(width, height, 0, 0) { }
+
+ inline C2Rect(uint32_t width, uint32_t height, uint32_t left, uint32_t top)
+ : mLeft(left), mTop(top), mWidth(width), mHeight(height) { }
+
+ // utility methods
+
+ inline bool isEmpty() const {
+ return mWidth == 0 || mHeight == 0;
+ }
+
+ inline bool isValid() const {
+ return mLeft <= ~mWidth && mTop <= ~mHeight;
+ }
+
+ inline operator bool() const {
+ return isValid() && !isEmpty();
+ }
+
+ inline bool operator!() const {
+ return !bool(*this);
+ }
+
+ inline bool contains(const C2Rect &other) const {
+ if (!isValid() || !other.isValid()) {
+ return false;
+ } else if (other.isEmpty()) {
+ return true;
+ } else {
+ return mLeft <= other.mLeft && mTop <= other.mTop
+ && mLeft + mWidth >= other.mLeft + other.mWidth
+ && mTop + mHeight >= other.mTop + other.mHeight;
+ }
+ }
+
+ inline bool operator==(const C2Rect &other) const {
+ if (!isValid()) {
+ return !other.isValid();
+ } else if (isEmpty()) {
+ return other.isEmpty();
+ } else {
+ return mLeft == other.mLeft && mTop == other.mTop
+ && mWidth == other.mWidth && mHeight == other.mHeight;
+ }
+ }
+
+ inline bool operator!=(const C2Rect &other) const {
+ return !operator==(other);
+ }
+
+ inline bool operator>=(const C2Rect &other) const {
+ return contains(other);
+ }
+
+ inline bool operator>(const C2Rect &other) const {
+ return contains(other) && !operator==(other);
+ }
+
+ inline bool operator<=(const C2Rect &other) const {
+ return other.contains(*this);
+ }
+
+ inline bool operator<(const C2Rect &other) const {
+ return other.contains(*this) && !operator==(other);
+ }
+};
+
+/**
+ * C2PlaneInfo: information on the layout of flexible planes.
+ *
+ * Public fields without getters/setters.
+ */
+struct C2PlaneInfo {
+// public:
+ enum Channel : uint32_t {
+ Y,
+ R,
+ G,
+ B,
+ A,
+ Cr,
+ Cb,
+ } mChannel;
+
+ int32_t mColInc; // column increment in bytes. may be negative
+ int32_t mRowInc; // row increment in bytes. may be negative
+ uint32_t mHorizSubsampling; // subsampling compared to width
+ uint32_t mVertSubsampling; // subsampling compared to height
+
+ uint32_t mBitDepth;
+ uint32_t mAllocatedDepth;
+
+ inline ssize_t minOffset(uint32_t width, uint32_t height) {
+ ssize_t offs = 0;
+ if (width > 0 && mColInc < 0) {
+ offs += mColInc * (ssize_t)(width - 1);
+ }
+ if (height > 0 && mRowInc < 0) {
+ offs += mRowInc * (ssize_t)(height - 1);
+ }
+ return offs;
+ }
+
+ inline ssize_t maxOffset(uint32_t width, uint32_t height, uint32_t allocatedDepth) {
+ ssize_t offs = (allocatedDepth + 7) >> 3;
+ if (width > 0 && mColInc > 0) {
+ offs += mColInc * (ssize_t)(width - 1);
+ }
+ if (height > 0 && mRowInc > 0) {
+ offs += mRowInc * (ssize_t)(height - 1);
+ }
+ return offs;
+ }
+};
+
+struct C2PlaneLayout {
+public:
+ enum Type : uint32_t {
+ MEDIA_IMAGE_TYPE_UNKNOWN = 0,
+ MEDIA_IMAGE_TYPE_YUV = 0x100,
+ MEDIA_IMAGE_TYPE_YUVA,
+ MEDIA_IMAGE_TYPE_RGB,
+ MEDIA_IMAGE_TYPE_RGBA,
+ };
+
+ Type mType;
+ uint32_t mNumPlanes; // number of planes
+
+ enum PlaneIndex : uint32_t {
+ Y = 0,
+ U = 1,
+ V = 2,
+ R = 0,
+ G = 1,
+ B = 2,
+ A = 3,
+ MAX_NUM_PLANES = 4,
+ };
+
+ C2PlaneInfo mPlanes[MAX_NUM_PLANES];
+};
+
+/**
+ * Aspect for objects that have a planar section (crop rectangle).
+ *
+ * This class is copiable.
+ */
+class _C2PlanarSection : public _C2PlanarCapacityAspect {
+/// \name Planar section interface
+/// @{
+public:
+ // crop can be an empty rect, does not have to line up with subsampling
+ // NOTE: we do not support floating-point crop
+ inline const C2Rect crop() { return mCrop; }
+
+ /**
+ * Sets crop to crop intersected with [(0,0) .. (width, height)]
+ */
+ inline void setCrop_be(const C2Rect &crop);
+
+ /**
+ * If crop is within the dimensions of this object, it sets crop to it.
+ *
+ * \return true iff crop is within the dimensions of this object
+ */
+ inline bool setCrop(const C2Rect &crop);
+
+private:
+ C2Rect mCrop;
+/// @}
+};
+
+class C2Block2D : public _C2PlanarSection {
+public:
+ const C2Handle *handle() const;
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Graphic view provides read or read-write access for a graphic block.
+ *
+ * This class is copiable.
+ *
+ * \note Due to the subsampling of graphic buffers, a read view must still contain a crop rectangle
+ * to ensure subsampling is followed. This results in nearly identical interface between read and
+ * write views, so C2GraphicView can encompass both of them.
+ */
+class C2GraphicView : public _C2PlanarSection {
+public:
+ /**
+ * \return pointer to the start of the block or nullptr on error.
+ */
+ const uint8_t *data() const;
+
+ /**
+ * \return pointer to the start of the block or nullptr on error.
+ */
+ uint8_t *data();
+
+ /**
+ * Returns a section of this view.
+ *
+ * \param rect the dimension of the section. \note This is clamped to the crop of this view.
+ *
+ * \return a read view containing the requested section of this view
+ */
+ const C2GraphicView subView(const C2Rect &rect) const;
+ C2GraphicView subView(const C2Rect &rect);
+
+ /**
+ * \return error during the creation/mapping of this view.
+ */
+ C2Error error() const;
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) graphic block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstGraphicBlock : public C2Block2D {
+public:
+ /**
+ * Maps this block into memory and returns a read view for it.
+ *
+ * \return a read view for this block.
+ */
+ C2Acquirable<const C2GraphicView> map() const;
+
+ /**
+ * Returns a section of this block.
+ *
+ * \param rect the coordinates of the section. \note This is clamped to the crop rectangle of
+ * this block.
+ *
+ * \return a constant graphic block containing a portion of this block
+ */
+ C2ConstGraphicBlock subBlock(const C2Rect &rect) const;
+
+ /**
+ * Returns the acquire fence for this block.
+ *
+ * \return a fence that must be waited on before reading the block.
+ */
+ C2Fence fence() const { return mFence; }
+
+private:
+ C2Fence mFence;
+};
+
+/**
+ * Graphic block is a writeable 2D block. Once written, it can be shared in whole or in part with
+ * consumers/readers as read-only const graphic block.
+ */
+class C2GraphicBlock : public C2Block2D {
+public:
+ /**
+ * Maps this block into memory and returns a write view for it.
+ *
+ * \return a write view for this block.
+ */
+ C2Acquirable<C2GraphicView> map();
+
+ /**
+ * Creates a read-only const linear block for a portion of this block; optionally protected
+ * by an acquire fence. There are two ways to use this:
+ *
+ * 1) share ready block after writing data into the block. In this case no fence shall be
+ * supplied, and the block shall not be modified after calling this method.
+ * 2) share block metadata before actually (finishing) writing the data into the block. In
+ * this case a fence must be supplied that will be triggered when the data is written.
+ * The block shall be modified only until firing the event for the fence.
+ */
+ C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence);
+};
+
+/// @}
+
+/// \defgroup buffer_onj Buffer objects
+/// @{
+
+// ================================================================================================
+// BUFFERS
+// ================================================================================================
+
+/// \todo: Do we still need this?
+///
+// There are 2 kinds of buffers: linear or graphic. Linear buffers can contain a single block, or
+// a list of blocks (LINEAR_CHUNKS). Support for list of blocks is optional, and can allow consuming
+// data from circular buffers or scattered data sources without extra memcpy. Currently, list of
+// graphic blocks is not supported.
+
+class C2LinearBuffer; // read-write buffer
+class C2GraphicBuffer; // read-write buffer
+class C2LinearChunksBuffer;
+
+/**
+ * C2BufferData: the main, non-meta data of a buffer. A buffer can contain either linear blocks
+ * or graphic blocks, and can contain either a single block or multiple blocks. This is determined
+ * by its type.
+ */
+class C2BufferData {
+public:
+ /**
+ * The type of buffer data.
+ */
+ enum Type : uint32_t {
+ LINEAR, ///< the buffer contains a single linear block
+ LINEAR_CHUNKS, ///< the buffer contains one or more linear blocks
+ GRAPHIC, ///< the buffer contains a single graphic block
+ GRAPHIC_CHUNKS, ///< the buffer contains one of more graphic blocks
+ };
+
+ /**
+ * Gets the type of this buffer (data).
+ * \return the type of this buffer data.
+ */
+ Type type() const;
+
+ /**
+ * Gets the linear blocks of this buffer.
+ * \return a constant list of const linear blocks of this buffer.
+ * \retval empty list if this buffer does not contain linear block(s).
+ */
+ const std::list<C2ConstLinearBlock> linearBlocks() const;
+
+ /**
+ * Gets the graphic blocks of this buffer.
+ * \return a constant list of const graphic blocks of this buffer.
+ * \retval empty list if this buffer does not contain graphic block(s).
+ */
+ const std::list<C2ConstGraphicBlock> graphicBlocks() const;
+
+private:
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+
+protected:
+ // no public constructor
+ // C2BufferData(const std::shared_ptr<const Impl> &impl) : mImpl(impl) {}
+};
+
+/**
+ * C2Buffer: buffer base class. These are always used as shared_ptrs. Though the underlying buffer
+ * objects (native buffers, ion buffers, or dmabufs) are reference-counted by the system,
+ * C2Buffers hold only a single reference.
+ *
+ * These objects cannot be used on the stack.
+ */
+class C2Buffer {
+public:
+ /**
+ * Gets the buffer's data.
+ *
+ * \return the buffer's data.
+ */
+ const C2BufferData data() const;
+
+ /**
+ * These will still work if used in onDeathNotify.
+ */
+#if 0
+ inline std::shared_ptr<C2LinearBuffer> asLinearBuffer() const {
+ return mType == LINEAR ? std::shared_ptr::reinterpret_cast<C2LinearBuffer>(this) : nullptr;
+ }
+
+ inline std::shared_ptr<C2GraphicBuffer> asGraphicBuffer() const {
+ return mType == GRAPHIC ? std::shared_ptr::reinterpret_cast<C2GraphicBuffer>(this) : nullptr;
+ }
+
+ inline std::shared_ptr<C2CircularBuffer> asCircularBuffer() const {
+ return mType == CIRCULAR ? std::shared_ptr::reinterpret_cast<C2CircularBuffer>(this) : nullptr;
+ }
+#endif
+
+ ///@name Pre-destroy notification handling
+ ///@{
+
+ /**
+ * Register for notification just prior to the destruction of this object.
+ */
+ typedef void (*OnDestroyNotify) (const C2Buffer *buf, void *arg);
+
+ /**
+ * Registers for a pre-destroy notification. This is called just prior to the destruction of
+ * this buffer (when this buffer is no longer valid.)
+ *
+ * \param onDestroyNotify the notification callback
+ * \param arg an arbitrary parameter passed to the callback
+ *
+ * \retval C2_OK the registration was successful.
+ * \retval C2_DUPLICATE a notification was already registered for this callback and argument
+ * \retval C2_NO_MEMORY not enough memory to register for this callback
+ * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+ */
+ C2Error registerOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+
+ /**
+ * Unregisters a previously registered pre-destroy notification.
+ *
+ * \param onDestroyNotify the notification callback
+ * \param arg an arbitrary parameter passed to the callback
+ *
+ * \retval C2_OK the unregistration was successful.
+ * \retval C2_NOT_FOUND the notification was not found
+ * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+ */
+ C2Error unregisterOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+
+ ///@}
+
+ virtual ~C2Buffer() = default;
+
+ ///@name Buffer-specific arbitrary metadata handling
+ ///@{
+
+ /**
+ * Gets the list of metadata associated with this buffer.
+ *
+ * \return a constant list of info objects associated with this buffer.
+ */
+ const std::list<std::shared_ptr<const C2Info>> infos() const;
+
+ /**
+ * Attaches (or updates) an (existing) metadata for this buffer.
+ * If the metadata is stream specific, the stream information will be reset.
+ *
+ * \param info Metadata to update
+ *
+ * \retval C2_OK the metadata was successfully attached/updated.
+ * \retval C2_NO_MEMORY not enough memory to attach the metadata (this return value is not
+ * used if the same kind of metadata is already attached to the buffer).
+ */
+ C2Error setInfo(const std::shared_ptr<C2Info> &info);
+
+ /**
+ * Checks if there is a certain type of metadata attached to this buffer.
+ *
+ * \param index the parameter type of the metadata
+ *
+ * \return true iff there is a metadata with the parameter type attached to this buffer.
+ */
+ bool hasInfo(C2Param::Type index) const;
+ std::shared_ptr<C2Info> removeInfo(C2Param::Type index) const;
+ ///@}
+
+protected:
+ // no public constructor
+ inline C2Buffer() = default;
+
+private:
+// Type _mType;
+};
+
+/**
+ * An extension of C2Info objects that can contain arbitrary buffer data.
+ *
+ * \note This object is not describable and contains opaque data.
+ */
+class C2InfoBuffer {
+public:
+ /**
+ * Gets the index of this info object.
+ *
+ * \return the parameter index.
+ */
+ const C2Param::Index index() const;
+
+ /**
+ * Gets the buffer's data.
+ *
+ * \return the buffer's data.
+ */
+ const C2BufferData data() const;
+};
+
+/// @}
+
+/**************************************************************************************************
+ ALLOCATIONS
+**************************************************************************************************/
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These are used by the allocators to select optimal memory type/pool and
+ * buffer layout.
+ *
+ * \note This struct has public fields without getters/setters. All methods are inline.
+ */
+struct C2MemoryUsage {
+// public:
+ // TODO: match these to gralloc1.h
+ enum Consumer : uint64_t {
+ kSoftwareRead = GRALLOC_USAGE_SW_READ_OFTEN,
+ kRenderScriptRead = GRALLOC_USAGE_RENDERSCRIPT,
+ kTextureRead = GRALLOC_USAGE_HW_TEXTURE,
+ kHardwareComposer = GRALLOC_USAGE_HW_COMPOSER,
+ kHardwareEncoder = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ kProtectedRead = GRALLOC_USAGE_PROTECTED,
+ };
+
+ enum Producer : uint64_t {
+ kSoftwareWrite = GRALLOC_USAGE_SW_WRITE_OFTEN,
+ kRenderScriptWrite = GRALLOC_USAGE_RENDERSCRIPT,
+ kTextureWrite = GRALLOC_USAGE_HW_RENDER,
+ kCompositionTarget = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+ kHardwareDecoder = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ kProtectedWrite = GRALLOC_USAGE_PROTECTED,
+ };
+
+ uint64_t mConsumer; // e.g. input
+ uint64_t mProducer; // e.g. output
+};
+
+/**
+ * \ingroup linear allocator
+ * 1D allocation interface.
+ */
+class C2LinearAllocation : public _C2LinearCapacityAspect {
+public:
+ /**
+ * Maps a portion of an allocation starting from |offset| with |size| into local process memory.
+ * Stores the starting address into |addr|, or NULL if the operation was unsuccessful.
+ * |fenceFd| is a file descriptor referring to an acquire sync fence object. If it is already
+ * safe to access the buffer contents, then -1.
+ *
+ * \param offset starting position of the portion to be mapped (this does not have to
+ * be page aligned)
+ * \param size size of the portion to be mapped (this does not have to be page
+ * aligned)
+ * \param usage the desired usage. \todo this must be kSoftwareRead and/or
+ * kSoftwareWrite.
+ * \param fenceFd a pointer to a file descriptor if an async mapping is requested. If
+ * not-null, and acquire fence FD will be stored here on success, or -1
+ * on failure. If null, the mapping will be synchronous.
+ * \param addr a pointer to where the starting address of the mapped portion will be
+ * stored. On failure, nullptr will be stored here.
+ *
+ * \todo Only one portion can be mapped at the same time - this is true for gralloc, but there
+ * is no need for this for 1D buffers.
+ * \todo Do we need to support sync operation as we could just wait for the fence?
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_NO_PERMISSION no permission to map the portion
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_NO_MEMORY not enough memory to complete the operation
+ * \retval C2_BAD_VALUE the parameters (offset/size) are invalid or outside the allocation, or
+ * the usage flags are invalid (caller error)
+ * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+ */
+ virtual C2Error map(
+ size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd /* nullable */,
+ void **addr /* nonnull */) = 0;
+
+ /**
+ * Unmaps a portion of an allocation at |addr| with |size|. These must be parameters previously
+ * passed to |map|; otherwise, this operation is a no-op.
+ *
+ * \param addr starting address of the mapped region
+ * \param size size of the mapped region
+ * \param fenceFd a pointer to a file descriptor if an async unmapping is requested. If
+ * not-null, a release fence FD will be stored here on success, or -1
+ * on failure. This fence signals when the original allocation contains
+ * any changes that happened to the mapped region. If null, the unmapping
+ * will be synchronous.
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_BAD_VALUE the parameters (addr/size) do not correspond to previously mapped
+ * regions (caller error)
+ * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+ * \retval C2_NO_PERMISSION no permission to unmap the portion (unexpected - system)
+ */
+ virtual C2Error unmap(void *addr, size_t size, int *fenceFd /* nullable */) = 0;
+
+ /**
+ * Returns true if this is a valid allocation.
+ *
+ * \todo remove?
+ */
+ virtual bool isValid() const = 0;
+
+ /**
+ * Returns a pointer to the allocation handle.
+ */
+ virtual const C2Handle *handle() const = 0;
+
+ /**
+ * Returns true if this is the same allocation as |other|.
+ */
+ virtual bool equals(const std::shared_ptr<C2LinearAllocation> &other) const = 0;
+
+protected:
+ // \todo should we limit allocation directly?
+ C2LinearAllocation(size_t capacity) : _C2LinearCapacityAspect(c2_min(capacity, UINT32_MAX)) {}
+ virtual ~C2LinearAllocation() = default;
+};
+
+/**
+ * \ingroup graphic allocator
+ * 2D allocation interface.
+ */
+class C2GraphicAllocation : public _C2PlanarCapacityAspect {
+public:
+ /**
+ * Maps a rectangular section (as defined by |rect|) of a 2D allocation into local process
+ * memory for flexible access. On success, it fills out |layout| with the plane specifications
+ * and fills the |addr| array with pointers to the first byte of the top-left pixel of each
+ * plane used. Otherwise, it leaves |layout| and |addr| untouched. |fenceFd| is a file
+ * descriptor referring to an acquire sync fence object. If it is already safe to access the
+ * buffer contents, then -1.
+ *
+ * \note Only one portion of the graphic allocation can be mapped at the same time. (This is
+ * from gralloc1 limitation.)
+ *
+ * \param rect section to be mapped (this does not have to be aligned)
+ * \param usage the desired usage. \todo this must be kSoftwareRead and/or
+ * kSoftwareWrite.
+ * \param fenceFd a pointer to a file descriptor if an async mapping is requested. If
+ * not-null, and acquire fence FD will be stored here on success, or -1
+ * on failure. If null, the mapping will be synchronous.
+ * \param layout a pointer to where the mapped planes' descriptors will be
+ * stored. On failure, nullptr will be stored here.
+ *
+ * \todo Do we need to support sync operation as we could just wait for the fence?
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_NO_PERMISSION no permission to map the section
+ * \retval C2_ALREADY_EXISTS there is already a mapped region (caller error)
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_NO_MEMORY not enough memory to complete the operation
+ * \retval C2_BAD_VALUE the parameters (rect) are invalid or outside the allocation, or the
+ * usage flags are invalid (caller error)
+ * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+
+ */
+ virtual C2Error map(
+ C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+ // TODO: return <addr, size> buffers with plane sizes
+ C2PlaneLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) = 0;
+
+ /**
+ * Unmaps the last mapped rectangular section.
+ *
+ * \param fenceFd a pointer to a file descriptor if an async unmapping is requested. If
+ * not-null, a release fence FD will be stored here on success, or -1
+ * on failure. This fence signals when the original allocation contains
+ * any changes that happened to the mapped section. If null, the unmapping
+ * will be synchronous.
+ *
+ * \retval C2_OK the operation was successful
+ * \retval C2_TIMED_OUT the operation timed out
+ * \retval C2_NOT_FOUND there is no mapped region (caller error)
+ * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+ * \retval C2_NO_PERMISSION no permission to unmap the section (unexpected - system)
+ */
+ virtual C2Error unmap(C2Fence *fenceFd /* nullable */) = 0;
+
+ /**
+ * Returns true if this is a valid allocation.
+ *
+ * \todo remove?
+ */
+ virtual bool isValid() const = 0;
+
+ /**
+ * Returns a pointer to the allocation handle.
+ */
+ virtual const C2Handle *handle() const = 0;
+
+ /**
+ * Returns true if this is the same allocation as |other|.
+ */
+ virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) = 0;
+
+protected:
+ virtual ~C2GraphicAllocation();
+};
+
+/**
+ * Allocators are used by the framework to allocate memory (allocations) for buffers. They can
+ * support either 1D or 2D allocations.
+ *
+ * \note In theory they could support both, but in practice, we will use only one or the other.
+ *
+ * Never constructed on stack.
+ *
+ * Allocators are provided by vendors.
+ */
+class C2Allocator {
+public:
+ /**
+ * Allocates a 1D allocation of given |capacity| and |usage|. If successful, the allocation is
+ * stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+ *
+ * \param capacity the size of requested allocation (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param usage the memory usage info for the requested allocation. \note that the
+ * returned allocation may be later used/mapped with different usage.
+ * The allocator should layout the buffer to be optimized for this usage,
+ * but must support any usage. One exception: protected buffers can
+ * only be used in a protected scenario.
+ * \param allocation pointer to where the allocation shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete the allocation
+ * \retval C2_TIMED_OUT the allocation timed out
+ * \retval C2_NO_PERMISSION no permission to complete the allocation
+ * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support 1D allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error allocateLinearBuffer(
+ uint32_t capacity __unused, C2MemoryUsage usage __unused,
+ std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+ *allocation = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+ /**
+ * (Re)creates a 1D allocation from a native |handle|. If successful, the allocation is stored
+ * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+ *
+ * \param handle the handle for the existing allocation
+ * \param allocation pointer to where the allocation shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was recreated successfully
+ * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+ * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+ * \retval C2_NO_PERMISSION no permission to recreate the allocation
+ * \retval C2_BAD_VALUE invalid handle (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support 1D allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error recreateLinearBuffer(
+ const C2Handle *handle __unused,
+ std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+ *allocation = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+ /**
+ * Allocates a 2D allocation of given |width|, |height|, |format| and |usage|. If successful,
+ * the allocation is stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+ *
+ * \param width the width of requested allocation (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param height the height of requested allocation (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param format the pixel format of requested allocation. This could be a vendor
+ * specific format.
+ * \param usage the memory usage info for the requested allocation. \note that the
+ * returned allocation may be later used/mapped with different usage.
+ * The allocator should layout the buffer to be optimized for this usage,
+ * but must support any usage. One exception: protected buffers can
+ * only be used in a protected scenario.
+ * \param allocation pointer to where the allocation shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete the allocation
+ * \retval C2_TIMED_OUT the allocation timed out
+ * \retval C2_NO_PERMISSION no permission to complete the allocation
+ * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support 2D allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error allocateGraphicBuffer(
+ uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+ C2MemoryUsage usage __unused,
+ std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+ *allocation = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+ /**
+ * (Re)creates a 2D allocation from a native handle. If successful, the allocation is stored
+ * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+ *
+ * \param handle the handle for the existing allocation
+ * \param allocation pointer to where the allocation shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was recreated successfully
+ * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+ * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+ * \retval C2_NO_PERMISSION no permission to recreate the allocation
+ * \retval C2_BAD_VALUE invalid handle (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support 2D allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during recreation (unexpected)
+ */
+ virtual C2Error recreateGraphicBuffer(
+ const C2Handle *handle __unused,
+ std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+ *allocation = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+protected:
+ C2Allocator() = default;
+
+ virtual ~C2Allocator() = default;
+};
+
+/**
+ * Block allocators are used by components to allocate memory for output buffers. They can
+ * support either linear (1D), circular (1D) or graphic (2D) allocations.
+ *
+ * Never constructed on stack.
+ *
+ * Block allocators are provided by the framework.
+ */
+class C2BlockAllocator {
+public:
+ /**
+ * Allocates a linear writeable block of given |capacity| and |usage|. If successful, the
+ * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+ *
+ * \param capacity the size of requested block.
+ * \param usage the memory usage info for the requested allocation. \note that the
+ * returned allocation may be later used/mapped with different usage.
+ * The allocator shall lay out the buffer to be optimized for this usage,
+ * but must support any usage. One exception: protected buffers can
+ * only be used in a protected scenario.
+ * \param block pointer to where the allocated block shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete the allocation
+ * \retval C2_TIMED_OUT the allocation timed out
+ * \retval C2_NO_PERMISSION no permission to complete the allocation
+ * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support linear allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error allocateLinearBlock(
+ uint32_t capacity __unused, C2MemoryUsage usage __unused,
+ std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+ *block = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+ /**
+ * Allocates a circular writeable block of given |capacity| and |usage|. If successful, the
+ * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+ *
+ * \param capacity the size of requested circular block. (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param usage the memory usage info for the requested allocation. \note that the
+ * returned allocation may be later used/mapped with different usage.
+ * The allocator shall lay out the buffer to be optimized for this usage,
+ * but must support any usage. One exception: protected buffers can
+ * only be used in a protected scenario.
+ * \param block pointer to where the allocated block shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete the allocation
+ * \retval C2_TIMED_OUT the allocation timed out
+ * \retval C2_NO_PERMISSION no permission to complete the allocation
+ * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support circular allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error allocateCircularBlock(
+ uint32_t capacity __unused, C2MemoryUsage usage __unused,
+ std::shared_ptr<C2CircularBlock> *block /* nonnull */) {
+ *block = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+ /**
+ * Allocates a 2D graphic block of given |width|, |height|, |format| and |usage|. If successful,
+ * the allocation is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+ *
+ * \param width the width of requested allocation (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param height the height of requested allocation (the allocation could be slightly
+ * larger, e.g. to account for any system-required alignment)
+ * \param format the pixel format of requested allocation. This could be a vendor
+ * specific format.
+ * \param usage the memory usage info for the requested allocation. \note that the
+ * returned allocation may be later used/mapped with different usage.
+ * The allocator should layout the buffer to be optimized for this usage,
+ * but must support any usage. One exception: protected buffers can
+ * only be used in a protected scenario.
+ * \param block pointer to where the allocation shall be stored on success. nullptr
+ * will be stored here on failure
+ *
+ * \retval C2_OK the allocation was successful
+ * \retval C2_NO_MEMORY not enough memory to complete the allocation
+ * \retval C2_TIMED_OUT the allocation timed out
+ * \retval C2_NO_PERMISSION no permission to complete the allocation
+ * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller error)
+ * \retval C2_UNSUPPORTED this allocator does not support 2D allocations
+ * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+ */
+ virtual C2Error allocateGraphicBlock(
+ uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+ C2MemoryUsage usage __unused,
+ std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+ *block = nullptr;
+ return C2_UNSUPPORTED;
+ }
+
+protected:
+ C2BlockAllocator() = default;
+
+ virtual ~C2BlockAllocator() = default;
+};
+
+/// @}
+
+/// \cond INTERNAL
+
+/// \todo These are no longer used
+
+/// \addtogroup linear
+/// @{
+
+/** \deprecated */
+class C2LinearBuffer
+ : public C2Buffer, public _C2LinearRangeAspect,
+ public std::enable_shared_from_this<C2LinearBuffer> {
+public:
+ /** \todo what is this? */
+ const C2Handle *handle() const;
+
+protected:
+ inline C2LinearBuffer(const C2ConstLinearBlock &block);
+
+private:
+ class Impl;
+ Impl *mImpl;
+};
+
+class C2ReadCursor;
+
+class C2WriteCursor {
+public:
+ uint32_t remaining() const; // remaining data to be read
+ void commit(); // commits the current position. discard data before current position
+ void reset() const; // resets position to the last committed position
+ // slices off at most |size| bytes, and moves cursor ahead by the number of bytes
+ // sliced off.
+ C2ReadCursor slice(uint32_t size) const;
+ // slices off at most |size| bytes, and moves cursor ahead by the number of bytes
+ // sliced off.
+ C2WriteCursor reserve(uint32_t size);
+ // bool read(T&);
+ // bool write(T&);
+ C2Fence waitForSpace(uint32_t size);
+};
+
+/// @}
+
+/// \addtogroup graphic
+/// @{
+
+struct C2ColorSpace {
+//public:
+ enum Standard {
+ BT601,
+ BT709,
+ BT2020,
+ // TODO
+ };
+
+ enum Range {
+ LIMITED,
+ FULL,
+ // TODO
+ };
+
+ enum TransferFunction {
+ BT709Transfer,
+ BT2020Transfer,
+ HybridLogGamma2,
+ HybridLogGamma4,
+ // TODO
+ };
+};
+
+/** \deprecated */
+class C2GraphicBuffer : public C2Buffer {
+public:
+ // constant attributes
+ inline uint32_t width() const { return mWidth; }
+ inline uint32_t height() const { return mHeight; }
+ inline uint32_t format() const { return mFormat; }
+ inline const C2MemoryUsage usage() const { return mUsage; }
+
+ // modifiable attributes
+
+
+ virtual const C2ColorSpace colorSpace() const = 0;
+ // best effort
+ virtual void setColorSpace_be(const C2ColorSpace &colorSpace) = 0;
+ virtual bool setColorSpace(const C2ColorSpace &colorSpace) = 0;
+
+ const C2Handle *handle() const;
+
+protected:
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mFormat;
+ C2MemoryUsage mUsage;
+
+ class Impl;
+ Impl *mImpl;
+};
+
+/// @}
+
+/// \endcond
+
+/// @}
+
+} // namespace android
+
+#endif // C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
new file mode 100644
index 0000000..1ee9302
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2016 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 C2COMPONENT_H_
+
+#define C2COMPONENT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+#include <functional>
+
+#include <C2Param.h>
+#include <C2Work.h>
+
+namespace android {
+
+/// \defgroup components Components
+/// @{
+
+class C2Component;
+
+class C2ComponentListener {
+public:
+ virtual void onWorkDone(std::weak_ptr<C2Component> component,
+ std::vector<std::unique_ptr<C2Work>> workItems) = 0;
+
+ virtual void onTripped(std::weak_ptr<C2Component> component,
+ std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;
+
+ virtual void onError(std::weak_ptr<C2Component> component,
+ uint32_t errorCode) = 0;
+
+ // virtual void onTunnelReleased(<from>, <to>) = 0;
+
+ // virtual void onComponentReleased(<id>) = 0;
+
+protected:
+ virtual ~C2ComponentListener();
+};
+
+/**
+ * Component interface object. This object contains all of the configuration of a potential or
+ * actual component. It can be created and used independently of an actual C2Component instance to
+ * query support and parameters for various component settings and configurations for a potential
+ * component. Actual components also expose this interface.
+ */
+
+class C2ComponentInterface {
+public:
+ // ALWAYS AVAILABLE METHODS
+ // =============================================================================================
+
+ /**
+ * Returns the name of this component or component interface object.
+ * This is a unique name for this component or component interface 'class'; however, multiple
+ * instances of this component SHALL have the same name.
+ *
+ * This method MUST be supported in any state. This call does not change the state nor the
+ * internal states of the component.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \return the name of this component or component interface object.
+ * \retval an empty string if there was not enough memory to allocate the actual name.
+ */
+ virtual C2String getName() const = 0;
+
+ /**
+ * Returns a unique ID for this component or interface object.
+ * This ID is used as work targets, unique work IDs, and when configuring tunneling.
+ *
+ * This method MUST be supported in any state. This call does not change the state nor the
+ * internal states of the component.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \return a unique node ID for this component or component interface instance.
+ */
+ virtual node_id getId() const = 0;
+
+ /**
+ * Queries a set of parameters from the component or interface object.
+ * Querying is performed at best effort: the component SHALL query all supported parameters and
+ * skip unsupported ones, or heap allocated parameters that could not be allocated. Any errors
+ * are communicated in the return value. Additionally, preallocated (e.g. stack) parameters that
+ * could not be queried are invalidated. Parameters to be allocated on the heap are omitted from
+ * the result.
+ *
+ * \note Parameter values do not depend on the order of query.
+ *
+ * \todo This method cannot be used to query info-buffers. Is that a problem?
+ *
+ * This method MUST be supported in any state. This call does not change the state nor the
+ * internal states of the component.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \param[in,out] stackParams a list of params queried. These are initialized specific to each
+ * setting; e.g. size and index are set and rest of the members are
+ * cleared.
+ * \note Flexible settings that are of incorrect size will be invalidated.
+ * \param[in] heapParamIndices a vector of param indices for params to be queried and returned on the
+ * heap. These parameters will be returned in heapParams. Unsupported param
+ * indices will be ignored.
+ * \param[out] heapParams a list of params where to which the supported heap parameters will be
+ * appended in the order they appear in heapParamIndices.
+ *
+ * \retval C2_OK all parameters could be queried
+ * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+ * supported
+ * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+ * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+ * (unexpected)
+ */
+ virtual status_t query_nb(
+ const std::vector<C2Param* const> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
+
+ /**
+ * Sets a set of parameters for the component or interface object.
+ * Tuning is performed at best effort: the component SHALL update all supported configuration at
+ * best effort (unless configured otherwise) and skip unsupported ones. Any errors are
+ * communicated in the return value and in |failures|.
+ *
+ * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+ * update may allow some subsequent parameter update.
+ *
+ * This method MUST be supported in any state.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \param[in,out] params a list of parameter updates. These will be updated to the actual
+ * parameter values after the updates (this is because tuning is performed
+ * at best effort).
+ * \todo params that could not be updated are not marked here, so are
+ * confusing - are they "existing" values or intended to be configured
+ * values?
+ * \param[out] failures a list of parameter failures
+ *
+ * \retval C2_OK all parameters could be updated successfully
+ * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+ * parameters were not supported
+ * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+ * they contained unsupported values. These are returned in |failures|.
+ * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+ * they contained unsupported values, but could not allocate a failure
+ * object for them.
+ * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+ * (unexpected)
+ */
+ virtual status_t config_nb(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+ /**
+ * Atomically sets a set of parameters for the component or interface object.
+ *
+ * \note This method is used mainly for reserving resources for a component.
+ *
+ * The component SHALL update all supported configuration at
+ * best effort(TBD) (unless configured otherwise) and skip unsupported ones. Any errors are
+ * communicated in the return value and in |failures|.
+ *
+ * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+ * update may allow some subsequent parameter update.
+ *
+ * This method MUST be supported in any state.
+ *
+ * This method may be momentarily blocking, but MUST return within 5ms.
+ *
+ * \param params[in,out] a list of parameter updates. These will be updated to the actual
+ * parameter values after the updates (this is because tuning is performed
+ * at best effort).
+ * \todo params that could not be updated are not marked here, so are
+ * confusing - are they "existing" values or intended to be configured
+ * values?
+ * \param failures[out] a list of parameter failures
+ *
+ * \retval C2_OK all parameters could be updated successfully
+ * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+ * parameters were not supported
+ * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+ * they contained unsupported values. These are returned in |failures|.
+ * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+ * they contained unsupported values, but could not allocate a failure
+ * object for them.
+ * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+ * (unexpected)
+ */
+ virtual status_t commit_sm(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+ // TUNNELING
+ // =============================================================================================
+
+ /**
+ * Creates a tunnel from this component to the target component.
+ *
+ * If the component is successfully created, subsequent work items queued may include a
+ * tunneled path between these components.
+ *
+ * This method MUST be supported in any state.
+ *
+ * This method may be momentarily blocking, but MUST return within 5ms.
+ *
+ * \retval C2_OK the tunnel was successfully created
+ * \retval C2_BAD_INDEX the target component does not exist
+ * \retval C2_ALREADY_EXIST the tunnel already exists
+ * \retval C2_UNSUPPORTED the tunnel is not supported
+ *
+ * \retval C2_TIMED_OUT could not create the tunnel within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the creation of the tunnel (unexpected)
+ */
+ virtual status_t createTunnel_sm(node_id targetComponent) = 0;
+
+ /**
+ * Releases a tunnel from this component to the target component.
+ *
+ * The release of a tunnel is delayed while there are pending work items for the tunnel.
+ * After releasing a tunnel, subsequent work items queued MUST NOT include a tunneled
+ * path between these components.
+ *
+ * This method MUST be supported in any state.
+ *
+ * This method may be momentarily blocking, but MUST return within 5ms.
+ *
+ * \retval C2_OK the tunnel was marked for release successfully
+ * \retval C2_BAD_INDEX the target component does not exist
+ * \retval C2_NOT_FOUND the tunnel does not exist
+ *
+ * \retval C2_TIMED_OUT could not mark the tunnel for release within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the release of the tunnel (unexpected)
+ */
+ virtual status_t releaseTunnel_sm(node_id targetComponent) = 0;
+
+
+ // REFLECTION MECHANISM (USED FOR EXTENSION)
+ // =============================================================================================
+
+ /**
+ * Returns the parameter reflector.
+ *
+ * This is used to describe parameter fields.
+ *
+ * \return a shared parameter reflector object.
+ */
+ virtual std::shared_ptr<C2ParamReflector> getParamReflector() const = 0;
+
+ /**
+ * Returns the set of supported parameters.
+ *
+ * \param[out] params a vector of supported parameters will be appended to this vector.
+ *
+ * \retval C2_OK the operation completed successfully.
+ * \retval C2_NO_MEMORY not enough memory to complete this method.
+ */
+ virtual status_t getSupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const = 0;
+
+ /**
+ *
+ * \todo should this take a list considering that setting some fields may further limit other
+ * fields in the same list?
+ */
+ virtual status_t getSupportedValues(
+ const std::vector<const C2ParamField> fields,
+ std::vector<C2FieldSupportedValues>* const values) const = 0;
+
+ virtual ~C2ComponentInterface() = default;
+};
+
+class C2Component {
+public:
+ // METHODS AVAILABLE WHEN RUNNING
+ // =============================================================================================
+
+ /**
+ * Queues up work for the component.
+ *
+ * This method MUST be supported in running (including tripped) states.
+ *
+ * This method MUST be "non-blocking" and return within 1ms
+ *
+ * It is acceptable for this method to return OK and return an error value using the
+ * onWorkDone() callback.
+ *
+ * \retval C2_OK the work was successfully queued
+ * \retval C2_BAD_INDEX some component(s) in the work do(es) not exist
+ * \retval C2_UNSUPPORTED the components are not tunneled
+ *
+ * \retval C2_NO_MEMORY not enough memory to queue the work
+ * \retval C2_CORRUPTED some unknown error prevented queuing the work (unexpected)
+ */
+ virtual status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) = 0;
+
+ /**
+ * Announces a work to be queued later for the component. This reserves a slot for the queue
+ * to ensure correct work ordering even if the work is queued later.
+ *
+ * This method MUST be supported in running (including tripped) states.
+ *
+ * This method MUST be "non-blocking" and return within 1 ms
+ *
+ * \retval C2_OK the work announcement has been successfully recorded
+ * \retval C2_BAD_INDEX some component(s) in the work outline do(es) not exist
+ * \retval C2_UNSUPPORTED the componentes are not tunneled
+ *
+ * \retval C2_NO_MEMORY not enough memory to record the work announcement
+ * \retval C2_CORRUPTED some unknown error prevented recording the announcement (unexpected)
+ *
+ * \todo Can this be rolled into queue_nb?
+ */
+ virtual status_t announce_nb(const std::vector<C2WorkOutline> &items) = 0;
+
+ /**
+ * Discards and abandons any pending work for the component, and optionally any component
+ * downstream.
+ *
+ * \todo define this: we could flush all work before last item queued for component across all
+ * components linked to this; flush only work items that are queued to this
+ * component
+ * \todo return work # of last flushed item; or all flushed (but not returned items)
+ * \todo we could make flush take a work item and flush all work before/after that item to allow
+ * TBD (slicing/seek?)
+ * \todo we could simply take a list of numbers and flush those... this is bad for decoders
+ * also, what would happen to fine grade references?
+ *
+ * This method MUST be supported in running (including tripped) states.
+ *
+ * This method may be momentarily blocking, but must return within 5ms.
+ *
+ * Work that could be immediately abandoned/discarded SHALL be returned in |flushedWork|; this
+ * can be done in an arbitrary order.
+ *
+ * Work that could not be abandoned or discarded immediately SHALL be marked to be
+ * discarded at the earliest opportunity, and SHALL be returned via the onWorkDone() callback.
+ *
+ * \param flushThrough flush work from this component and all components connected downstream
+ * from it via tunneling.
+ *
+ * \retval C2_OK the work announcement has been successfully recorded
+ * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+ */
+ virtual status_t flush_sm(bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) = 0;
+
+ /**
+ * Drains the component, and optionally downstream components
+ *
+ * \todo define this; we could place EOS to all upstream components, just this component, or
+ * all upstream and downstream component.
+ * \todo should EOS carry over to downstream components?
+ *
+ * Marks last work item as "end-of-stream", so component is notified not to wait for further
+ * work before it processes work already queued. This method is called to set the end-of-stream
+ * flag after work has been queued. Client can continue to queue further work immediately after
+ * this method returns.
+ *
+ * This method MUST be supported in running (including tripped) states.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * Work that is completed SHALL be returned via the onWorkDone() callback.
+ *
+ * \param drainThrough marks the last work item with a persistent "end-of-stream" marker that
+ * will drain downstream components.
+ *
+ * \todo this may confuse work-ordering downstream; could be an mode enum
+ *
+ * \retval C2_OK the work announcement has been successfully recorded
+ * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+ */
+ virtual status_t drain_nb(bool drainThrough) = 0;
+
+ // STATE CHANGE METHODS
+ // =============================================================================================
+
+ /**
+ * Starts the component.
+ *
+ * This method MUST be supported in stopped state.
+ *
+ * \todo This method MUST return within 500ms. Seems this should be able to return quickly, as
+ * there are no immediate guarantees. Though there are guarantees for responsiveness immediately
+ * after start returns.
+ *
+ * \todo Could we just start a ComponentInterface to get a Component?
+ *
+ * \retval C2_OK the work announcement has been successfully recorded
+ * \retval C2_NO_MEMORY not enough memory to start the component
+ * \retval C2_TIMED_OUT the component could not be started within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented starting the component (unexpected)
+ */
+ virtual status_t start() = 0;
+
+ /**
+ * Stops the component.
+ *
+ * This method MUST be supported in running (including tripped) state.
+ *
+ * This method MUST return withing 500ms.
+ *
+ * Upon this call, all pending work SHALL be abandoned.
+ *
+ * \todo should this return completed work, since client will just free it? Perhaps just to
+ * verify accounting.
+ *
+ * This does not alter any settings and tunings that may have resulted in a tripped state.
+ * (Is this material given the definition? Perhaps in case we want to start again.)
+ */
+ virtual status_t stop() = 0;
+
+ /**
+ * Resets the component.
+ *
+ * This method MUST be supported in running (including tripped) state.
+ *
+ * This method MUST be supported during any other call (\todo or just blocking ones?)
+ *
+ * This method MUST return withing 500ms.
+ *
+ * After this call returns all work is/must be abandoned, all references should be released.
+ *
+ * \todo should this return completed work, since client will just free it? Also, if it unblocks
+ * a stop, where should completed work be returned?
+ *
+ * This brings settings back to their default - "guaranteeing" no tripped space.
+ *
+ * \todo reclaim support - it seems that since ownership is passed, this will allow reclaiming stuff.
+ */
+ virtual void reset() = 0;
+
+ /**
+ * Releases the component.
+ *
+ * This method MUST be supported in any state. (\todo Or shall we force reset() first to bring
+ * to a known state?)
+ *
+ * This method MUST return withing 500ms.
+ *
+ * \todo should this return completed work, since client will just free it? Also, if it unblocks
+ * a stop, where should completed work be returned?
+ *
+ * TODO: does it matter if this call has a short time limit? Yes, as upon return all references
+ * shall be abandoned.
+ */
+ virtual void release() = 0;
+
+ /**
+ * Returns the interface for this component.
+ *
+ * \return the component interface
+ */
+ virtual std::shared_ptr<C2ComponentInterface> intf() = 0;
+
+protected:
+ virtual ~C2Component() = default;
+};
+
+class C2FrameInfoParser {
+public:
+ /**
+ * \return the content type supported by this info parser.
+ *
+ * \todo this may be redundant
+ */
+ virtual C2StringLiteral getType() const = 0;
+
+ /**
+ * \return a vector of supported parameter indices parsed by this info parser.
+ *
+ * \todo sticky vs. non-sticky params? this may be communicated by param-reflector.
+ */
+ virtual const std::vector<C2Param::Index> getParsedParams() const = 0;
+
+ /**
+ * Resets this info parser. This brings this parser to its initial state after creation.
+ *
+ * This method SHALL return within 5ms.
+ *
+ * \retval C2_OK the info parser was reset
+ * \retval C2_TIMED_OUT could not reset the parser within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the resetting of the parser (unexpected)
+ */
+ virtual status_t reset() { return C2_OK; }
+
+ virtual status_t parseFrame(C2BufferPack &frame);
+
+ virtual ~C2FrameInfoParser() = default;
+};
+
+struct C2ComponentInfo {
+ // TBD
+
+};
+
+class C2AllocatorStore {
+public:
+ // TBD
+
+ enum Type {
+ LINEAR, ///< basic linear allocator type
+ GRALLOC, ///< basic gralloc allocator type
+ };
+
+ /**
+ * Creates an allocator.
+ *
+ * \param type the type of allocator to create
+ * \param allocator shared pointer where the created allocator is stored. Cleared on failure
+ * and updated on success.
+ *
+ * \retval C2_OK the allocator was created successfully
+ * \retval C2_TIMED_OUT could not create the allocator within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the creation of the allocator (unexpected)
+ *
+ * \retval C2_NOT_FOUND no such allocator
+ * \retval C2_NO_MEMORY not enough memory to create the allocator
+ */
+ virtual status_t createAllocator(Type type, std::shared_ptr<C2Allocator>* const allocator) = 0;
+
+ virtual ~C2AllocatorStore() = default;
+};
+
+class C2ComponentStore {
+ /**
+ * Creates a component.
+ *
+ * This method SHALL return within 100ms.
+ *
+ * \param name name of the component to create
+ * \param component shared pointer where the created component is stored. Cleared on
+ * failure and updated on success.
+ *
+ * \retval C2_OK the component was created successfully
+ * \retval C2_TIMED_OUT could not create the component within the time limit (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the creation of the component (unexpected)
+ *
+ * \retval C2_NOT_FOUND no such component
+ * \retval C2_NO_MEMORY not enough memory to create the component
+ */
+ virtual status_t createComponent(C2String name, std::shared_ptr<C2Component>* const component);
+
+ /**
+ * Creates a component interface.
+ *
+ * This method SHALL return within 100ms.
+ *
+ * \param name name of the component interface to create
+ * \param interface shared pointer where the created interface is stored
+ *
+ * \retval C2_OK the component interface was created successfully
+ * \retval C2_TIMED_OUT could not create the component interface within the time limit
+ * (unexpected)
+ * \retval C2_CORRUPTED some unknown error prevented the creation of the component interface
+ * (unexpected)
+ *
+ * \retval C2_NOT_FOUND no such component interface
+ * \retval C2_NO_MEMORY not enough memory to create the component interface
+ *
+ * \todo Do we need an interface, or could this just be a component that is never started?
+ */
+ virtual status_t createInterface(C2String name, std::shared_ptr<C2ComponentInterface>* const interface);
+
+ /**
+ * Returns the list of components supported by this component store.
+ *
+ * This method SHALL return within 1ms.
+ *
+ * \retval vector of component information.
+ */
+ virtual std::vector<std::unique_ptr<const C2ComponentInfo>> getComponents();
+
+ // -------------------------------------- UTILITY METHODS --------------------------------------
+
+ // on-demand buffer layout conversion (swizzling)
+ virtual status_t copyBuffer(std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst);
+
+ // status_t selectPreferredColor(formats<A>, formats<B>);
+
+ // GLOBAL SETTINGS
+ // system-wide stride & slice-height (???)
+
+ /**
+ * Queries a set of system-wide parameters.
+ * Querying is performed at best effort: the store SHALL query all supported parameters and
+ * skip unsupported ones, or heap allocated parameters that could not be allocated. Any errors
+ * are communicated in the return value. Additionally, preallocated (e.g. stack) parameters that
+ * could not be queried are invalidated. Parameters to be allocated on the heap are omitted from
+ * the result.
+ *
+ * \note Parameter values do not depend on the order of query.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \param stackParams a list of params queried. These are initialized specific to each
+ * setting; e.g. size and index are set and rest of the members are
+ * cleared.
+ * NOTE: Flexible settings that are of incorrect size will be invalidated.
+ * \param heapParamIndices a vector of param indices for params to be queried and returned on the
+ * heap. These parameters will be returned in heapParams. Unsupported param
+ * indices will be ignored.
+ * \param heapParams a list of params where to which the supported heap parameters will be
+ * appended in the order they appear in heapParamIndices.
+ *
+ * \retval C2_OK all parameters could be queried
+ * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+ * supported
+ * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+ * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+ * (unexpected)
+ */
+ virtual status_t query_nb(
+ const std::vector<C2Param* const> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) = 0;
+
+ /**
+ * Sets a set of system-wide parameters.
+ *
+ * \note There are no settable system-wide parameters defined thus far, but may be added in the
+ * future.
+ *
+ * Tuning is performed at best effort: the store SHALL update all supported configuration at
+ * best effort (unless configured otherwise) and skip unsupported ones. Any errors are
+ * communicated in the return value and in |failures|.
+ *
+ * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+ * update may allow some subsequent parameter update.
+ *
+ * This method MUST be "non-blocking" and return within 1ms.
+ *
+ * \param params a list of parameter updates. These will be updated to the actual
+ * parameter values after the updates (this is because tuning is performed
+ * at best effort).
+ * \todo params that could not be updated are not marked here, so are
+ * confusing - are they "existing" values or intended to be configured
+ * values?
+ * \param failures a list of parameter failures
+ *
+ * \retval C2_OK all parameters could be updated successfully
+ * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+ * parameters were not supported
+ * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+ * they contained unsupported values. These are returned in |failures|.
+ * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+ * they contained unsupported values, but could not allocate a failure
+ * object for them.
+ * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+ * (unexpected)
+ */
+ virtual status_t config_nb(
+ const std::vector<C2Param* const> ¶ms,
+ std::list<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+ virtual ~C2ComponentStore() = default;
+};
+
+// ================================================================================================
+
+/// @}
+
+} // namespace android
+
+#endif // C2COMPONENT_H_
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
new file mode 100644
index 0000000..30e9193
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 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 C2CONFIG_H_
+#define C2CONFIG_H_
+
+#include <C2ParamDef.h>
+
+namespace android {
+
+/// \defgroup config Component configuration
+/// @{
+
+#ifndef DEFINE_C2_ENUM_VALUE_AUTO_HELPER
+#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...)
+#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...)
+#endif
+
+#define C2ENUM(name, type, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, NULL, __VA_ARGS__)
+
+#define C2ENUM_CUSTOM_PREFIX(name, type, prefix, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, __VA_ARGS__)
+
+#define C2ENUM_CUSTOM_NAMES(name, type, names, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, __VA_ARGS__)
+
+enum C2ParamIndexKind : uint32_t {
+ /// domain
+ kParamIndexDomain,
+
+ /// configuration descriptors
+ kParamIndexSupportedParams,
+ kParamIndexRequiredParams,
+ kParamIndexReadOnlyParams,
+ kParamIndexRequestedInfos,
+
+ /// latency
+ kParamIndexLatency,
+
+ // generic time behavior
+ kParamIndexTemporal,
+
+ /// port configuration
+ kParamIndexMime,
+ kParamIndexStreamCount,
+ kParamIndexFormat,
+
+ // video info
+
+ kParamIndexStructStart = 0x1,
+ kParamIndexVideoSize,
+ kParamIndexMaxVideoSizeHint,
+
+ kParamIndexParamStart = 0x800,
+};
+
+C2ENUM(C2DomainKind, int32_t,
+ C2DomainVideo,
+ C2DomainAudio,
+ C2DomainOther = C2DomainAudio + 1
+);
+
+// read-only
+
+typedef C2GlobalParam<C2Info, C2SimpleValueStruct<C2DomainKind>, kParamIndexDomain> C2ComponentDomainInfo;
+// typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexDomain> C2ComponentDomainInfo;
+//DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<C2DomainKind>, { C2FIELD(mValue, "value") });
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexSupportedParams> C2SupportedParamsInfo;
+
+/// \todo do we define it as a param?
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexRequiredParams> C2RequiredParamsInfo;
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexReadOnlyParams> C2ReadOnlyParamsInfo;
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexRequestedInfos> C2RequestedInfosInfo;
+
+// read-only
+//typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexRequestedInfos> C2RequestedInfosInfo;
+
+/// latency
+
+typedef C2PortParam<C2Info, C2Uint32Value, kParamIndexLatency> C2PortLatencyInfo;
+
+typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexLatency> C2ComponentLatencyInfo;
+
+/// \todo
+typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexTemporal> C2ComponentTemporalInfo;
+
+/// port configuration
+
+typedef C2PortParam<C2Tuning, C2StringValue, kParamIndexMime> C2PortMimeConfig;
+
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexStreamCount> C2PortStreamCountConfig;
+
+typedef C2StreamParam<C2Tuning, C2StringValue, kParamIndexMime> C2StreamMimeConfig;
+
+C2ENUM(C2FormatKind, uint32_t,
+ C2FormatCompressed,
+ C2FormatAudio = 1,
+ C2FormatVideo = 4,
+)
+
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexFormat> C2StreamFormatConfig;
+
+/*
+ Component description fields:
+
+// format (video/compressed/audio/other-do we need other?) per stream
+
+// likely some of these are exposed as separate settings:
+
+struct C2BaseTuning {
+ // latency characteristics
+ uint32_t latency;
+ bool temporal; // seems this only makes sense if latency is 1..., so this could be captured as latency = 0
+ uint32_t delay;
+
+ uint32_t numInputStreams; // RW? - or suggestion only: RO
+ uint32_t numOutputStreams; // RW
+ //
+ // refs characteristics (per stream?)
+ uint32_t maxInputRefs; // RO
+ uint32_t maxOutputRefs; // RO
+ uint32_t maxInputMemory; // RO - max time refs are held for
+ uint32_t maxOutputMemory; // RO
+
+ // per stream
+ bool compressed;
+ // format... video/compressed/audio/other?
+ // actual "audio/video" format type
+ uint32_t width/height? is this needed, or just queue...
+ // mime...
+};
+*/
+
+
+
+
+
+
+// overall component
+// => C: domain: audio or video
+// => C: kind: decoder, encoder or filter
+// => "mime" class
+
+// => C: temporal (bool) => does this depend on ordering?
+// => I: latency
+// => I: history max duration...
+// => I: history max frames kept...
+// => I: reordering depth
+// => I: frc (bool) (perhaps ratio?)
+// => I: current frc
+
+// - pause
+// => last frame 'number' processed
+// => current frame 'number' processed
+// => invalid settings =>[]
+
+// video decoder configuration: // audio
+// - encoding // -encoding
+// - hint: max width/height // -hint: sample rate, channels
+// - hint: profile/level // -hint: tools used
+// - hint: framerate (bitrate?) // -hint: bitrate
+// - default: color space (from container)
+// - hint: color format // -hint: pcm-encoding
+// - hint: # of views (e.g. MVC) // -hint?: channel groups
+// - default: HDR static info (from container) // -hint?: channel mappings
+// - hint: rotation (e.g. for allocator)
+
+// => # of streams required and their formats? (setting?)
+// => # of streams produced and their formats? (tuning)
+
+// => output
+// - # of views // -channel groups && channel mappings
+// - width/height/crop/color format/color space/HDR static info (from buffers)
+// (as required by the allocator & framework)
+// - SEI (or equivalent) <= [port]
+// - CC
+// - reference info
+
+// video encoder configurations
+// - encoding // - encoding
+// - hint: width/height // - hint: sample rate, channels
+// - hint: frame rate
+// - hint: max width/height (? does this differ from width/height?)
+// - # of input (e.g. MVC) // - hint: # groups and mappings
+// - # of output (e.g. SVC) => bitrates/width/height/framerates? per stream
+// - hint: profile/level // - hint: profile/level
+// - HDR static info + (info: HDR)
+// - color space
+// - hint: color format? // - hint: pcm encoding
+// - SEI
+// - CC
+// - reference directive
+// - hint: bitrate (or quality) // - hint: bitrate/quality
+// - optional: codec-specific parameters // - optional: csd
+
+// => output // => output
+// - layers per stream? // E-AC3?... DTS?...Dolby-Vision?
+// - reference info
+
+
+// RM:
+// - need SPS for full knowledge => component should return max. (component can use less)
+// - critical parameters? (interlaced? profile? level?)
+
+struct C2VideoSizeStruct {
+ int32_t mWidth; ///< video width
+ int32_t mHeight; ///< video height
+
+ DEFINE_AND_DESCRIBE_C2STRUCT(VideoSize)
+ C2FIELD(mWidth, "width")
+ C2FIELD(mHeight, "height")
+};
+
+// video size for video decoder [OUT]
+typedef C2StreamParam<C2Info, C2VideoSizeStruct> C2VideoSizeStreamInfo;
+
+// max video size for video decoder [IN]
+typedef C2PortParam<C2Setting, C2VideoSizeStruct, kParamIndexMaxVideoSizeHint> C2MaxVideoSizeHintPortSetting;
+
+// video encoder size [IN]
+typedef C2StreamParam<C2Tuning, C2VideoSizeStruct> C2VideoSizeStreamTuning;
+
+/// @}
+
+} // namespace android
+
+#endif
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
new file mode 100644
index 0000000..fd43061
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (C) 2016 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 C2PARAM_H_
+#define C2PARAM_H_
+
+#include <C2.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <list>
+#include <string>
+#include <type_traits>
+
+#define C2_PACK __attribute__((packed))
+
+namespace android {
+
+/// \addtogroup Parameters
+/// @{
+
+/// \defgroup internal Internal helpers.
+
+/*!
+ * \file
+ * PARAMETERS: SETTINGs, TUNINGs, and INFOs
+ * ===
+ *
+ * These represent miscellaneous control and metadata information and are likely copied into
+ * kernel space. Therefore, these are C-like structures designed to carry just a small amount of
+ * information. We are using C++ to be able to add constructors, as well as non-virtual and class
+ * methods.
+ *
+ * ==Specification details:
+ *
+ * Restrictions:
+ * - must be POD struct, e.g. no vtable (no virtual destructor)
+ * - must have the same size in 64-bit and 32-bit mode (no size_t)
+ * - as such, no pointer members
+ *
+ * Behavior:
+ * - Params can be global (not related to input or output), related to input or output,
+ * or related to an input/output stream.
+ * - All params are queried/set using a unique param index, which incorporates a potential stream
+ * index and/or port.
+ * - Querying (supported) params MUST never fail.
+ * - All params MUST have default values.
+ * - If some fields have "unsupported" or "invalid" values during setting, this SHOULD be
+ * communicated to the app.
+ * a) Ideally, this should be avoided. When setting parameters, in general, component should do
+ * "best effort" to apply all settings. It should change "invalid/unsupported" values to the
+ * nearest supported values.
+ * - This is communicated to the client by changing the source values in tune()/
+ * configure().
+ * b) If falling back to a supported value is absolutely impossible, the component SHALL return
+ * an error for the specific setting, but should continue to apply other settings.
+ * TODO: this currently may result in unintended results.
+ *
+ * **NOTE:** unlike OMX, params are not versioned. Instead, a new struct with new base index
+ * SHALL be added as new versions are required.
+ *
+ * The proper subtype (Setting, Info or Param) is incorporated into the class type. Define structs
+ * to define multiple subtyped versions of related parameters.
+ *
+ * ==Implementation details:
+ *
+ * - Use macros to define parameters
+ * - All parameters must have a default constructor
+ * - This is only used for instantiating the class in source (e.g. will not be used
+ * when building a parameter by the framework from key/value pairs.)
+ */
+
+/// \ingroup internal
+struct _C2ParamManipulator;
+
+/**
+ * Parameter base class.
+ */
+struct C2Param {
+ // param index encompasses the following:
+ //
+ // - type (setting, tuning, info, struct)
+ // - vendor extension flag
+ // - flexible parameter flag
+ // - direction (global, input, output)
+ // - stream flag
+ // - stream ID (usually 0)
+ //
+ // layout:
+ //
+ // +------+-----+---+------+--------+----|------+--------------+
+ // | kind | dir | - |stream|streamID|flex|vendor| base index |
+ // +------+-----+---+------+--------+----+------+--------------+
+ // bit: 31..30 29.28 25 24 .. 17 16 15 14 .. 0
+ //
+public:
+ /**
+ * C2Param kinds, usable as bitmaps.
+ */
+ enum Kind : uint32_t {
+ NONE = 0,
+ STRUCT = (1 << 0),
+ INFO = (1 << 1),
+ SETTING = (1 << 2),
+ TUNING = (1 << 3) | SETTING, // tunings are settings
+ };
+
+ /**
+ * base index (including the vendor extension bit) is a global index for
+ * C2 parameter structs. (e.g. the same indices cannot be reused for different
+ * structs for different components).
+ */
+ struct BaseIndex {
+ protected:
+ enum : uint32_t {
+ kTypeMask = 0xC0000000,
+ kTypeStruct = 0x00000000,
+ kTypeTuning = 0x40000000,
+ kTypeSetting = 0x80000000,
+ kTypeInfo = 0xC0000000,
+
+ kDirMask = 0x30000000,
+ kDirGlobal = 0x20000000,
+ kDirUndefined = 0x30000000, // MUST have all bits set
+ kDirInput = 0x00000000,
+ kDirOutput = 0x10000000,
+
+ kStreamFlag = 0x02000000,
+ kStreamIdMask = 0x01FE0000,
+ kStreamIdShift = 17,
+ kStreamIdMax = kStreamIdMask >> kStreamIdShift,
+ kStreamMask = kStreamFlag | kStreamIdMask,
+
+ kFlexibleFlag = 0x00010000,
+ kVendorFlag = 0x00008000,
+ kParamMask = 0x0000FFFF,
+ kBaseMask = kParamMask | kFlexibleFlag,
+ };
+
+ public:
+ enum : uint32_t {
+ kVendorStart = kVendorFlag, ///< vendor structs SHALL start after this
+ _kFlexibleFlag = kFlexibleFlag, // TODO: this is only needed for testing
+ };
+
+ /// constructor/conversion from uint32_t
+ inline BaseIndex(uint32_t index) : mIndex(index) { }
+
+ // no conversion from uint64_t
+ inline BaseIndex(uint64_t index) = delete;
+
+ /// returns true iff this is a vendor extension parameter
+ inline bool isVendor() const { return mIndex & kVendorFlag; }
+
+ /// returns true iff this is a flexible parameter (with variable size)
+ inline bool isFlexible() const { return mIndex & kFlexibleFlag; }
+
+ /// returns the base type: the index for the underlying struct
+ inline unsigned int baseIndex() const { return mIndex & kBaseMask; }
+
+ /// returns the param index for the underlying struct
+ inline unsigned int paramIndex() const { return mIndex & kParamMask; }
+
+ DEFINE_FIELD_BASED_COMPARISON_OPERATORS(BaseIndex, mIndex)
+
+ protected:
+ uint32_t mIndex;
+ };
+
+ /**
+ * type encompasses the parameter kind (tuning, setting, info), whether the
+ * parameter is global, input or output, and whether it is for a stream.
+ */
+ struct Type : public BaseIndex {
+ /// returns true iff this is a global parameter (not for input nor output)
+ inline bool isGlobal() const { return (mIndex & kDirMask) == kDirGlobal; }
+ /// returns true iff this is an input or input stream parameter
+ inline bool forInput() const { return (mIndex & kDirMask) == kDirInput; }
+ /// returns true iff this is an output or output stream parameter
+ inline bool forOutput() const { return (mIndex & kDirMask) == kDirOutput; }
+
+ /// returns true iff this is a stream parameter
+ inline bool forStream() const { return mIndex & kStreamFlag; }
+ /// returns true iff this is a port (input or output) parameter
+ inline bool forPort() const { return !forStream() && !isGlobal(); }
+
+ /// returns the parameter type: the parameter index without the stream ID
+ inline uint32_t type() const { return mIndex & (~kStreamIdMask); }
+
+ /// return the kind of this param
+ inline Kind kind() const {
+ switch (mIndex & kTypeMask) {
+ case kTypeStruct: return STRUCT;
+ case kTypeInfo: return INFO;
+ case kTypeSetting: return SETTING;
+ case kTypeTuning: return TUNING;
+ default: return NONE; // should not happen
+ }
+ }
+
+ /// constructor/conversion from uint32_t
+ inline Type(uint32_t index) : BaseIndex(index) { }
+
+ // no conversion from uint64_t
+ inline Type(uint64_t index) = delete;
+
+ private:
+ friend struct C2Param; // for setPort()
+ friend struct C2Tuning; // for kTypeTuning
+ friend struct C2Setting; // for kTypeSetting
+ friend struct C2Info; // for kTypeInfo
+ // for kDirGlobal
+ template<typename T, typename S, int I, class F> friend struct C2GlobalParam;
+ template<typename T, typename S, int I, class F> friend struct C2PortParam; // for kDir*
+ template<typename T, typename S, int I, class F> friend struct C2StreamParam; // for kDir*
+ friend struct _C2ParamInspector; // for testing
+
+ /**
+ * Sets the port/stream direction.
+ * @return true on success, false if could not set direction (e.g. it is global param).
+ */
+ inline bool setPort(bool output) {
+ if (isGlobal()) {
+ return false;
+ } else {
+ mIndex = (mIndex & ~kDirMask) | (output ? kDirOutput : kDirInput);
+ return true;
+ }
+ }
+ };
+
+ /**
+ * index encompasses all remaining information: basically the stream ID.
+ */
+ struct Index : public Type {
+ /// returns the index as uint32_t
+ inline operator uint32_t() const { return mIndex; }
+
+ /// constructor/conversion from uint32_t
+ inline Index(uint32_t index) : Type(index) { }
+
+ // no conversion from uint64_t
+ inline Index(uint64_t index) = delete;
+
+ /// returns the stream ID or ~0 if not a stream
+ inline unsigned stream() const {
+ return forStream() ? rawStream() : ~0U;
+ }
+
+ private:
+ friend struct C2Param; // for setStream, makeStreamId, isValid
+ friend struct _C2ParamInspector; // for testing
+
+ /**
+ * @return true if the type is valid, e.g. direction is not undefined AND
+ * stream is 0 if not a stream param.
+ */
+ inline bool isValid() const {
+ // there is no Type::isValid (even though some of this check could be
+ // performed on types) as this is only used on index...
+ return (forStream() ? rawStream() < kStreamIdMax : rawStream() == 0)
+ && (mIndex & kDirMask) != kDirUndefined;
+ }
+
+ /// returns the raw stream ID field
+ inline unsigned rawStream() const {
+ return (mIndex & kStreamIdMask) >> kStreamIdShift;
+ }
+
+ /// returns the streamId bitfield for a given |stream|. If stream is invalid,
+ /// returns an invalid bitfield.
+ inline static uint32_t makeStreamId(unsigned stream) {
+ // saturate stream ID (max value is invalid)
+ if (stream > kStreamIdMax) {
+ stream = kStreamIdMax;
+ }
+ return (stream << kStreamIdShift) & kStreamIdMask;
+ }
+
+ /**
+ * Sets the stream index.
+ * \return true on success, false if could not set index (e.g. not a stream param).
+ */
+ inline bool setStream(unsigned stream) {
+ if (forStream()) {
+ mIndex = (mIndex & ~kStreamIdMask) | makeStreamId(stream);
+ return this->stream() < kStreamIdMax;
+ }
+ return false;
+ }
+ };
+
+public:
+ // public getters for Index methods
+
+ /// returns true iff this is a vendor extension parameter
+ inline bool isVendor() const { return _mIndex.isVendor(); }
+ /// returns true iff this is a flexible parameter
+ inline bool isFlexible() const { return _mIndex.isFlexible(); }
+ /// returns true iff this is a global parameter (not for input nor output)
+ inline bool isGlobal() const { return _mIndex.isGlobal(); }
+ /// returns true iff this is an input or input stream parameter
+ inline bool forInput() const { return _mIndex.forInput(); }
+ /// returns true iff this is an output or output stream parameter
+ inline bool forOutput() const { return _mIndex.forOutput(); }
+
+ /// returns true iff this is a stream parameter
+ inline bool forStream() const { return _mIndex.forStream(); }
+ /// returns true iff this is a port (input or output) parameter
+ inline bool forPort() const { return _mIndex.forPort(); }
+
+ /// returns the stream ID or ~0 if not a stream
+ inline unsigned stream() const { return _mIndex.stream(); }
+
+ /// returns the parameter type: the parameter index without the stream ID
+ inline uint32_t type() const { return _mIndex.type(); }
+
+ /// returns the kind of this parameter
+ inline Kind kind() const { return _mIndex.kind(); }
+
+ /// returns the size of the parameter or 0 if the parameter is invalid
+ inline size_t size() const { return _mSize; }
+
+ /// returns true iff the parameter is valid
+ inline operator bool() const { return _mIndex.isValid() && _mSize > 0; }
+
+ /// returns true iff the parameter is invalid
+ inline bool operator!() const { return !operator bool(); }
+
+ // equality is done by memcmp (use equals() to prevent any overread)
+ inline bool operator==(const C2Param &o) const {
+ return equals(o) && memcmp(this, &o, _mSize) == 0;
+ }
+ inline bool operator!=(const C2Param &o) const { return !operator==(o); }
+
+ /// safe(r) type cast from pointer and size
+ inline static C2Param* From(void *addr, size_t len) {
+ // _mSize must fit into size
+ if (len < sizeof(_mSize) + offsetof(C2Param, _mSize)) {
+ return nullptr;
+ }
+ // _mSize must match length
+ C2Param *param = (C2Param*)addr;
+ if (param->_mSize != len) {
+ return nullptr;
+ }
+ return param;
+ }
+
+#if 0
+ template<typename P, class=decltype(C2Param(P()))>
+ P *As() { return P::From(this); }
+ template<typename P>
+ const P *As() const { return const_cast<const P*>(P::From(const_cast<C2Param*>(this))); }
+#endif
+
+protected:
+ /// sets the stream field. Returns true iff successful.
+ inline bool setStream(unsigned stream) {
+ return _mIndex.setStream(stream);
+ }
+
+ /// sets the port (direction). Returns true iff successful.
+ inline bool setPort(bool output) {
+ return _mIndex.setPort(output);
+ }
+
+public:
+ /// invalidate this parameter. There is no recovery from this call; e.g. parameter
+ /// cannot be 'corrected' to be valid.
+ inline void invalidate() { _mSize = 0; }
+
+ // if other is the same kind of (valid) param as this, copy it into this and return true.
+ // otherwise, do not copy anything, and return false.
+ inline bool updateFrom(const C2Param &other) {
+ if (other._mSize == _mSize && other._mIndex == _mIndex && _mSize > 0) {
+ memcpy(this, &other, _mSize);
+ return true;
+ }
+ return false;
+ }
+
+protected:
+ // returns |o| if it is a null ptr, or if can suitably be a param of given |type| (e.g. has
+ // same type (ignoring stream ID), and size). Otherwise, returns null. If |checkDir| is false,
+ // allow undefined or different direction (e.g. as constructed from C2PortParam() vs.
+ // C2PortParam::input), but still require equivalent type (stream, port or global); otherwise,
+ // return null.
+ inline static const C2Param* ifSuitable(
+ const C2Param* o, size_t size, Type type, size_t flexSize = 0, bool checkDir = true) {
+ if (o == nullptr || o->_mSize < size || (flexSize && ((o->_mSize - size) % flexSize))) {
+ return nullptr;
+ } else if (checkDir) {
+ return o->_mIndex.type() == type.mIndex ? o : nullptr;
+ } else if (o->_mIndex.isGlobal()) {
+ return nullptr;
+ } else {
+ return ((o->_mIndex.type() ^ type.mIndex) & ~Type::kDirMask) ? nullptr : o;
+ }
+ }
+
+ /// base constructor
+ inline C2Param(uint32_t paramSize, Index paramIndex)
+ : _mSize(paramSize),
+ _mIndex(paramIndex) {
+ if (paramSize > sizeof(C2Param)) {
+ memset(this + 1, 0, paramSize - sizeof(C2Param));
+ }
+ }
+
+ /// base constructor with stream set
+ inline C2Param(uint32_t paramSize, Index paramIndex, unsigned stream)
+ : _mSize(paramSize),
+ _mIndex(paramIndex | Index::makeStreamId(stream)) {
+ if (paramSize > sizeof(C2Param)) {
+ memset(this + 1, 0, paramSize - sizeof(C2Param));
+ }
+ if (!forStream()) {
+ invalidate();
+ }
+ }
+
+private:
+ friend struct _C2ParamInspector; // for testing
+
+ /// returns the base type: the index for the underlying struct (for testing
+ /// as this can be gotten by the baseIndex enum)
+ inline uint32_t _baseIndex() const { return _mIndex.baseIndex(); }
+
+ /// returns true iff |o| has the same size and index as this. This performs the
+ /// basic check for equality.
+ inline bool equals(const C2Param &o) const {
+ return _mSize == o._mSize && _mIndex == o._mIndex;
+ }
+
+ uint32_t _mSize;
+ Index _mIndex;
+};
+
+/// \ingroup internal
+/// allow C2Params access to private methods, e.g. constructors
+#define C2PARAM_MAKE_FRIENDS \
+ template<typename U, typename S, int I, class F> friend struct C2GlobalParam; \
+ template<typename U, typename S, int I, class F> friend struct C2PortParam; \
+ template<typename U, typename S, int I, class F> friend struct C2StreamParam; \
+
+/**
+ * Setting base structure for component method signatures. Wrap constructors.
+ */
+struct C2Setting : public C2Param {
+protected:
+ template<typename ...Args>
+ inline C2Setting(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+ enum : uint32_t { indexFlags = Type::kTypeSetting };
+};
+
+/**
+ * Tuning base structure for component method signatures. Wrap constructors.
+ */
+struct C2Tuning : public C2Setting {
+protected:
+ template<typename ...Args>
+ inline C2Tuning(const Args(&... args)) : C2Setting(args...) { }
+public: // TODO
+ enum : uint32_t { indexFlags = Type::kTypeTuning };
+};
+
+/**
+ * Info base structure for component method signatures. Wrap constructors.
+ */
+struct C2Info : public C2Param {
+protected:
+ template<typename ...Args>
+ inline C2Info(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+ enum : uint32_t { indexFlags = Type::kTypeInfo };
+};
+
+/**
+ * Structure uniquely specifying a field in an arbitrary structure.
+ *
+ * \note This structure is used differently in C2FieldDescriptor to
+ * identify array fields, such that _mSize is the size of each element. This is
+ * because the field descriptor contains the array-length, and we want to keep
+ * a relevant element size for variable length arrays.
+ */
+struct _C2FieldId {
+//public:
+ /**
+ * Constructor used for C2FieldDescriptor that removes the array extent.
+ *
+ * \param[in] offset pointer to the field in an object at address 0.
+ */
+ template<typename T, class B=typename std::remove_extent<T>::type>
+ inline _C2FieldId(T* offset)
+ : // offset is from "0" so will fit on 32-bits
+ _mOffset((uint32_t)(uintptr_t)(offset)),
+ _mSize(sizeof(B)) { }
+
+ /**
+ * Direct constructor from offset and size.
+ *
+ * \param[in] offset offset of the field.
+ * \param[in] size size of the field.
+ */
+ inline _C2FieldId(size_t offset, size_t size)
+ : _mOffset(offset), _mSize(size) {}
+
+ /**
+ * Constructor used to identify a field in an object.
+ *
+ * \param U[type] pointer to the object that contains this field. This is needed in case the
+ * field is in an (inherited) base class, in which case T will be that base class.
+ * \param pm[im] member pointer to the field
+ */
+ template<typename R, typename T, typename U, typename B=typename std::remove_extent<R>::type>
+ inline _C2FieldId(U *, R T::* pm)
+ : _mOffset((uint32_t)(uintptr_t)(&(((U*)256)->*pm)) - 256u),
+ _mSize(sizeof(B)) { }
+
+ /**
+ * Constructor used to identify a field in an object.
+ *
+ * \param U[type] pointer to the object that contains this field
+ * \param pm[im] member pointer to the field
+ */
+ template<typename R, typename T, typename B=typename std::remove_extent<R>::type>
+ inline _C2FieldId(R T::* pm)
+ : _mOffset((uint32_t)(uintptr_t)(&(((T*)0)->*pm))),
+ _mSize(sizeof(B)) { }
+
+ inline bool operator==(const _C2FieldId &other) const {
+ return _mOffset == other._mOffset && _mSize == other._mSize;
+ }
+
+ inline bool operator<(const _C2FieldId &other) const {
+ return _mOffset < other._mOffset ||
+ // NOTE: order parent structure before sub field
+ (_mOffset == other._mOffset && _mSize > other._mSize);
+ }
+
+ DEFINE_OTHER_COMPARISON_OPERATORS(_C2FieldId)
+
+#if 0
+ inline uint32_t offset() const { return _mOffset; }
+ inline uint32_t size() const { return _mSize; }
+#endif
+
+#if defined(FRIEND_TEST)
+ friend void PrintTo(const _C2FieldId &d, ::std::ostream*);
+#endif
+
+private:
+ uint32_t _mOffset; // offset of field
+ uint32_t _mSize; // size of field
+};
+
+/**
+ * Structure uniquely specifying a field in a configuration
+ */
+struct C2ParamField {
+//public:
+ // TODO: fix what this is for T[] (for now size becomes T[1])
+ template<typename S, typename T>
+ inline C2ParamField(S* param, T* offset)
+ : _mIndex(param->index()),
+ _mFieldId(offset) {}
+
+ template<typename R, typename T, typename U>
+ inline C2ParamField(U *p, R T::* pm) : _mIndex(p->type()), _mFieldId(p, pm) { }
+
+ inline bool operator==(const C2ParamField &other) const {
+ return _mIndex == other._mIndex && _mFieldId == other._mFieldId;
+ }
+
+ inline bool operator<(const C2ParamField &other) const {
+ return _mIndex < other._mIndex ||
+ (_mIndex == other._mIndex && _mFieldId < other._mFieldId);
+ }
+
+ DEFINE_OTHER_COMPARISON_OPERATORS(C2ParamField)
+
+private:
+ C2Param::Index _mIndex;
+ _C2FieldId _mFieldId;
+};
+
+/**
+ * A shared (union) representation of numeric values
+ */
+class C2Value {
+public:
+ /// A union of supported primitive types.
+ union Primitive {
+ int32_t i32; ///< int32_t value
+ uint32_t u32; ///< uint32_t value
+ int64_t i64; ///< int64_t value
+ uint64_t u64; ///< uint64_t value
+ float fp; ///< float value
+
+ // constructors - implicit
+ Primitive(int32_t value) : i32(value) { }
+ Primitive(uint32_t value) : u32(value) { }
+ Primitive(int64_t value) : i64(value) { }
+ Primitive(uint64_t value) : u64(value) { }
+ Primitive(float value) : fp(value) { }
+
+ Primitive() : u64(0) { }
+
+ private:
+ friend class C2Value;
+ template<typename T> const T &ref() const;
+ };
+
+ enum Type {
+ NO_INIT,
+ INT32,
+ UINT32,
+ INT64,
+ UINT64,
+ FLOAT,
+ };
+
+ template<typename T> static constexpr Type typeFor();
+
+ // constructors - implicit
+ template<typename T>
+ C2Value(T value) : mType(typeFor<T>()), mValue(value) { }
+
+ C2Value() : mType(NO_INIT) { }
+
+ inline Type type() const { return mType; }
+
+ template<typename T>
+ inline bool get(T *value) const {
+ if (mType == typeFor<T>()) {
+ *value = mValue.ref<T>();
+ return true;
+ }
+ return false;
+ }
+
+private:
+ Type mType;
+ Primitive mValue;
+};
+
+template<> const int32_t &C2Value::Primitive::ref<int32_t>() const { return i32; }
+template<> const int64_t &C2Value::Primitive::ref<int64_t>() const { return i64; }
+template<> const uint32_t &C2Value::Primitive::ref<uint32_t>() const { return u32; }
+template<> const uint64_t &C2Value::Primitive::ref<uint64_t>() const { return u64; }
+template<> const float &C2Value::Primitive::ref<float>() const { return fp; }
+
+template<> constexpr C2Value::Type C2Value::typeFor<int32_t>() { return INT32; }
+template<> constexpr C2Value::Type C2Value::typeFor<int64_t>() { return INT64; }
+template<> constexpr C2Value::Type C2Value::typeFor<uint32_t>() { return UINT32; }
+template<> constexpr C2Value::Type C2Value::typeFor<uint64_t>() { return UINT64; }
+template<> constexpr C2Value::Type C2Value::typeFor<float>() { return FLOAT; }
+
+/**
+ * field descriptor. A field is uniquely defined by an index into a parameter.
+ * (Note: Stream-id is not captured as a field.)
+ *
+ * Ordering of fields is by offset. In case of structures, it is depth first,
+ * with a structure taking an index just before and in addition to its members.
+ */
+struct C2FieldDescriptor {
+//public:
+ /** field types and flags
+ * \note: only 32-bit and 64-bit fields are supported (e.g. no boolean, as that
+ * is represented using INT32).
+ */
+ enum Type : uint32_t {
+ // primitive types
+ INT32 = C2Value::INT32, ///< 32-bit signed integer
+ UINT32 = C2Value::UINT32, ///< 32-bit unsigned integer
+ INT64 = C2Value::INT64, ///< 64-bit signed integer
+ UINT64 = C2Value::UINT64, ///< 64-bit signed integer
+ FLOAT = C2Value::FLOAT, ///< 32-bit floating point
+
+ // array types
+ STRING = 0x100, ///< fixed-size string (POD)
+ BLOB, ///< blob. Blobs have no sub-elements and can be thought of as byte arrays;
+ ///< however, bytes cannot be individually addressed by clients.
+
+ // complex types
+ STRUCT_FLAG = 0x10000, ///< structs. Marked with this flag in addition to their baseIndex.
+ };
+
+ typedef std::pair<C2String, C2Value::Primitive> named_value_type;
+ typedef std::vector<const named_value_type> named_values_type;
+ //typedef std::pair<std::vector<C2String>, std::vector<C2Value::Primitive>> named_values_type;
+
+ /**
+ * Template specialization that returns the named values for a type.
+ *
+ * \todo hide from client.
+ *
+ * \return a vector of name-value pairs.
+ */
+ template<typename B>
+ static named_values_type namedValuesFor(const B &);
+
+ inline C2FieldDescriptor(uint32_t type, uint32_t length, C2StringLiteral name, size_t offset, size_t size)
+ : _mType((Type)type), _mLength(length), _mName(name), _mFieldId(offset, size) { }
+
+ template<typename T, class B=typename std::remove_extent<T>::type>
+ inline C2FieldDescriptor(const T* offset, const char *name)
+ : _mType(this->getType((B*)nullptr)),
+ _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ _mName(name),
+ _mNamedValues(namedValuesFor(*(B*)0)),
+ _mFieldId(offset) {}
+
+/*
+ template<typename T, typename B=typename std::remove_extent<T>::type>
+ inline C2FieldDescriptor<T, B, false>(T* offset, const char *name)
+ : _mType(this->getType((B*)nullptr)),
+ _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ _mName(name),
+ _mFieldId(offset) {}
+*/
+
+ /// \deprecated
+ template<typename T, typename S, class B=typename std::remove_extent<T>::type>
+ constexpr inline C2FieldDescriptor(S*, T S::* field, const char *name)
+ : _mType(this->getType((B*)nullptr)),
+ _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+ _mName(name),
+ _mFieldId(&(((S*)0)->*field)) {}
+
+ /// returns the type of this field
+ inline Type type() const { return _mType; }
+ /// returns the length of the field in case it is an array. Returns 0 for
+ /// T[] arrays, returns 1 for T[1] arrays as well as if the field is not an array.
+ inline size_t length() const { return _mLength; }
+ /// returns the name of the field
+ inline C2StringLiteral name() const { return _mName; }
+
+ const named_values_type &namedValues() const { return _mNamedValues; }
+
+#if defined(FRIEND_TEST)
+ friend void PrintTo(const C2FieldDescriptor &, ::std::ostream*);
+ friend bool operator==(const C2FieldDescriptor &, const C2FieldDescriptor &);
+ FRIEND_TEST(C2ParamTest_ParamFieldList, VerifyStruct);
+#endif
+
+private:
+ const Type _mType;
+ const uint32_t _mLength; // the last member can be arbitrary length if it is T[] array,
+ // extending to the end of the parameter (this is marked with
+ // 0). T[0]-s are not fields.
+ const C2StringLiteral _mName;
+ const named_values_type _mNamedValues;
+
+ const _C2FieldId _mFieldId; // field identifier (offset and size)
+
+ // NOTE: We do not capture default value(s) here as that may depend on the component.
+ // NOTE: We also do not capture bestEffort, as 1) this should be true for most fields,
+ // 2) this is at parameter granularity.
+
+ // type resolution
+ inline static Type getType(int32_t*) { return INT32; }
+ inline static Type getType(uint32_t*) { return UINT32; }
+ inline static Type getType(int64_t*) { return INT64; }
+ inline static Type getType(uint64_t*) { return UINT64; }
+ inline static Type getType(float*) { return FLOAT; }
+ inline static Type getType(char*) { return STRING; }
+ inline static Type getType(uint8_t*) { return BLOB; }
+
+ template<typename T,
+ class=typename std::enable_if<std::is_enum<T>::value>::type>
+ inline static Type getType(T*) {
+ typename std::underlying_type<T>::type underlying(0);
+ return getType(&underlying);
+ }
+
+ // verify C2Struct by having a fieldList and a baseIndex.
+ template<typename T,
+ class=decltype(T::baseIndex + 1), class=decltype(T::fieldList)>
+ inline static Type getType(T*) {
+ static_assert(!std::is_base_of<C2Param, T>::value, "cannot use C2Params as fields");
+ return (Type)(T::baseIndex | STRUCT_FLAG);
+ }
+};
+
+#define DEFINE_NO_NAMED_VALUES_FOR(type) \
+template<> inline C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const type &) { \
+ return named_values_type(); \
+}
+
+// We cannot subtype constructor for enumerated types so insted define no named values for
+// non-enumerated integral types.
+DEFINE_NO_NAMED_VALUES_FOR(int32_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint32_t)
+DEFINE_NO_NAMED_VALUES_FOR(int64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint8_t)
+DEFINE_NO_NAMED_VALUES_FOR(char)
+DEFINE_NO_NAMED_VALUES_FOR(float)
+
+/**
+ * Describes the fields of a structure.
+ */
+struct C2StructDescriptor {
+public:
+ /// Returns the parameter type
+ inline C2Param::BaseIndex baseIndex() const { return _mType.baseIndex(); }
+
+ // Returns the number of fields in this param (not counting any recursive fields).
+ // Must be at least 1 for valid params.
+ inline size_t numFields() const { return _mFields.size(); }
+
+ // Returns the list of immediate fields (not counting any recursive fields).
+ typedef std::vector<const C2FieldDescriptor>::const_iterator field_iterator;
+ inline field_iterator cbegin() const { return _mFields.cbegin(); }
+ inline field_iterator cend() const { return _mFields.cend(); }
+
+ // only supplying const iterator - but these are needed for range based loops
+ inline field_iterator begin() const { return _mFields.cbegin(); }
+ inline field_iterator end() const { return _mFields.cend(); }
+
+ template<typename T>
+ inline C2StructDescriptor(T*)
+ : C2StructDescriptor(T::baseIndex, T::fieldList) { }
+
+ inline C2StructDescriptor(
+ C2Param::BaseIndex type,
+ std::initializer_list<const C2FieldDescriptor> fields)
+ : _mType(type), _mFields(fields) { }
+
+private:
+ const C2Param::BaseIndex _mType;
+ const std::vector<const C2FieldDescriptor> _mFields;
+};
+
+/**
+ * Describes parameters for a component.
+ */
+struct C2ParamDescriptor {
+public:
+ /**
+ * Returns whether setting this param is required to configure this component.
+ * This can only be true for builtin params for platform-defined components (e.g. video and
+ * audio encoders/decoders, video/audio filters).
+ * For vendor-defined components, it can be true even for vendor-defined params,
+ * but it is not recommended, in case the component becomes platform-defined.
+ */
+ inline bool isRequired() const { return _mIsRequired; }
+
+ /**
+ * Returns whether this parameter is persistent. This is always true for C2Tuning and C2Setting,
+ * but may be false for C2Info. If true, this parameter persists across frames and applies to
+ * the current and subsequent frames. If false, this C2Info parameter only applies to the
+ * current frame and is not assumed to have the same value (or even be present) on subsequent
+ * frames, unless it is specified for those frames.
+ */
+ inline bool isPersistent() const { return _mIsPersistent; }
+
+ /// Returns the name of this param.
+ /// This defaults to the underlying C2Struct's name, but could be altered for a component.
+ inline C2String name() const { return _mName; }
+
+ /// Returns the parameter type
+ /// \todo fix this
+ inline C2Param::Type type() const { return _mType; }
+
+ template<typename T>
+ inline C2ParamDescriptor(bool isRequired, C2StringLiteral name, const T*)
+ : _mIsRequired(isRequired),
+ _mIsPersistent(true),
+ _mName(name),
+ _mType(T::typeIndex) { }
+
+ inline C2ParamDescriptor(
+ bool isRequired, C2StringLiteral name, C2Param::Type type)
+ : _mIsRequired(isRequired),
+ _mIsPersistent(true),
+ _mName(name),
+ _mType(type) { }
+
+private:
+ const bool _mIsRequired;
+ const bool _mIsPersistent;
+ const C2String _mName;
+ const C2Param::Type _mType;
+};
+
+/// \ingroup internal
+/// Define a structure without baseIndex.
+#define DEFINE_C2STRUCT_NO_BASE(name) \
+public: \
+ typedef C2##name##Struct _type; /**< type name shorthand */ \
+ const static std::initializer_list<const C2FieldDescriptor> fieldList; /**< structure fields */
+
+/// Define a structure with matching baseIndex.
+#define DEFINE_C2STRUCT(name) \
+public: \
+ enum : uint32_t { baseIndex = kParamIndex##name }; \
+ DEFINE_C2STRUCT_NO_BASE(name)
+
+/// Define a flexible structure with matching baseIndex.
+#define DEFINE_FLEX_C2STRUCT(name, flexMember) \
+public: \
+ FLEX(C2##name##Struct, flexMember) \
+ enum : uint32_t { baseIndex = kParamIndex##name | C2Param::BaseIndex::_kFlexibleFlag }; \
+ DEFINE_C2STRUCT_NO_BASE(name)
+
+/// \ingroup internal
+/// Describe a structure of a templated structure.
+#define DESCRIBE_TEMPLATED_C2STRUCT(strukt, list) \
+ template<> \
+ const std::initializer_list<const C2FieldDescriptor> strukt::fieldList = list;
+
+/// \deprecated
+/// Describe the fields of a structure using an initializer list.
+#define DESCRIBE_C2STRUCT(name, list) \
+ const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = list;
+
+/**
+ * Describe a field of a structure.
+ * These must be in order.
+ *
+ * There are two ways to use this macro:
+ *
+ * ~~~~~~~~~~~~~ (.cpp)
+ * struct C2VideoWidthStruct {
+ * int32_t mWidth;
+ * C2VideoWidthStruct() {} // optional default constructor
+ * C2VideoWidthStruct(int32_t _width) : mWidth(_width) {}
+ *
+ * DEFINE_AND_DESCRIBE_C2STRUCT(VideoWidth)
+ * C2FIELD(mWidth, "width")
+ * };
+ * ~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~ (.cpp)
+ * struct C2VideoWidthStruct {
+ * int32_t mWidth;
+ * C2VideoWidthStruct() = default; // optional default constructor
+ * C2VideoWidthStruct(int32_t _width) : mWidth(_width) {}
+ *
+ * DEFINE_C2STRUCT(VideoWidth)
+ * } C2_PACK;
+ *
+ * DESCRIBE_C2STRUCT(VideoWidth, {
+ * C2FIELD(mWidth, "width")
+ * })
+ * ~~~~~~~~~~~~~
+ *
+ * For flexible structures (those ending in T[]), use the flexible macros:
+ *
+ * ~~~~~~~~~~~~~ (.cpp)
+ * struct C2VideoFlexWidthsStruct {
+ * int32_t mWidths[];
+ * C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ * private:
+ * // may have private constructors taking number of widths as the first argument
+ * // This is used by the C2Param factory methods, e.g.
+ * // C2VideoFlexWidthsGlobalParam::alloc_unique(size_t, int32_t);
+ * C2VideoFlexWidthsStruct(size_t flexCount, int32_t value) {
+ * for (size_t i = 0; i < flexCount; ++i) {
+ * mWidths[i] = value;
+ * }
+ * }
+ *
+ * // If the last argument is T[N] or std::initializer_list<T>, the flexCount will
+ * // be automatically calculated and passed by the C2Param factory methods, e.g.
+ * // int widths[] = { 1, 2, 3 };
+ * // C2VideoFlexWidthsGlobalParam::alloc_unique(widths);
+ * template<unsigned N>
+ * C2VideoFlexWidthsStruct(size_t flexCount, const int32_t(&init)[N]) {
+ * for (size_t i = 0; i < flexCount; ++i) {
+ * mWidths[i] = init[i];
+ * }
+ * }
+ *
+ * DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoFlexWidths, mWidths)
+ * C2FIELD(mWidths, "widths")
+ * };
+ * ~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~ (.cpp)
+ * struct C2VideoFlexWidthsStruct {
+ * int32_t mWidths[];
+ * C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ * DEFINE_FLEX_C2STRUCT(VideoFlexWidths, mWidths)
+ * } C2_PACK;
+ *
+ * DESCRIBE_C2STRUCT(VideoFlexWidths, {
+ * C2FIELD(mWidths, "widths")
+ * })
+ * ~~~~~~~~~~~~~
+ *
+ */
+#define C2FIELD(member, name) \
+ C2FieldDescriptor(&((_type*)(nullptr))->member, name),
+
+/// \deprecated
+#define C2SOLE_FIELD(member, name) \
+ C2FieldDescriptor(&_type::member, name, 0)
+
+/// Define a structure with matching baseIndex and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_C2STRUCT(name) \
+ DEFINE_C2STRUCT(name) } C2_PACK; \
+ const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = {
+
+/// Define a flexible structure with matching baseIndex and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember) \
+ DEFINE_FLEX_C2STRUCT(name, flexMember) } C2_PACK; \
+ const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = {
+
+/**
+ * Parameter reflector class.
+ *
+ * This class centralizes the description of parameter structures. This can be shared
+ * by multiple components as describing a parameter does not imply support of that
+ * parameter. However, each supported parameter and any dependent structures within
+ * must be described by the parameter reflector provided by a component.
+ */
+class C2ParamReflector {
+public:
+ /**
+ * Describes a parameter structure.
+ *
+ * \param[in] paramIndex the base index of the parameter structure
+ *
+ * \return the description of the parameter structure
+ * \retval nullptr if the parameter is not supported by this reflector
+ *
+ * This methods shall not block and return immediately.
+ *
+ * \note this class does not take a set of indices because we would then prefer
+ * to also return any dependent structures, and we don't want this logic to be
+ * repeated in each reflector. Alternately, this could just return a map of all
+ * descriptions, but we want to conserve memory if client only wants the description
+ * of a few indices.
+ */
+ virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) = 0;
+
+protected:
+ virtual ~C2ParamReflector() = default;
+};
+
+/**
+ * A useable supported values for a field.
+ *
+ * This can be either a range or a set of values. The range can be linear or geometric with a
+ * clear minimum and maximum value, and can have an optional step size or geometric ratio. Values
+ * can optionally represent flags.
+ *
+ * \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
+ */
+template<typename T>
+struct C2TypedFieldSupportedValues {
+//public:
+ enum Type {
+ RANGE, ///< a numeric range that can be continuous or discrete
+ VALUES, ///< a list of values
+ FLAGS ///< a list of flags that can be OR-ed
+ };
+
+ Type type;
+
+ struct {
+ T min;
+ T max;
+ T step;
+ T nom;
+ T denom;
+ } range;
+ std::vector<T> values;
+
+ C2TypedFieldSupportedValues(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+ : type(RANGE),
+ range{min, max, step, (T)1, (T)1} { }
+
+ C2TypedFieldSupportedValues(T min, T max, T nom, T den) :
+ type(RANGE),
+ range{min, max, (T)0, nom, den} { }
+
+ C2TypedFieldSupportedValues(bool flags, std::initializer_list<T> list) :
+ type(flags ? FLAGS : VALUES),
+ values(list) {}
+};
+
+/**
+ * Generic supported values for a field.
+ *
+ * This can be either a range or a set of values. The range can be linear or geometric with a
+ * clear minimum and maximum value, and can have an optional step size or geometric ratio. Values
+ * can optionally represent flags.
+ *
+ * \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
+ */
+struct C2FieldSupportedValues {
+//public:
+ enum Type {
+ RANGE, ///< a numeric range that can be continuous or discrete
+ VALUES, ///< a list of values
+ FLAGS ///< a list of flags that can be OR-ed
+ };
+
+ Type type;
+
+ typedef C2Value::Primitive Primitive;
+
+ struct {
+ Primitive min;
+ Primitive max;
+ Primitive step;
+ Primitive nom;
+ Primitive denom;
+ } range;
+ std::vector<Primitive> values;
+
+ template<typename T>
+ C2FieldSupportedValues(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+ : type(RANGE),
+ range{min, max, step, (T)1, (T)1} { }
+
+ template<typename T>
+ C2FieldSupportedValues(T min, T max, T nom, T den) :
+ type(RANGE),
+ range{min, max, (T)0, nom, den} { }
+
+ template<typename T>
+ C2FieldSupportedValues(bool flags, std::initializer_list<T> list)
+ : type(flags ? FLAGS : VALUES),
+ range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+ for(T value : list) {
+ values.emplace_back(value);
+ }
+ }
+
+ template<typename T, typename E=decltype(C2FieldDescriptor::namedValuesFor(*(T*)0))>
+ C2FieldSupportedValues(bool flags, const T*)
+ : type(flags ? FLAGS : VALUES),
+ range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+ C2FieldDescriptor::named_values_type named = C2FieldDescriptor::namedValuesFor(*(T*)0);
+ for (const C2FieldDescriptor::named_value_type &item : named) {
+ values.emplace_back(item.second);
+ }
+ }
+};
+
+/// @}
+
+} // namespace android
+
+#endif // C2PARAM_H_
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
new file mode 100644
index 0000000..f369617
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+/** \file
+ * Templates used to declare parameters.
+ */
+#ifndef C2PARAM_DEF_H_
+#define C2PARAM_DEF_H_
+
+#include <type_traits>
+
+#include <C2Param.h>
+
+namespace android {
+
+/// \addtogroup Parameters
+/// @{
+
+/* ======================== UTILITY TEMPLATES FOR PARAMETER DEFINITIONS ======================== */
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class that checks if a type has equality and inequality operators.
+struct C2_HIDE _C2Comparable_impl
+{
+ template<typename S, typename=decltype(S() == S())>
+ static std::true_type __testEQ(int);
+ template<typename>
+ static std::false_type __testEQ(...);
+
+ template<typename S, typename=decltype(S() != S())>
+ static std::true_type __testNE(int);
+ template<typename>
+ static std::false_type __testNE(...);
+};
+
+/**
+ * Helper template that returns if a type has equality and inequality operators.
+ *
+ * Use as _C2Comparable<typename S>::value.
+ */
+template<typename S>
+struct C2_HIDE _C2Comparable
+ : public std::integral_constant<bool, decltype(_C2Comparable_impl::__testEQ<S>(0))::value
+ || decltype(_C2Comparable_impl::__testNE<S>(0))::value> {
+};
+
+/// Helper class that checks if a type has a baseIndex constant.
+struct C2_HIDE _C2BaseIndexHelper_impl
+{
+ template<typename S, int=S::baseIndex>
+ static std::true_type __testBaseIndex(int);
+ template<typename>
+ static std::false_type __testBaseIndex(...);
+};
+
+/// Helper template that verifies a type's baseIndex and creates it if the type does not have one.
+template<typename S, int BaseIndex,
+ bool HasBase=decltype(_C2BaseIndexHelper_impl::__testBaseIndex<S>(0))::value>
+struct C2_HIDE C2BaseIndexOverride {
+ // TODO: what if we allow structs without baseIndex?
+ static_assert(BaseIndex == S::baseIndex, "baseIndex differs from structure");
+};
+
+/// Specialization for types without a baseIndex.
+template<typename S, int BaseIndex>
+struct C2_HIDE C2BaseIndexOverride<S, BaseIndex, false> {
+public:
+ enum : uint32_t {
+ baseIndex = BaseIndex, ///< baseIndex override.
+ };
+};
+
+/// Helper template that adds a baseIndex to a type if it does not have one.
+template<typename S, int BaseIndex>
+struct C2_HIDE C2AddBaseIndex : public S, public C2BaseIndexOverride<S, BaseIndex> {};
+
+/**
+ * \brief Helper class to check struct requirements for parameters.
+ *
+ * Features:
+ * - verify default constructor, no virtual methods, and no equality operators.
+ * - expose typeIndex, and non-flex flexSize.
+ */
+template<typename S, int BaseIndex, unsigned TypeIndex>
+struct C2_HIDE C2StructCheck {
+ static_assert(
+ std::is_default_constructible<S>::value, "C2 structure must have default constructor");
+ static_assert(!std::is_polymorphic<S>::value, "C2 structure must not have virtual methods");
+ static_assert(!_C2Comparable<S>::value, "C2 structure must not have operator== or !=");
+
+public:
+ enum : uint32_t {
+ typeIndex = BaseIndex | TypeIndex
+ };
+
+protected:
+ enum : uint32_t {
+ flexSize = 0, // TODO: is this still needed? this may be confusing.
+ };
+};
+
+/// Helper class that checks if a type has an integer flexSize member.
+struct C2_HIDE _C2Flexible_impl {
+ /// specialization for types that have a flexSize member
+ template<typename S, unsigned=S::flexSize>
+ static std::true_type __testFlexSize(int);
+ template<typename>
+ static std::false_type __testFlexSize(...);
+};
+
+/// Helper template that returns if a type has an integer flexSize member.
+template<typename S>
+struct C2_HIDE _C2Flexible
+ : public std::integral_constant<bool, decltype(_C2Flexible_impl::__testFlexSize<S>(0))::value> {
+};
+
+/// Macro to test if a type is flexible (has a flexSize member).
+#define IF_FLEXIBLE(S) ENABLE_IF(_C2Flexible<S>::value)
+/// Shorthand for std::enable_if
+#define ENABLE_IF(cond) typename std::enable_if<cond>::type
+
+/// Helper template that exposes the flexible subtype of a struct.
+template<typename S, typename E=void>
+struct C2_HIDE _C2FlexHelper {
+ typedef void flexType;
+ enum : uint32_t { flexSize = 0 };
+};
+
+/// Specialization for flexible types.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S,
+ typename std::enable_if<!std::is_void<typename S::flexMemberType>::value>::type> {
+ typedef typename _C2FlexHelper<typename S::flexMemberType>::flexType flexType;
+ enum : uint32_t { flexSize = _C2FlexHelper<typename S::flexMemberType>::flexSize };
+};
+
+/// Specialization for flex arrays.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S[],
+ typename std::enable_if<std::is_void<typename _C2FlexHelper<S>::flexType>::value>::type> {
+ typedef S flexType;
+ enum : uint32_t { flexSize = sizeof(S) };
+};
+
+/**
+ * \brief Helper class to check flexible struct requirements and add common operations.
+ *
+ * Features:
+ * - expose baseIndex and fieldList (this is normally inherited from the struct, but flexible
+ * structs cannot be base classes and thus inherited from)
+ * - disable copy assignment and construction (TODO: this is already done in the FLEX macro for the
+ * flexible struct, so may not be needed here)
+ */
+template<typename S, int BaseIndex, unsigned TypeIndex>
+struct C2_HIDE C2FlexStructCheck : public C2StructCheck<S, BaseIndex, TypeIndex> {
+public:
+ enum : uint32_t {
+ /// \hideinitializer
+ baseIndex = BaseIndex | C2Param::BaseIndex::_kFlexibleFlag, ///< flexible struct base-index
+ };
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList; // TODO assign here
+
+ // default constructor needed because of the disabled copy constructor
+ inline C2FlexStructCheck() = default;
+
+protected:
+ // cannot copy flexible params
+ C2FlexStructCheck(const C2FlexStructCheck<S, BaseIndex, TypeIndex> &) = delete;
+ C2FlexStructCheck& operator= (const C2FlexStructCheck<S, BaseIndex, TypeIndex> &) = delete;
+
+ // constants used for helper methods
+ enum : uint32_t {
+ /// \hideinitializer
+ flexSize = _C2FlexHelper<S>::flexSize, ///< size of flexible type
+ /// \hideinitializer
+ maxSize = (uint32_t)std::min((size_t)UINT32_MAX, SIZE_MAX), // TODO: is this always u32 max?
+ /// \hideinitializer
+ baseSize = sizeof(S) + sizeof(C2Param), ///< size of the base param
+ };
+
+ /// returns the allocated size of this param with flexCount, or 0 if it would overflow.
+ inline static size_t calcSize(size_t flexCount, size_t size = baseSize) {
+ if (flexCount <= (maxSize - size) / S::flexSize) {
+ return size + S::flexSize * flexCount;
+ }
+ return 0;
+ }
+
+ /// dynamic new operator usable for params of type S
+ inline void* operator new(size_t size, size_t flexCount) noexcept {
+ // TODO: assert(size == baseSize);
+ size = calcSize(flexCount, size);
+ if (size > 0) {
+ return ::operator new(size);
+ }
+ return nullptr;
+ }
+};
+
+// TODO: this probably does not work.
+/// Expose fieldList from subClass;
+template<typename S, int BaseIndex, unsigned TypeIndex>
+const std::initializer_list<const C2FieldDescriptor> C2FlexStructCheck<S, BaseIndex, TypeIndex>::fieldList = S::fieldList;
+
+/// Define From() cast operators for params.
+#define DEFINE_CAST_OPERATORS(_type) \
+ inline static _type* From(C2Param *other) { \
+ return (_type*)C2Param::ifSuitable( \
+ other, sizeof(_type),_type::typeIndex, _type::flexSize, \
+ (_type::typeIndex & T::Index::kDirUndefined) != T::Index::kDirUndefined); \
+ } \
+ inline static const _type* From(const C2Param *other) { \
+ return const_cast<const _type*>(From(const_cast<C2Param *>(other))); \
+ } \
+ inline static _type* From(std::nullptr_t) { return nullptr; } \
+
+/**
+ * Define flexible allocators (alloc_shared or alloc_unique) for flexible params.
+ * - P::alloc_xyz(flexCount, args...): allocate for given flex-count.
+ * - P::alloc_xyz(args..., T[]): allocate for size of (and with) init array.
+ * - P::alloc_xyz(T[]): allocate for size of (and with) init array with no other args.
+ * - P::alloc_xyz(args..., std::initializer_list<T>): allocate for size of (and with) initializer
+ * list.
+ */
+#define DEFINE_FLEXIBLE_ALLOC(_type, S, ptr) \
+ template<typename ...Args> \
+ inline static std::ptr##_ptr<_type> alloc_##ptr(size_t flexCount, const Args(&... args)) { \
+ return std::ptr##_ptr<_type>(new(flexCount) _type(flexCount, args...)); \
+ } \
+ /* NOTE: unfortunately this is not supported by clang yet */ \
+ template<typename ...Args, typename U=typename S::flexType, unsigned N> \
+ inline static std::ptr##_ptr<_type> alloc_##ptr(const Args(&... args), const U(&init)[N]) { \
+ return std::ptr##_ptr<_type>(new(N) _type(N, args..., init)); \
+ } \
+ /* so for now, specialize for no args */ \
+ template<typename U=typename S::flexType, unsigned N> \
+ inline static std::ptr##_ptr<_type> alloc_##ptr(const U(&init)[N]) { \
+ return std::ptr##_ptr<_type>(new(N) _type(N, init)); \
+ } \
+ template<typename ...Args, typename U=typename S::flexType> \
+ inline static std::ptr##_ptr<_type> alloc_##ptr( \
+ const Args(&... args), const std::initializer_list<U> &init) { \
+ return std::ptr##_ptr<_type>(new(init.size()) _type(init.size(), args..., init)); \
+ } \
+
+/**
+ * Define flexible methods alloc_shared, alloc_unique and flexCount.
+ */
+#define DEFINE_FLEXIBLE_METHODS(_type, S) \
+ DEFINE_FLEXIBLE_ALLOC(_type, S, shared) \
+ DEFINE_FLEXIBLE_ALLOC(_type, S, unique) \
+ inline size_t flexCount() const { \
+ static_assert(sizeof(_type) == _type::baseSize, "incorrect baseSize"); \
+ size_t sz = this->size(); \
+ if (sz >= sizeof(_type)) { \
+ return (sz - sizeof(_type)) / _type::flexSize; \
+ } \
+ return 0; \
+ } \
+
+/// Mark flexible member variable and make structure flexible.
+#define FLEX(cls, m) \
+ C2_DO_NOT_COPY(cls) \
+private: \
+ C2PARAM_MAKE_FRIENDS \
+ /* default constructor with flexCount */ \
+ inline cls(size_t) : cls() {} \
+ /** \if 0 */ \
+ template<typename, typename> friend struct _C2FlexHelper; \
+ typedef decltype(m) flexMemberType; \
+public: \
+ /* constexpr static flexMemberType cls::* flexMember = &cls::m; */ \
+ typedef typename _C2FlexHelper<flexMemberType>::flexType flexType; \
+ static_assert(\
+ !std::is_void<flexType>::value, \
+ "member is not flexible, or a flexible array of a flexible type"); \
+ enum : uint32_t { flexSize = _C2FlexHelper<flexMemberType>::flexSize }; \
+ /** \endif */ \
+
+/// @}
+
+/**
+ * Global-parameter template.
+ *
+ * Base template to define a global setting/tuning or info based on a structure and
+ * an optional BaseIndex. Global parameters are not tied to a port (input or output).
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2GlobalParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2StructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirGlobal> {
+private:
+ typedef C2GlobalParam<T, S, BaseIndex> _type;
+
+public:
+ /// Wrapper around base structure's constructor.
+ template<typename ...Args>
+ inline C2GlobalParam(const Args(&... args)) : T(sizeof(_type), _type::typeIndex), S(args...) { }
+
+ DEFINE_CAST_OPERATORS(_type)
+};
+
+/**
+ * Global-parameter template for flexible structures.
+ *
+ * Base template to define a global setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Global parameters are not tied to a port (input or output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2GlobalParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+ : public T, public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirGlobal> {
+private:
+ typedef C2GlobalParam<T, S, BaseIndex> _type;
+
+ /// Wrapper around base structure's constructor.
+ template<typename ...Args>
+ inline C2GlobalParam(size_t flexCount, const Args(&... args))
+ : T(_type::calcSize(flexCount), _type::typeIndex), m(flexCount, args...) { }
+
+public:
+ S m; ///< wrapped flexible structure
+
+ DEFINE_FLEXIBLE_METHODS(_type, S)
+ DEFINE_CAST_OPERATORS(_type)
+};
+
+/**
+ * Port-parameter template.
+ *
+ * Base template to define a port setting/tuning or info based on a structure and
+ * an optional BaseIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2PortParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ private C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirUndefined> {
+private:
+ typedef C2PortParam<T, S, BaseIndex> _type;
+
+public:
+ /// Default constructor.
+ inline C2PortParam() : T(sizeof(_type), _type::typeIndex) { }
+ template<typename ...Args>
+ /// Wrapper around base structure's constructor while specifying port/direction.
+ inline C2PortParam(bool _output, const Args(&... args))
+ : T(sizeof(_type), _output ? output::typeIndex : input::typeIndex), S(args...) { }
+ /// Set port/direction.
+ inline void setPort(bool output) { C2Param::setPort(output); }
+
+ DEFINE_CAST_OPERATORS(_type)
+
+ /// Specialization for an input port parameter.
+ struct input : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirInput> {
+ /// Wrapper around base structure's constructor.
+ template<typename ...Args>
+ inline input(const Args(&... args)) : T(sizeof(_type), input::typeIndex), S(args...) { }
+
+ DEFINE_CAST_OPERATORS(input)
+
+ };
+
+ /// Specialization for an output port parameter.
+ struct output : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirOutput> {
+ /// Wrapper around base structure's constructor.
+ template<typename ...Args>
+ inline output(const Args(&... args)) : T(sizeof(_type), output::typeIndex), S(args...) { }
+
+ DEFINE_CAST_OPERATORS(output)
+ };
+};
+
+/**
+ * Port-parameter template for flexible structures.
+ *
+ * Base template to define a port setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2PortParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+ : public T, public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirUndefined> {
+private:
+ typedef C2PortParam<T, S, BaseIndex> _type;
+
+ /// Default constructor for basic allocation: new(flexCount) P.
+ inline C2PortParam(size_t flexCount) : T(_type::calcSize(flexCount), _type::typeIndex) { }
+ template<typename ...Args>
+ /// Wrapper around base structure's constructor while also specifying port/direction.
+ inline C2PortParam(size_t flexCount, bool _output, const Args(&... args))
+ : T(_type::calcSize(flexCount), _output ? output::typeIndex : input::typeIndex),
+ m(flexCount, args...) { }
+
+public:
+ /// Set port/direction.
+ inline void setPort(bool output) { C2Param::setPort(output); }
+
+ S m; ///< wrapped flexible structure
+
+ DEFINE_FLEXIBLE_METHODS(_type, S)
+ DEFINE_CAST_OPERATORS(_type)
+
+ /// Specialization for an input port parameter.
+ struct input : public T, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirInput> {
+ private:
+ /// Wrapper around base structure's constructor while also specifying port/direction.
+ template<typename ...Args>
+ inline input(size_t flexCount, const Args(&... args))
+ : T(_type::calcSize(flexCount), input::typeIndex), m(flexCount, args...) { }
+
+ public:
+ S m; ///< wrapped flexible structure
+
+ DEFINE_FLEXIBLE_METHODS(input, S)
+ DEFINE_CAST_OPERATORS(input)
+ };
+
+ /// Specialization for an output port parameter.
+ struct output : public T, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirOutput> {
+ private:
+ /// Wrapper around base structure's constructor while also specifying port/direction.
+ template<typename ...Args>
+ inline output(size_t flexCount, const Args(&... args))
+ : T(_type::calcSize(flexCount), output::typeIndex), m(flexCount, args...) { }
+
+ public:
+ S m; ///< wrapped flexible structure
+
+ DEFINE_FLEXIBLE_METHODS(output, S)
+ DEFINE_CAST_OPERATORS(output)
+ };
+};
+
+/**
+ * Stream-parameter template.
+ *
+ * Base template to define a stream setting/tuning or info based on a structure and
+ * an optional BaseIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2StreamParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ private C2StructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Index::kDirUndefined> {
+private:
+ typedef C2StreamParam<T, S, BaseIndex> _type;
+
+public:
+ /// Default constructor. Port/direction and stream-ID is undefined.
+ inline C2StreamParam() : T(sizeof(_type), _type::typeIndex) { }
+ /// Wrapper around base structure's constructor while also specifying port/direction and
+ /// stream-ID.
+ template<typename ...Args>
+ inline C2StreamParam(bool _output, unsigned stream, const Args(&... args))
+ : T(sizeof(_type), _output ? output::typeIndex : input::typeIndex, stream),
+ S(args...) { }
+ /// Set port/direction.
+ inline void setPort(bool output) { C2Param::setPort(output); }
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_CAST_OPERATORS(_type)
+
+ /// Specialization for an input stream parameter.
+ struct input : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2StructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Type::kDirInput> {
+ /// Default constructor. Stream-ID is undefined.
+ inline input() : T(sizeof(_type), input::typeIndex) { }
+ /// Wrapper around base structure's constructor while also specifying stream-ID.
+ template<typename ...Args>
+ inline input(unsigned stream, const Args(&... args))
+ : T(sizeof(_type), input::typeIndex, stream), S(args...) { }
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_CAST_OPERATORS(input)
+ };
+
+ /// Specialization for an output stream parameter.
+ struct output : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2StructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Type::kDirOutput> {
+ /// Default constructor. Stream-ID is undefined.
+ inline output() : T(sizeof(_type), output::typeIndex) { }
+ /// Wrapper around base structure's constructor while also specifying stream-ID.
+ template<typename ...Args>
+ inline output(unsigned stream, const Args(&... args))
+ : T(sizeof(_type), output::typeIndex, stream), S(args...) { }
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_CAST_OPERATORS(output)
+ };
+};
+
+/**
+ * Stream-parameter template for flexible structures.
+ *
+ * Base template to define a stream setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2StreamParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+ : public T, public C2BaseIndexOverride<S, BaseIndex>,
+ private C2FlexStructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Index::kDirUndefined> {
+private:
+ typedef C2StreamParam<T, S> _type;
+ /// Default constructor. Port/direction and stream-ID is undefined.
+ inline C2StreamParam(size_t flexCount) : T(_type::calcSize(flexCount), _type::typeIndex, 0u) { }
+ /// Wrapper around base structure's constructor while also specifying port/direction and
+ /// stream-ID.
+ template<typename ...Args>
+ inline C2StreamParam(size_t flexCount, bool _output, unsigned stream, const Args(&... args))
+ : T(_type::calcSize(flexCount), _output ? output::typeIndex : input::typeIndex, stream),
+ m(flexCount, args...) { }
+
+public:
+ S m; ///< wrapped flexible structure
+
+ /// Set port/direction.
+ inline void setPort(bool output) { C2Param::setPort(output); }
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_FLEXIBLE_METHODS(_type, S)
+ DEFINE_CAST_OPERATORS(_type)
+
+ /// Specialization for an input stream parameter.
+ struct input : public T, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2FlexStructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Type::kDirInput> {
+ private:
+ /// Default constructor. Stream-ID is undefined.
+ inline input(size_t flexCount) : T(_type::calcSize(flexCount), input::typeIndex) { }
+ /// Wrapper around base structure's constructor while also specifying stream-ID.
+ template<typename ...Args>
+ inline input(size_t flexCount, unsigned stream, const Args(&... args))
+ : T(_type::calcSize(flexCount), input::typeIndex, stream), m(flexCount, args...) { }
+
+ public:
+ S m; ///< wrapped flexible structure
+
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_FLEXIBLE_METHODS(input, S)
+ DEFINE_CAST_OPERATORS(input)
+ };
+
+ /// Specialization for an output stream parameter.
+ struct output : public T, public C2BaseIndexOverride<S, BaseIndex>,
+ public C2FlexStructCheck<S, BaseIndex,
+ T::indexFlags | T::Index::kStreamFlag | T::Type::kDirOutput> {
+ private:
+ /// Default constructor. Stream-ID is undefined.
+ inline output(size_t flexCount) : T(_type::calcSize(flexCount), output::typeIndex) { }
+ /// Wrapper around base structure's constructor while also specifying stream-ID.
+ template<typename ...Args>
+ inline output(size_t flexCount, unsigned stream, const Args(&... args))
+ : T(_type::calcSize(flexCount), output::typeIndex, stream), m(flexCount, args...) { }
+
+ public:
+ S m; ///< wrapped flexible structure
+
+ /// Set stream-id. \retval true if the stream-id was successfully set.
+ inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+ DEFINE_FLEXIBLE_METHODS(output, S)
+ DEFINE_CAST_OPERATORS(output)
+ };
+};
+
+/* ======================== SIMPLE VALUE PARAMETERS ======================== */
+
+/**
+ * \ingroup internal
+ * A structure template encapsulating a single element with default constructors and no base-index.
+ */
+template<typename T>
+struct C2SimpleValueStruct {
+ T mValue; ///< simple value of the structure
+ // Default constructor.
+ inline C2SimpleValueStruct() = default;
+ // Constructor with an initial value.
+ inline C2SimpleValueStruct(T value) : mValue(value) {}
+ DEFINE_C2STRUCT_NO_BASE(SimpleValue)
+};
+
+// TODO: move this and next to some generic place
+/**
+ * Interface to a block of (mapped) memory containing an array of some type (T).
+ */
+template<typename T>
+struct C2MemoryBlock {
+ /// \returns the number of elements in this block.
+ virtual size_t size() const = 0;
+ /// \returns a const pointer to the start of this block. Care must be taken to not read outside
+ /// the block.
+ virtual const T *data() const = 0; // TODO: should this be friend access only in some C2Memory module?
+ /// \returns a pointer to the start of this block. Care must be taken to not read or write
+ /// outside the block.
+ inline T *data() { return const_cast<T*>(data()); }
+protected:
+ // TODO: for now it should never be deleted as C2MemoryBlock
+ virtual ~C2MemoryBlock() = default;
+};
+
+/**
+ * Interface to a block of memory containing a constant (constexpr) array of some type (T).
+ */
+template<typename T>
+struct C2ConstMemoryBlock : public C2MemoryBlock<T> {
+ virtual const T * data() const { return mData; }
+ virtual size_t size() const { return mSize; }
+
+ /// Constructor.
+ template<unsigned N>
+ inline constexpr C2ConstMemoryBlock(const T(&init)[N]) : mData(init), mSize(N) {}
+
+private:
+ const T *mData;
+ const size_t mSize;
+};
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class to initialize flexible arrays with various initalizers.
+struct _C2ValueArrayHelper {
+ // char[]-s are used as null terminated strings, so the last element is never inited.
+
+ /// Initialize a flexible array using a constexpr memory block.
+ template<typename T>
+ static void init(T(&array)[], size_t arrayLen, const C2MemoryBlock<T> &block) {
+ // reserve last element for terminal 0 for strings
+ if (arrayLen && std::is_same<T, char>::value) {
+ --arrayLen;
+ }
+ if (block.data()) {
+ memcpy(array, block.data(), std::min(arrayLen, block.size()) * sizeof(T));
+ }
+ }
+
+ /// Initialize a flexible array using an initializer list.
+ template<typename T>
+ static void init(T(&array)[], size_t arrayLen, const std::initializer_list<T> &init) {
+ size_t ix = 0;
+ // reserve last element for terminal 0 for strings
+ if (arrayLen && std::is_same<T, char>::value) {
+ --arrayLen;
+ }
+ for (const T &item : init) {
+ if (ix == arrayLen) {
+ break;
+ }
+ array[ix++] = item;
+ }
+ }
+
+ /// Initialize a flexible array using another flexible array.
+ template<typename T, unsigned N>
+ static void init(T(&array)[], size_t arrayLen, const T(&str)[N]) {
+ // reserve last element for terminal 0 for strings
+ if (arrayLen && std::is_same<T, char>::value) {
+ --arrayLen;
+ }
+ if (arrayLen) {
+ strncpy(array, str, std::min(arrayLen, (size_t)N));
+ }
+ }
+};
+
+/**
+ * Specialization for a flexible blob and string arrays. A structure template encapsulating a single
+ * flexible array member with default flexible constructors and no base-index. This type cannot be
+ * constructed on its own as it's size is 0.
+ *
+ * \internal This is different from C2SimpleArrayStruct<T[]> simply because its member has the name
+ * as mValue to reflect this is a single value.
+ */
+template<typename T>
+struct C2SimpleValueStruct<T[]> {
+ static_assert(std::is_same<T, char>::value || std::is_same<T, uint8_t>::value,
+ "C2SimpleValueStruct<T[]> is only for BLOB or STRING");
+ T mValue[];
+
+ inline C2SimpleValueStruct() = default;
+ DEFINE_C2STRUCT_NO_BASE(SimpleValue)
+ FLEX(C2SimpleValueStruct, mValue)
+
+private:
+ inline C2SimpleValueStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+ _C2ValueArrayHelper::init(mValue, flexCount, block);
+ }
+
+ inline C2SimpleValueStruct(size_t flexCount, const std::initializer_list<T> &init) {
+ _C2ValueArrayHelper::init(mValue, flexCount, init);
+ }
+
+ template<unsigned N>
+ inline C2SimpleValueStruct(size_t flexCount, const T(&init)[N]) {
+ _C2ValueArrayHelper::init(mValue, flexCount, init);
+ }
+};
+
+/// @}
+
+/**
+ * A structure template encapsulating a single flexible array element of a specific type (T) with
+ * default constructors and no base-index. This type cannot be constructed on its own as it's size
+ * is 0. Instead, it is meant to be used as a parameter, e.g.
+ *
+ * typedef C2StreamParam<C2Info, C2SimpleArrayStruct<C2MyFancyStruct>,
+ * kParamIndexMyFancyArrayStreamParam> C2MyFancyArrayStreamInfo;
+ */
+template<typename T>
+struct C2SimpleArrayStruct {
+ static_assert(!std::is_same<T, char>::value && !std::is_same<T, uint8_t>::value,
+ "use C2SimpleValueStruct<T[]> is for BLOB or STRING");
+
+ T mValues[]; ///< array member
+ /// Default constructor
+ inline C2SimpleArrayStruct() = default;
+ DEFINE_C2STRUCT_NO_BASE(SimpleArray)
+ FLEX(C2SimpleArrayStruct, mValues)
+
+private:
+ /// Construct from a C2MemoryBlock.
+ /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ inline C2SimpleArrayStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+ _C2ValueArrayHelper::init(mValues, flexCount, block);
+ }
+
+ /// Construct from an initializer list.
+ /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ inline C2SimpleArrayStruct(size_t flexCount, const std::initializer_list<T> &init) {
+ _C2ValueArrayHelper::init(mValues, flexCount, init);
+ }
+
+ /// Construct from another flexible array.
+ /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+ template<unsigned N>
+ inline C2SimpleArrayStruct(size_t flexCount, const T(&init)[N]) {
+ _C2ValueArrayHelper::init(mValues, flexCount, init);
+ }
+};
+
+/**
+ * \addtogroup simplevalue Simple value and array structures.
+ * @{
+ *
+ * Simple value structures.
+ *
+ * Structures containing a single simple value. These can be reused to easily define simple
+ * parameters of various types:
+ *
+ * typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexMyIntegerPortParam>
+ * C2MyIntegerPortParamTuning;
+ *
+ * They contain a single member (mValue or mValues) that is described as "value" or "values".
+ */
+/// A 32-bit signed integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<int32_t> C2Int32Value;
+/// A 32-bit signed integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<int32_t> C2Int32Array;
+/// A 32-bit unsigned integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint32_t> C2Uint32Value;
+/// A 32-bit unsigned integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<uint32_t> C2Uint32Array;
+/// A 64-bit signed integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<int64_t> C2Int64Value;
+/// A 64-bit signed integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<int64_t> C2Int64Array;
+/// A 64-bit unsigned integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint64_t> C2Uint64Value;
+/// A 64-bit unsigned integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<uint64_t> C2Uint64Array;
+/// A float parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<float> C2FloatValue;
+/// A float array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<float> C2FloatArray;
+/// A blob flexible parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint8_t[]> C2BlobValue;
+/// A string flexible parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<char[]> C2StringValue;
+
+#if 1
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleValueStruct<T>::fieldList = { C2FIELD(mValue, "value") };
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleValueStruct<T[]>::fieldList = { C2FIELD(mValue, "value") };
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleArrayStruct<T>::fieldList = { C2FIELD(mValues, "values") };
+#else
+// This seem to be able to be handled by the template above
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<int32_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint32_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<int64_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint64_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<float>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint8_t[]>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<char[]>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<int32_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<uint32_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<int64_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<uint64_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<float>, { C2FIELD(mValues, "values") });
+#endif
+
+/// @}
+
+/// @}
+
+} // namespace android
+
+#endif // C2PARAM_DEF_H_
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
new file mode 100644
index 0000000..a42d11a
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 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 C2WORK_H_
+
+#define C2WORK_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <C2Param.h>
+#include <C2Buffer.h>
+#include <C2Config.h>
+
+#include <memory>
+#include <list>
+#include <vector>
+
+typedef int status_t;
+
+namespace android {
+
+/// \defgroup work Work and data processing
+/// @{
+
+struct C2SettingResult {
+ enum Failure {
+ READ_ONLY, ///< parameter is read-only and cannot be set
+ MISMATCH, ///< parameter mismatches input data
+ BAD_VALUE, ///< parameter does not accept value
+ BAD_TYPE, ///< parameter is not supported
+ BAD_PORT, ///< parameter is not supported on the specific port
+ BAD_INDEX, ///< parameter is not supported on the specific stream
+ CONFLICT, ///< parameter is in conflict with another setting
+ };
+
+ C2ParamField field;
+ Failure failure;
+ std::unique_ptr<C2FieldSupportedValues> supportedValues; //< if different from normal (e.g. in conflict w/another param or input data)
+ std::list<C2ParamField> conflictingFields;
+};
+
+// ================================================================================================
+// WORK
+// ================================================================================================
+
+// node_id-s
+typedef uint32_t node_id;
+
+enum flags_t : uint32_t {
+ BUFFERFLAG_CODEC_CONFIG,
+ BUFFERFLAG_DROP_FRAME,
+ BUFFERFLAG_END_OF_STREAM,
+};
+
+enum {
+ kParamIndexWorkOrdinal,
+};
+
+struct C2WorkOrdinalStruct {
+ uint64_t timestamp;
+ uint64_t frame_index; // submission ordinal on the initial component
+ uint64_t custom_ordinal; // can be given by the component, e.g. decode order
+
+ DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
+ C2FIELD(timestamp, "timestamp")
+ C2FIELD(frame_index, "frame-index")
+ C2FIELD(custom_ordinal, "custom-ordinal")
+};
+
+struct C2BufferPack {
+//public:
+ flags_t flags;
+ C2WorkOrdinalStruct ordinal;
+ std::vector<std::shared_ptr<C2Buffer>> buffers;
+ //< for initial work item, these may also come from the parser - if provided
+ //< for output buffers, these are the responses to requestedInfos
+ std::list<std::unique_ptr<C2Info>> infos;
+ std::list<std::shared_ptr<C2InfoBuffer>> infoBuffers;
+};
+
+struct C2Worklet {
+//public:
+ // IN
+ node_id component;
+
+ std::list<std::unique_ptr<C2Param>> tunings; //< tunings to be applied before processing this
+ // worklet
+ std::list<C2Param::Type> requestedInfos;
+ std::vector<std::shared_ptr<C2BlockAllocator>> allocators; //< This vector shall be the same size as
+ //< output.buffers.
+
+ // OUT
+ C2BufferPack output;
+ std::list<std::unique_ptr<C2SettingResult>> failures;
+};
+
+/**
+ * This structure holds information about all a single work item.
+ *
+ * This structure shall be passed by the client to the component for the first worklet. As such,
+ * worklets must not be empty. The ownership of this object is passed.
+ *
+ * input:
+ * The input data to be processed. This is provided by the client with ownership. When the work
+ * is returned, the input buffer-pack's buffer vector shall contain nullptrs.
+ *
+ * worklets:
+ * The chain of components and associated allocators, tunings and info requests that the data
+ * must pass through. If this has more than a single element, the tunnels between successive
+ * components of the worklet chain must have been (successfully) pre-registered at the time
+ * the work is submitted. Allocating the output buffers in the worklets is the responsibility
+ * of each component. Upon work submission, each output buffer-pack shall be an appropriately
+ * sized vector containing nullptrs. When the work is completed/returned to the client,
+ *
+ * worklets_processed:
+ * It shall be initialized to 0 by the client when the work is submitted.
+ * It shall contain the number of worklets that were successfully processed when the work is
+ * returned. If this is less then the number of worklets, result must not be success.
+ * It must be in the range of [0, worklets.size()].
+ *
+ * result:
+ * The final outcome of the work. If 0 when work is returned, it is assumed that all worklets
+ * have been processed.
+ */
+struct C2Work {
+//public:
+ // pre-chain infos (for portions of a tunneling chain that happend before this work-chain for
+ // this work item - due to framework facilitated (non-tunneled) work-chaining)
+ std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Info>>> preChainInfos;
+ std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Buffer>>> preChainInfoBlobs;
+
+ C2BufferPack input;
+ std::list<std::unique_ptr<C2Worklet>> worklets;
+
+ uint32_t worklets_processed;
+ status_t result;
+};
+
+struct C2WorkOutline {
+//public:
+ C2WorkOrdinalStruct ordinal;
+ std::list<node_id> chain;
+};
+
+/// @}
+
+} // namespace android
+
+#endif // C2WORK_H_
diff --git a/media/libstagefright/codec2/tests/Android.mk b/media/libstagefright/codec2/tests/Android.mk
new file mode 100644
index 0000000..49c4253
--- /dev/null
+++ b/media/libstagefright/codec2/tests/Android.mk
@@ -0,0 +1,37 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := codec2_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ vndk/C2UtilTest.cpp \
+ C2_test.cpp \
+ C2Param_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libstagefright_codec2 \
+ liblog
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/libstagefright/codec2/include \
+ frameworks/av/media/libstagefright/codec2/vndk/include \
+ $(TOP)/frameworks/native/include/media/openmax \
+
+LOCAL_CFLAGS += -Werror -Wall -std=c++14
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
new file mode 100644
index 0000000..ec82c84
--- /dev/null
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -0,0 +1,2687 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2Param_test"
+
+#include <gtest/gtest.h>
+
+#include <util/C2ParamUtils.h>
+#include <C2ParamDef.h>
+
+namespace android {
+
+void PrintTo(const _C2FieldId &id, ::std::ostream* os) {
+ *os << "@" << id._mOffset << "+" << id._mSize;
+}
+
+void PrintTo(const C2FieldDescriptor &fd, ::std::ostream *os) {
+ using FD=C2FieldDescriptor;
+ switch (fd.type()) {
+ case FD::INT32: *os << "i32"; break;
+ case FD::INT64: *os << "i64"; break;
+ case FD::UINT32: *os << "u32"; break;
+ case FD::UINT64: *os << "u64"; break;
+ case FD::FLOAT: *os << "float"; break;
+ case FD::STRING: *os << "char"; break;
+ case FD::BLOB: *os << "u8"; break;
+ default:
+ if (fd.type() & FD::STRUCT_FLAG) {
+ *os << "struct-" << (fd.type() & ~FD::STRUCT_FLAG);
+ } else {
+ *os << "type-" << fd.type();
+ }
+ }
+ *os << " " << fd.name();
+ if (fd.length() > 1) {
+ *os << "[" << fd.length() << "]";
+ } else if (fd.length() == 0) {
+ *os << "[]";
+ }
+ *os << " (";
+ PrintTo(fd._mFieldId, os);
+ *os << "*" << fd.length() << ")";
+}
+
+enum C2ParamIndexType {
+ kParamIndexNumber,
+ kParamIndexNumbers,
+ kParamIndexNumber2,
+ kParamIndexVendorStart = C2Param::BaseIndex::kVendorStart,
+ kParamIndexVendorNumbers,
+};
+
+void ffff(int(*)(int)) {}
+
+/* ============================= STRUCT DECLARATION AND DESCRIPTION ============================= */
+
+typedef C2FieldDescriptor FD;
+
+class C2ParamTest : public ::testing::Test {
+};
+
+class C2ParamTest_ParamFieldList
+ : public ::testing::TestWithParam<std::initializer_list<const C2FieldDescriptor>> {
+};
+
+enum {
+ kParamIndexSize,
+ kParamIndexTestA,
+ kParamIndexTestB,
+ kParamIndexTestFlexS32,
+ kParamIndexTestFlexEndS32,
+ kParamIndexTestFlexS64,
+ kParamIndexTestFlexEndS64,
+ kParamIndexTestFlexSize,
+ kParamIndexTestFlexEndSize,
+};
+
+struct C2SizeStruct {
+ int32_t mNumber;
+ int32_t mHeight;
+ enum : uint32_t { baseIndex = kParamIndexSize }; // <= needed for C2FieldDescriptor
+ const static std::initializer_list<const C2FieldDescriptor> fieldList; // <= needed for C2FieldDescriptor
+ const static FD::Type TYPE = (FD::Type)(baseIndex | FD::STRUCT_FLAG);
+};
+
+DEFINE_NO_NAMED_VALUES_FOR(C2SizeStruct)
+
+// Test 1. define a structure without any helper methods
+
+bool operator==(const C2FieldDescriptor &a, const C2FieldDescriptor &b) {
+ return a.type() == b.type()
+ && a.length() == b.length()
+ && strcmp(a.name(), b.name()) == 0
+ && a._mFieldId == b._mFieldId;
+}
+
+struct C2TestStruct_A {
+ int32_t mSigned32;
+ int64_t mSigned64[2];
+ uint32_t mUnsigned32[1];
+ uint64_t mUnsigned64;
+ float mFloat;
+ C2SizeStruct mSize[3];
+ uint8_t mBlob[100];
+ char mString[100];
+ bool mYesNo[100];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = kParamIndexTest };
+ // typedef C2TestStruct_A _type;
+} __attribute__((packed));
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A::fieldList =
+ { { FD::INT32, 1, "s32", 0, 4 },
+ { FD::INT64, 2, "s64", 4, 8 },
+ { FD::UINT32, 1, "u32", 20, 4 },
+ { FD::UINT64, 1, "u64", 24, 8 },
+ { FD::FLOAT, 1, "fp", 32, 4 },
+ { C2SizeStruct::TYPE, 3, "size", 36, 8 },
+ { FD::BLOB, 100, "blob", 60, 1 },
+ { FD::STRING, 100, "str", 160, 1 },
+ { FD::BLOB, 100, "y-n", 260, 1 } };
+
+TEST_P(C2ParamTest_ParamFieldList, VerifyStruct) {
+ std::vector<const C2FieldDescriptor> fields = GetParam(), expected = C2TestStruct_A::fieldList;
+
+ // verify first field descriptor
+ EXPECT_EQ(FD::INT32, fields[0].type());
+ EXPECT_STREQ("s32", fields[0].name());
+ EXPECT_EQ(1u, fields[0].length());
+ EXPECT_EQ(_C2FieldId(0, 4), fields[0]._mFieldId);
+
+ EXPECT_EQ(expected[0], fields[0]);
+ EXPECT_EQ(expected[1], fields[1]);
+ EXPECT_EQ(expected[2], fields[2]);
+ EXPECT_EQ(expected[3], fields[3]);
+ EXPECT_EQ(expected[4], fields[4]);
+ EXPECT_EQ(expected[5], fields[5]);
+ EXPECT_EQ(expected[6], fields[6]);
+ EXPECT_EQ(expected[7], fields[7]);
+ for (size_t i = 8; i < fields.size() && i < expected.size(); ++i) {
+ EXPECT_EQ(expected[i], fields[i]);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(InitializerList, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A::fieldList));
+
+// define fields using C2FieldDescriptor pointer constructor
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A_FD_PTR_fieldList =
+ { C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSigned32, "s32"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSigned64, "s64"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mUnsigned32, "u32"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mUnsigned64, "u64"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mFloat, "fp"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSize, "size"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mBlob, "blob"),
+ C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mString, "str"),
+ // C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mYesNo, "y-n")
+ };
+
+INSTANTIATE_TEST_CASE_P(PointerConstructor, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A_FD_PTR_fieldList));
+
+// define fields using C2FieldDescriptor member-pointer constructor
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A_FD_MEM_PTR_fieldList =
+ { C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSigned32, "s32"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSigned64, "s64"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned32, "u32"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned64, "u64"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mFloat, "fp"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSize, "size"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mBlob, "blob"),
+ C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mString, "str"),
+ // C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mYesNo, "y-n")
+ };
+
+INSTANTIATE_TEST_CASE_P(MemberPointerConstructor, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A_FD_MEM_PTR_fieldList));
+
+// Test 2. define a structure with two-step helper methods
+
+struct C2TestAStruct {
+ int32_t mSigned32;
+ int64_t mSigned64[2];
+ uint32_t mUnsigned32[1];
+ uint64_t mUnsigned64;
+ float mFloat;
+ C2SizeStruct mSize[3];
+ uint8_t mBlob[100];
+ char mString[100];
+ bool mYesNo[100];
+
+private: // test access level
+ DEFINE_C2STRUCT(TestA)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestA, {
+ C2FIELD(mSigned32, "s32")
+ C2FIELD(mSigned64, "s64")
+ C2FIELD(mUnsigned32, "u32")
+ C2FIELD(mUnsigned64, "u64")
+ C2FIELD(mFloat, "fp")
+ C2FIELD(mSize, "size")
+ C2FIELD(mBlob, "blob")
+ C2FIELD(mString, "str")
+ // C2FIELD(mYesNo, "y-n")
+}) // ; optional
+
+INSTANTIATE_TEST_CASE_P(DescribeStruct2Step, C2ParamTest_ParamFieldList, ::testing::Values(C2TestAStruct::fieldList));
+
+// Test 3. define a structure with one-step helper method
+
+struct C2TestBStruct {
+ int32_t mSigned32;
+ int64_t mSigned64[2];
+ uint32_t mUnsigned32[1];
+ uint64_t mUnsigned64;
+ float mFloat;
+ C2SizeStruct mSize[3];
+ uint8_t mBlob[100];
+ char mString[100];
+ bool mYesNo[100];
+
+private: // test access level
+ DEFINE_AND_DESCRIBE_C2STRUCT(TestB)
+
+ C2FIELD(mSigned32, "s32")
+ C2FIELD(mSigned64, "s64")
+ C2FIELD(mUnsigned32, "u32")
+ C2FIELD(mUnsigned64, "u64")
+ C2FIELD(mFloat, "fp")
+ C2FIELD(mSize, "size")
+ C2FIELD(mBlob, "blob")
+ C2FIELD(mString, "str")
+ // C2FIELD(mYesNo, "y-n")
+};
+
+INSTANTIATE_TEST_CASE_P(DescribeStruct1Step, C2ParamTest_ParamFieldList, ::testing::Values(C2TestBStruct::fieldList));
+
+// Test 4. flexible members
+
+template<typename T>
+class C2ParamTest_FlexParamFieldList : public ::testing::Test {
+protected:
+ using Type=FD::Type;
+
+ // static std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+ static std::vector<std::vector<const C2FieldDescriptor>>
+ GetLists();
+
+ constexpr static Type flexType =
+ std::is_same<T, int32_t>::value ? FD::INT32 :
+ std::is_same<T, int64_t>::value ? FD::INT64 :
+ std::is_same<T, uint32_t>::value ? FD::UINT32 :
+ std::is_same<T, uint64_t>::value ? FD::UINT64 :
+ std::is_same<T, float>::value ? FD::FLOAT :
+ std::is_same<T, uint8_t>::value ? FD::BLOB :
+ std::is_same<T, char>::value ? FD::STRING :
+ std::is_same<T, C2SizeStruct>::value ? C2SizeStruct::TYPE : (Type)0;
+ constexpr static size_t flexSize = sizeof(T);
+};
+
+typedef ::testing::Types<int32_t, int64_t, C2SizeStruct> FlexTypes;
+TYPED_TEST_CASE(C2ParamTest_FlexParamFieldList, FlexTypes);
+
+TYPED_TEST(C2ParamTest_FlexParamFieldList, VerifyStruct) {
+ for (auto a : this->GetLists()) {
+ std::vector<const C2FieldDescriptor> fields = a;
+ if (fields.size() > 1) {
+ EXPECT_EQ(2u, fields.size());
+ EXPECT_EQ(C2FieldDescriptor(FD::INT32, 1, "s32", 0, 4), fields[0]);
+ EXPECT_EQ(C2FieldDescriptor(this->flexType, 0, "flex", 4, this->flexSize),
+ fields[1]);
+ } else {
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(C2FieldDescriptor(this->flexType, 0, "flex", 0, this->flexSize),
+ fields[0]);
+ }
+ }
+}
+
+struct C2TestStruct_FlexS32 {
+ int32_t mFlex[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = kParamIndexTestFlex, flexSize = 4 };
+ // typedef C2TestStruct_FlexS32 _type;
+ // typedef int32_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexS32::fieldList = {
+ { FD::INT32, 0, "flex", 0, 4 }
+};
+
+struct C2TestStruct_FlexEndS32 {
+ int32_t mSigned32;
+ int32_t mFlex[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = kParamIndexTestFlexEnd, flexSize = 4 };
+ // typedef C2TestStruct_FlexEnd _type;
+ // typedef int32_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS32::fieldList = {
+ { FD::INT32, 1, "s32", 0, 4 },
+ { FD::INT32, 0, "flex", 4, 4 },
+};
+
+const static std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS32_ptr_fieldList = {
+ C2FieldDescriptor(&((C2TestStruct_FlexEndS32*)0)->mSigned32, "s32"),
+ C2FieldDescriptor(&((C2TestStruct_FlexEndS32*)0)->mFlex, "flex"),
+};
+
+struct C2TestFlexS32Struct {
+ int32_t mFlexSigned32[];
+private: // test access level
+ C2TestFlexS32Struct() {}
+
+ DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexS32, mFlexSigned32)
+ C2FIELD(mFlexSigned32, "flex")
+};
+
+struct C2TestFlexEndS32Struct {
+ int32_t mSigned32;
+ int32_t mFlexSigned32[];
+private: // test access level
+ C2TestFlexEndS32Struct() {}
+
+ DEFINE_FLEX_C2STRUCT(TestFlexEndS32, mFlexSigned32)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndS32, {
+ C2FIELD(mSigned32, "s32")
+ C2FIELD(mFlexSigned32, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<int32_t>::GetLists() {
+ return {
+ C2TestStruct_FlexS32::fieldList,
+ C2TestStruct_FlexEndS32::fieldList,
+ C2TestStruct_FlexEndS32_ptr_fieldList,
+ C2TestFlexS32Struct::fieldList,
+ C2TestFlexEndS32Struct::fieldList,
+ };
+}
+
+struct C2TestStruct_FlexS64 {
+ int64_t mFlexSigned64[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = kParamIndexTestFlexS64, flexSize = 8 };
+ // typedef C2TestStruct_FlexS64 _type;
+ // typedef int64_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexS64::fieldList = {
+ { FD::INT64, 0, "flex", 0, 8 }
+};
+
+struct C2TestStruct_FlexEndS64 {
+ int32_t mSigned32;
+ int64_t mSigned64Flex[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = C2TestStruct_FlexEndS64, flexSize = 8 };
+ // typedef C2TestStruct_FlexEndS64 _type;
+ // typedef int64_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS64::fieldList = {
+ { FD::INT32, 1, "s32", 0, 4 },
+ { FD::INT64, 0, "flex", 4, 8 },
+};
+
+struct C2TestFlexS64Struct {
+ int64_t mFlexSigned64[];
+ C2TestFlexS64Struct() {}
+
+ DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexS64, mFlexSigned64)
+ C2FIELD(mFlexSigned64, "flex")
+};
+
+struct C2TestFlexEndS64Struct {
+ int32_t mSigned32;
+ int64_t mFlexSigned64[];
+ C2TestFlexEndS64Struct() {}
+
+ DEFINE_FLEX_C2STRUCT(TestFlexEndS64, mFlexSigned64)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndS64, {
+ C2FIELD(mSigned32, "s32")
+ C2FIELD(mFlexSigned64, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<int64_t>::GetLists() {
+ return {
+ C2TestStruct_FlexS64::fieldList,
+ C2TestStruct_FlexEndS64::fieldList,
+ C2TestFlexS64Struct::fieldList,
+ C2TestFlexEndS64Struct::fieldList,
+ };
+}
+
+struct C2TestStruct_FlexSize {
+ C2SizeStruct mFlexSize[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = kParamIndexTestFlexSize, flexSize = 8 };
+ // typedef C2TestStruct_FlexSize _type;
+ // typedef C2SizeStruct flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexSize::fieldList = {
+ { C2SizeStruct::TYPE, 0, "flex", 0, sizeof(C2SizeStruct) }
+};
+
+struct C2TestStruct_FlexEndSize {
+ int32_t mSigned32;
+ C2SizeStruct mSizeFlex[];
+
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ // enum : uint32_t { baseIndex = C2TestStruct_FlexEndSize, flexSize = 8 };
+ // typedef C2TestStruct_FlexEndSize _type;
+ // typedef C2SizeStruct flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndSize::fieldList = {
+ { FD::INT32, 1, "s32", 0, 4 },
+ { C2SizeStruct::TYPE, 0, "flex", 4, sizeof(C2SizeStruct) },
+};
+
+struct C2TestFlexSizeStruct {
+ C2SizeStruct mFlexSize[];
+ C2TestFlexSizeStruct() {}
+
+ DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexSize, mFlexSize)
+ C2FIELD(mFlexSize, "flex")
+};
+
+struct C2TestFlexEndSizeStruct {
+ int32_t mSigned32;
+ C2SizeStruct mFlexSize[];
+ C2TestFlexEndSizeStruct() {}
+
+ DEFINE_FLEX_C2STRUCT(TestFlexEndSize, mFlexSize)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndSize, {
+ C2FIELD(mSigned32, "s32")
+ C2FIELD(mFlexSize, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<C2SizeStruct>::GetLists() {
+ return {
+ C2TestStruct_FlexSize::fieldList,
+ C2TestStruct_FlexEndSize::fieldList,
+ C2TestFlexSizeStruct::fieldList,
+ C2TestFlexEndSizeStruct::fieldList,
+ };
+}
+
+TEST_F(C2ParamTest, FieldId) {
+ // pointer constructor
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&((C2TestStruct_A*)0)->mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&((C2TestStruct_A*)0)->mSigned64));
+ EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId(&((C2TestStruct_A*)0)->mUnsigned32));
+ EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId(&((C2TestStruct_A*)0)->mUnsigned64));
+ EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId(&((C2TestStruct_A*)0)->mFloat));
+ EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId(&((C2TestStruct_A*)0)->mSize));
+ EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId(&((C2TestStruct_A*)0)->mBlob));
+ EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId(&((C2TestStruct_A*)0)->mString));
+ EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId(&((C2TestStruct_A*)0)->mYesNo));
+
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&((C2TestFlexEndSizeStruct*)0)->mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&((C2TestFlexEndSizeStruct*)0)->mFlexSize));
+
+ // member pointer constructor
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSigned64));
+ EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned32));
+ EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned64));
+ EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mFloat));
+ EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSize));
+ EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mBlob));
+ EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mString));
+ EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mYesNo));
+
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId((C2TestFlexEndSizeStruct*)0, &C2TestFlexEndSizeStruct::mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId((C2TestFlexEndSizeStruct*)0, &C2TestFlexEndSizeStruct::mFlexSize));
+
+ // member pointer sans type pointer
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&C2TestStruct_A::mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&C2TestStruct_A::mSigned64));
+ EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId(&C2TestStruct_A::mUnsigned32));
+ EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId(&C2TestStruct_A::mUnsigned64));
+ EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId(&C2TestStruct_A::mFloat));
+ EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId(&C2TestStruct_A::mSize));
+ EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId(&C2TestStruct_A::mBlob));
+ EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId(&C2TestStruct_A::mString));
+ EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId(&C2TestStruct_A::mYesNo));
+
+ EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&C2TestFlexEndSizeStruct::mSigned32));
+ EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&C2TestFlexEndSizeStruct::mFlexSize));
+
+ typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+ typedef C2GlobalParam<C2Info, C2TestFlexEndSizeStruct> C2TestFlexEndSizeInfo;
+
+ // pointer constructor in C2Param
+ EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&((C2TestAInfo*)0)->mSigned32));
+ EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&((C2TestAInfo*)0)->mSigned64));
+ EXPECT_EQ(_C2FieldId(28, 4), _C2FieldId(&((C2TestAInfo*)0)->mUnsigned32));
+ EXPECT_EQ(_C2FieldId(32, 8), _C2FieldId(&((C2TestAInfo*)0)->mUnsigned64));
+ EXPECT_EQ(_C2FieldId(40, 4), _C2FieldId(&((C2TestAInfo*)0)->mFloat));
+ EXPECT_EQ(_C2FieldId(44, 8), _C2FieldId(&((C2TestAInfo*)0)->mSize));
+ EXPECT_EQ(_C2FieldId(68, 1), _C2FieldId(&((C2TestAInfo*)0)->mBlob));
+ EXPECT_EQ(_C2FieldId(168, 1), _C2FieldId(&((C2TestAInfo*)0)->mString));
+ EXPECT_EQ(_C2FieldId(268, 1), _C2FieldId(&((C2TestAInfo*)0)->mYesNo));
+
+ EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&((C2TestFlexEndSizeInfo*)0)->m.mSigned32));
+ EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&((C2TestFlexEndSizeInfo*)0)->m.mFlexSize));
+
+ // member pointer in C2Param
+ EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSigned32));
+ EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSigned64));
+ EXPECT_EQ(_C2FieldId(28, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mUnsigned32));
+ EXPECT_EQ(_C2FieldId(32, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mUnsigned64));
+ EXPECT_EQ(_C2FieldId(40, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mFloat));
+ EXPECT_EQ(_C2FieldId(44, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSize));
+ EXPECT_EQ(_C2FieldId(68, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mBlob));
+ EXPECT_EQ(_C2FieldId(168, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mString));
+ EXPECT_EQ(_C2FieldId(268, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mYesNo));
+
+ // NOTE: cannot use a member pointer for flex params due to introduction of 'm'
+ // EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&C2TestFlexEndSizeInfo::m.mSigned32));
+ // EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&C2TestFlexEndSizeInfo::m.mFlexSize));
+
+
+
+}
+
+struct S32 {
+ template<typename T, class B=typename std::remove_extent<T>::type>
+ inline S32(const T*) {
+ static_assert(!std::is_array<T>::value, "should not be an array");
+ static_assert(std::is_same<B, int32_t>::value, "should be int32_t");
+ }
+};
+
+struct FLX {
+ template<typename U, typename T, class B=typename std::remove_extent<T>::type>
+ inline FLX(const T*, const U*) {
+ static_assert(std::is_array<T>::value, "should be an array");
+ static_assert(std::extent<T>::value == 0, "should be an array of 0 extent");
+ static_assert(std::is_same<B, U>::value, "should be type U");
+ }
+};
+
+struct MP {
+ template<typename U, typename T, typename ExpectedU, typename UnexpectedU>
+ inline MP(T U::*, const ExpectedU*, const UnexpectedU*) {
+ static_assert(!std::is_same<U, UnexpectedU>::value, "should not be member pointer of the base type");
+ static_assert(std::is_same<U, ExpectedU>::value, "should be member pointer of the derived type");
+ }
+
+ template<typename U, typename T, typename B, typename D>
+ inline MP(T D::*, const D*) { }
+};
+
+void compiledStatic_arrayTypePropagationTest() {
+ (void)S32(&((C2TestFlexEndS32Struct *)0)->mSigned32);
+ (void)FLX(&((C2TestFlexEndS32Struct *)0)->mFlexSigned32, (int32_t*)0);
+ (void)FLX(&((C2TestFlexS32Struct *)0)->mFlexSigned32, (int32_t*)0);
+
+ typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+
+ // TRICKY: &derivedClass::baseMember has type of baseClass::*
+ static_assert(std::is_same<decltype(&C2TestAInfo::mSigned32), int32_t C2TestAStruct::*>::value,
+ "base member pointer should have base class in type");
+
+ // therefore, member pointer expands to baseClass::* in templates
+ (void)MP(&C2TestAInfo::mSigned32,
+ (C2TestAStruct*)0 /* expected */, (C2TestAInfo*)0 /* unexpected */);
+ // but can be cast to derivedClass::*
+ (void)MP((int32_t C2TestAInfo::*)&C2TestAInfo::mSigned32,
+ (C2TestAInfo*)0 /* expected */, (C2TestAStruct*)0 /* unexpected */);
+
+ // TRICKY: baseClass::* does not autoconvert to derivedClass::* even in templates
+ // (void)MP(&C2TestAInfo::mSigned32, (C2TestAInfo*)0);
+}
+
+TEST_F(C2ParamTest, MemberPointerCast) {
+ typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+
+ static_assert(offsetof(C2TestAInfo, mSigned32) == 8, "offset should be 8");
+ constexpr int32_t C2TestAStruct::* s32ptr = &C2TestAInfo::mSigned32;
+ constexpr int32_t C2TestAInfo::* s32ptr_derived = (int32_t C2TestAStruct::*)&C2TestAInfo::mSigned32;
+ constexpr int32_t C2TestAInfo::* s32ptr_cast2derived = (int32_t C2TestAInfo::*)s32ptr;
+ C2TestAInfo *info = (C2TestAInfo *)256;
+ C2TestAStruct *strukt = (C2TestAStruct *)info;
+ int32_t *info_s32_derived = &(info->*s32ptr_derived);
+ int32_t *info_s32_cast2derived = &(info->*s32ptr_cast2derived);
+ int32_t *info_s32 = &(info->*s32ptr);
+ int32_t *strukt_s32 = &(strukt->*s32ptr);
+
+ EXPECT_EQ(256u, (uintptr_t)info);
+ EXPECT_EQ(264u, (uintptr_t)strukt);
+ EXPECT_EQ(264u, (uintptr_t)info_s32_derived);
+ EXPECT_EQ(264u, (uintptr_t)info_s32_cast2derived);
+ EXPECT_EQ(264u, (uintptr_t)info_s32);
+ EXPECT_EQ(264u, (uintptr_t)strukt_s32);
+
+ typedef C2GlobalParam<C2Info, C2TestFlexEndSizeStruct> C2TestFlexEndSizeInfo;
+ static_assert(offsetof(C2TestFlexEndSizeInfo, m.mSigned32) == 8, "offset should be 8");
+ static_assert(offsetof(C2TestFlexEndSizeInfo, m.mFlexSize) == 12, "offset should be 12");
+}
+
+/* ===================================== PARAM USAGE TESTS ===================================== */
+
+struct C2NumberStruct {
+ int32_t mNumber;
+ C2NumberStruct() {}
+ C2NumberStruct(int32_t _number) : mNumber(_number) {}
+
+ DEFINE_AND_DESCRIBE_C2STRUCT(Number)
+ C2FIELD(mNumber, "number")
+};
+
+struct C2NumbersStruct {
+ int32_t mNumbers[];
+ C2NumbersStruct() {}
+
+ DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(Numbers, mNumbers)
+ C2FIELD(mNumbers, "numbers")
+};
+static_assert(sizeof(C2NumbersStruct) == 0, "C2NumbersStruct has incorrect size");
+
+typedef C2GlobalParam<C2Tuning, C2NumberStruct> C2NumberTuning;
+typedef C2PortParam<C2Tuning, C2NumberStruct> C2NumberPortTuning;
+typedef C2StreamParam<C2Tuning, C2NumberStruct> C2NumberStreamTuning;
+
+typedef C2GlobalParam<C2Tuning, C2NumbersStruct> C2NumbersTuning;
+typedef C2PortParam<C2Tuning, C2NumbersStruct> C2NumbersPortTuning;
+typedef C2StreamParam<C2Tuning, C2NumbersStruct> C2NumbersStreamTuning;
+
+//
+#if 0
+
+void test() {
+ C2NumberStruct s(10);
+ (void)C2NumberStruct::fieldList;
+};
+
+typedef C2StreamParam<C2Tuning, C2Int64Value, kParamIndexNumberB> C2NumberConfig4;
+typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexNumber> C2NumberConfig3;
+typedef C2GlobalParam<C2Tuning, C2StringValue, kParamIndexNumber> C2VideoNameConfig;
+
+void test3() {
+ C2NumberConfig3 s(10);
+ s.mValue = 11;
+ s = 12;
+ (void)C2NumberConfig3::fieldList;
+ std::shared_ptr<C2VideoNameConfig> n = C2VideoNameConfig::alloc_shared(25);
+ strcpy(n->m.mValue, "lajos");
+ C2NumberConfig4 t(false, 0, 11);
+ t.mValue = 15;
+};
+
+struct C2NumbersStruct {
+ int32_t mNumbers[];
+ enum { baseIndex = kParamIndexNumber };
+ const static std::initializer_list<const C2FieldDescriptor> fieldList;
+ C2NumbersStruct() {}
+
+ FLEX(C2NumbersStruct, mNumbers);
+};
+
+static_assert(sizeof(C2NumbersStruct) == 0, "yes");
+
+
+typedef C2GlobalParam<C2Info, C2NumbersStruct> C2NumbersInfo;
+
+const std::initializer_list<const C2FieldDescriptor> C2NumbersStruct::fieldList =
+// { { FD::INT32, 0, "widths" } };
+ { C2FieldDescriptor(&((C2NumbersStruct*)(nullptr))->mNumbers, "number") };
+
+typedef C2PortParam<C2Tuning, C2NumberStruct> C2NumberConfig;
+
+std::list<const C2FieldDescriptor> myList = C2NumberConfig::fieldList;
+
+ std::unique_ptr<android::C2ParamDescriptor> __test_describe(uint32_t paramType) {
+ std::list<const C2FieldDescriptor> fields = describeC2Params<C2NumberConfig>();
+
+ auto widths = C2NumbersInfo::alloc_shared(5);
+ widths->flexCount();
+ widths->m.mNumbers[4] = 1;
+
+ test();
+ test3();
+
+ C2NumberConfig outputWidth(false, 123);
+
+ C2Param::Index index(paramType);
+ switch (paramType) {
+ case C2NumberConfig::baseIndex:
+ return std::unique_ptr<C2ParamDescriptor>(new C2ParamDescriptor{
+ true /* isRequired */,
+ "number",
+ index,
+ });
+ }
+ return nullptr;
+ }
+
+
+} // namespace android
+
+#endif
+//
+
+template<typename T>
+bool canSetPort(T &o, bool output) { return o.setPort(output); }
+bool canSetPort(...) { return false; }
+
+template<typename S, typename=decltype(((S*)0)->setPort(true))>
+static std::true_type _canCallSetPort(int);
+template<typename>
+static std::false_type _canCallSetPort(...);
+#define canCallSetPort(x) decltype(_canCallSetPort<std::remove_reference<decltype(x)>::type>(0))::value
+
+/* ======================================= STATIC TESTS ======================================= */
+
+static_assert(_C2Comparable<int>::value, "int is not comparable");
+static_assert(!_C2Comparable<void>::value, "void is comparable");
+
+struct C2_HIDE _test0 {
+ bool operator==(const _test0&);
+ bool operator!=(const _test0&);
+};
+struct C2_HIDE _test1 {
+ bool operator==(const _test1&);
+};
+struct C2_HIDE _test2 {
+ bool operator!=(const _test2&);
+};
+static_assert(_C2Comparable<_test0>::value, "class with == and != is not comparable");
+static_assert(_C2Comparable<_test1>::value, "class with == is not comparable");
+static_assert(_C2Comparable<_test2>::value, "class with != is not comparable");
+
+/* ======================================= C2PARAM TESTS ======================================= */
+
+struct _C2ParamInspector {
+ static void StaticTest();
+ static void StaticFlexTest();
+};
+
+// TEST_F(_C2ParamInspector, StaticTest) {
+void _C2ParamInspector::StaticTest() {
+ typedef C2Param::Index I;
+
+ // C2NumberStruct: baseIndex = kIndex (args)
+ static_assert(C2NumberStruct::baseIndex == kParamIndexNumber, "bad index");
+ static_assert(sizeof(C2NumberStruct) == 4, "bad size");
+
+ // C2NumberTuning: kIndex | tun | global (args)
+ static_assert(C2NumberTuning::baseIndex == kParamIndexNumber, "bad index");
+ static_assert(C2NumberTuning::typeIndex == (kParamIndexNumber | I::kTypeTuning | I::kDirGlobal), "bad index");
+ static_assert(sizeof(C2NumberTuning) == 12, "bad size");
+
+ static_assert(offsetof(C2NumberTuning, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumberTuning, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumberTuning, mNumber) == 8, "bad offset");
+
+ // C2NumberPortTuning: kIndex | tun | port (bool, args)
+ static_assert(sizeof(C2NumberPortTuning) == 12, "bad size");
+ // C2NumberPortTuning::input: kIndex | tun | port | input (args)
+ // C2NumberPortTuning::output: kIndex | tun | port | output (args)
+ static_assert(C2NumberPortTuning::input::baseIndex ==
+ kParamIndexNumber, "bad index");
+ static_assert(C2NumberPortTuning::input::typeIndex ==
+ (kParamIndexNumber | I::kTypeTuning | I::kDirInput), "bad index");
+ static_assert(C2NumberPortTuning::output::baseIndex ==
+ kParamIndexNumber, "bad index");
+ static_assert(C2NumberPortTuning::output::typeIndex ==
+ (kParamIndexNumber | I::kTypeTuning | I::kDirOutput), "bad index");
+ static_assert(sizeof(C2NumberPortTuning::input) == 12, "bad size");
+ static_assert(sizeof(C2NumberPortTuning::output) == 12, "bad size");
+ static_assert(offsetof(C2NumberPortTuning::input, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumberPortTuning::input, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumberPortTuning::input, mNumber) == 8, "bad offset");
+ static_assert(offsetof(C2NumberPortTuning::output, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumberPortTuning::output, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumberPortTuning::output, mNumber) == 8, "bad offset");
+
+ // C2NumberStreamTuning: kIndex | tun | str (bool, uint, args)
+ static_assert(sizeof(C2NumberStreamTuning) == 12u, "bad size");
+ // C2NumberStreamTuning::input kIndex | tun | str | input (int, args)
+ // C2NumberStreamTuning::output kIx | tun | str | output (int, args)
+ static_assert(C2NumberStreamTuning::input::baseIndex ==
+ kParamIndexNumber, "bad index");
+ static_assert(C2NumberStreamTuning::input::typeIndex ==
+ (kParamIndexNumber | I::kTypeTuning | I::kDirInput | I::kStreamFlag), "bad index");
+ static_assert(C2NumberStreamTuning::output::baseIndex ==
+ kParamIndexNumber, "bad index");
+ static_assert(C2NumberStreamTuning::output::typeIndex ==
+ (kParamIndexNumber | I::kTypeTuning | I::kDirOutput | I::kStreamFlag), "bad index");
+ static_assert(sizeof(C2NumberStreamTuning::input) == 12u, "bad size");
+ static_assert(sizeof(C2NumberStreamTuning::output) == 12u, "bad size");
+ static_assert(offsetof(C2NumberStreamTuning::input, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumberStreamTuning::input, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumberStreamTuning::input, mNumber) == 8, "bad offset");
+ static_assert(offsetof(C2NumberStreamTuning::output, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumberStreamTuning::output, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumberStreamTuning::output, mNumber) == 8, "bad offset");
+}
+
+void _C2ParamInspector::StaticFlexTest() {
+ typedef C2Param::Index I;
+
+ // C2NumbersStruct: baseIndex = kIndex (args)
+ static_assert(C2NumbersStruct::baseIndex == (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(sizeof(C2NumbersStruct) == 0, "bad size");
+
+ // C2NumbersTuning: kIndex | tun | global (args)
+ static_assert(C2NumbersTuning::baseIndex == (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(C2NumbersTuning::typeIndex == (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirGlobal), "bad index");
+ static_assert(sizeof(C2NumbersTuning) == 8, "bad size");
+
+ static_assert(offsetof(C2NumbersTuning, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumbersTuning, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumbersTuning, m.mNumbers) == 8, "bad offset");
+
+ // C2NumbersPortTuning: kIndex | tun | port (bool, args)
+ static_assert(sizeof(C2NumbersPortTuning) == 8, "bad size");
+ // C2NumbersPortTuning::input: kIndex | tun | port | input (args)
+ // C2NumbersPortTuning::output: kIndex | tun | port | output (args)
+ static_assert(C2NumbersPortTuning::input::baseIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(C2NumbersPortTuning::input::typeIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirInput), "bad index");
+ static_assert(C2NumbersPortTuning::output::baseIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(C2NumbersPortTuning::output::typeIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirOutput), "bad index");
+ static_assert(sizeof(C2NumbersPortTuning::input) == 8, "bad size");
+ static_assert(sizeof(C2NumbersPortTuning::output) == 8, "bad size");
+ static_assert(offsetof(C2NumbersPortTuning::input, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumbersPortTuning::input, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumbersPortTuning::input, m.mNumbers) == 8, "bad offset");
+ static_assert(offsetof(C2NumbersPortTuning::output, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumbersPortTuning::output, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumbersPortTuning::output, m.mNumbers) == 8, "bad offset");
+
+ // C2NumbersStreamTuning: kIndex | tun | str (bool, uint, args)
+ static_assert(sizeof(C2NumbersStreamTuning) == 8, "bad size");
+ // C2NumbersStreamTuning::input kIndex | tun | str | input (int, args)
+ // C2NumbersStreamTuning::output kIx | tun | str | output (int, args)
+ static_assert(C2NumbersStreamTuning::input::baseIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(C2NumbersStreamTuning::input::typeIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirInput | I::kStreamFlag), "bad index");
+ static_assert(C2NumbersStreamTuning::output::baseIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+ static_assert(C2NumbersStreamTuning::output::typeIndex ==
+ (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirOutput | I::kStreamFlag), "bad index");
+ static_assert(sizeof(C2NumbersStreamTuning::input) == 8, "bad size");
+ static_assert(sizeof(C2NumbersStreamTuning::output) == 8, "bad size");
+ static_assert(offsetof(C2NumbersStreamTuning::input, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumbersStreamTuning::input, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumbersStreamTuning::input, m.mNumbers) == 8, "bad offset");
+ static_assert(offsetof(C2NumbersStreamTuning::output, _mSize) == 0, "bad size");
+ static_assert(offsetof(C2NumbersStreamTuning::output, _mIndex) == 4, "bad offset");
+ static_assert(offsetof(C2NumbersStreamTuning::output, m.mNumbers) == 8, "bad offset");
+}
+
+TEST_F(C2ParamTest, ParamOpsTest) {
+ const C2NumberStruct str(100);
+ C2NumberStruct bstr;
+
+ {
+ EXPECT_EQ(100, str.mNumber);
+ bstr.mNumber = 100;
+
+ C2Param::BaseIndex index = C2NumberStruct::baseIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+ }
+
+ const C2NumberTuning tun(100);
+ C2NumberTuning btun;
+
+ {
+ // flags & invariables
+ for (const auto &p : { tun, btun }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+ EXPECT_EQ(12u, p.size());
+
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_TRUE(p.isGlobal());
+ EXPECT_FALSE(p.forInput());
+ EXPECT_FALSE(p.forOutput());
+ EXPECT_FALSE(p.forStream());
+ EXPECT_FALSE(p.forPort());
+ }
+
+ // value
+ EXPECT_EQ(100, tun.mNumber);
+ EXPECT_EQ(0, btun.mNumber);
+ EXPECT_FALSE(tun == btun);
+ EXPECT_FALSE(tun.operator==(btun));
+ EXPECT_TRUE(tun != btun);
+ EXPECT_TRUE(tun.operator!=(btun));
+ btun.mNumber = 100;
+ EXPECT_EQ(tun, btun);
+
+ // index
+ EXPECT_EQ(C2Param::Type(tun.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(tun.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(tun.type(), C2NumberTuning::typeIndex);
+ EXPECT_EQ(tun.stream(), ~0u);
+
+ C2Param::BaseIndex index = C2NumberTuning::baseIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+ C2Param::Type type = C2NumberTuning::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_FALSE(type.isFlexible());
+ EXPECT_TRUE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ EXPECT_EQ(C2NumberTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&tun), &tun);
+ EXPECT_EQ(C2NumberPortTuning::From(&tun), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&tun), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&tun), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::From(&tun), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&tun), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&tun), nullptr);
+ }
+
+ const C2NumberPortTuning outp1(true, 100), inp1(false, 100);
+ C2NumberPortTuning boutp1, binp1, binp3(false, 100);
+ const C2NumberPortTuning::input inp2(100);
+ C2NumberPortTuning::input binp2;
+ const C2NumberPortTuning::output outp2(100);
+ C2NumberPortTuning::output boutp2;
+
+ {
+ static_assert(canCallSetPort(binp3), "should be able to");
+ static_assert(canCallSetPort(binp1), "should be able to");
+ static_assert(!canCallSetPort(inp1), "should not be able to (const)");
+ static_assert(!canCallSetPort(inp2), "should not be able to (const & type)");
+ static_assert(!canCallSetPort(binp2), "should not be able to (type)");
+
+ // flags & invariables
+ for (const auto &p : { outp1, inp1, boutp1 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_FALSE(p.forStream());
+ EXPECT_TRUE(p.forPort());
+ }
+ for (const auto &p : { inp2, binp2 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_FALSE(p.forStream());
+ EXPECT_TRUE(p.forPort());
+ }
+ for (const auto &p : { outp2, boutp2 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_FALSE(p.forStream());
+ EXPECT_TRUE(p.forPort());
+ }
+
+ // port specific flags & invariables
+ EXPECT_FALSE(outp1.forInput());
+ EXPECT_TRUE(outp1.forOutput());
+
+ EXPECT_TRUE(inp1.forInput());
+ EXPECT_FALSE(inp1.forOutput());
+
+ for (const auto &p : { outp1, inp1 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+ EXPECT_EQ(100, p.mNumber);
+ }
+ for (const auto &p : { outp2, boutp2 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+
+ EXPECT_FALSE(p.forInput());
+ EXPECT_TRUE(p.forOutput());
+ }
+ for (const auto &p : { inp2, binp2 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+
+ EXPECT_TRUE(p.forInput());
+ EXPECT_FALSE(p.forOutput());
+ }
+ for (const auto &p : { boutp1 } ) {
+ EXPECT_FALSE((bool)p);
+ EXPECT_TRUE(!p);
+
+ EXPECT_FALSE(p.forInput());
+ EXPECT_FALSE(p.forOutput());
+ EXPECT_EQ(0, p.mNumber);
+ }
+
+ // values
+ EXPECT_EQ(100, inp2.mNumber);
+ EXPECT_EQ(100, outp2.mNumber);
+ EXPECT_EQ(0, binp1.mNumber);
+ EXPECT_EQ(0, binp2.mNumber);
+ EXPECT_EQ(0, boutp1.mNumber);
+ EXPECT_EQ(0, boutp2.mNumber);
+
+ EXPECT_TRUE(inp1 != outp1);
+ EXPECT_TRUE(inp1 == inp2);
+ EXPECT_TRUE(outp1 == outp2);
+ EXPECT_TRUE(binp1 == boutp1);
+ EXPECT_TRUE(binp2 != boutp2);
+
+ EXPECT_TRUE(inp1 != binp1);
+ binp1.mNumber = 100;
+ EXPECT_TRUE(inp1 != binp1);
+ binp1.setPort(false /* output */);
+ EXPECT_TRUE((bool)binp1);
+ EXPECT_FALSE(!binp1);
+ EXPECT_TRUE(inp1 == binp1);
+
+ EXPECT_TRUE(inp2 != binp2);
+ binp2.mNumber = 100;
+ EXPECT_TRUE(inp2 == binp2);
+
+ binp1.setPort(true /* output */);
+ EXPECT_TRUE(outp1 == binp1);
+
+ EXPECT_TRUE(outp1 != boutp1);
+ boutp1.mNumber = 100;
+ EXPECT_TRUE(outp1 != boutp1);
+ boutp1.setPort(true /* output */);
+ EXPECT_TRUE((bool)boutp1);
+ EXPECT_FALSE(!boutp1);
+ EXPECT_TRUE(outp1 == boutp1);
+
+ EXPECT_TRUE(outp2 != boutp2);
+ boutp2.mNumber = 100;
+ EXPECT_TRUE(outp2 == boutp2);
+
+ boutp1.setPort(false /* output */);
+ EXPECT_TRUE(inp1 == boutp1);
+
+ // index
+ EXPECT_EQ(C2Param::Type(inp1.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(inp1.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(inp1.type(), C2NumberPortTuning::input::typeIndex);
+ EXPECT_EQ(inp1.stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(inp2.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(inp2.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(inp2.type(), C2NumberPortTuning::input::typeIndex);
+ EXPECT_EQ(inp2.stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(outp1.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outp1.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(outp1.type(), C2NumberPortTuning::output::typeIndex);
+ EXPECT_EQ(outp1.stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(outp2.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outp2.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(outp2.type(), C2NumberPortTuning::output::typeIndex);
+ EXPECT_EQ(outp2.stream(), ~0u);
+
+ C2Param::BaseIndex index = C2NumberPortTuning::input::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+ index = C2NumberPortTuning::output::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+ C2Param::Type type = C2NumberPortTuning::input::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_FALSE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_TRUE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_TRUE(type.forPort());
+
+ type = C2NumberPortTuning::output::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_FALSE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_TRUE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_TRUE(type.forPort());
+
+ EXPECT_EQ(C2NumberPortTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&inp1), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&inp2), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&outp1), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&outp2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::From(&inp1), &inp1);
+ EXPECT_EQ(C2NumberPortTuning::From(&inp2), (C2NumberPortTuning*)&inp2);
+ EXPECT_EQ(C2NumberPortTuning::From(&outp1), &outp1);
+ EXPECT_EQ(C2NumberPortTuning::From(&outp2), (C2NumberPortTuning*)&outp2);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&inp1), (C2NumberPortTuning::input*)&inp1);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&inp2), &inp2);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&outp1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&outp2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&inp1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&inp2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&outp1), (C2NumberPortTuning::output*)&outp1);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&outp2), &outp2);
+ EXPECT_EQ(C2NumberStreamTuning::From(&inp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::From(&inp2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::From(&outp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::From(&outp2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&inp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&inp2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&outp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&outp2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&inp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&inp2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&outp1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&outp2), nullptr);
+ }
+
+ const C2NumberStreamTuning outs1(true, 1u, 100), ins1(false, 1u, 100);
+ C2NumberStreamTuning bouts1, bins1, bins3(false, 1u, 100);
+ const C2NumberStreamTuning::input ins2(1u, 100);
+ C2NumberStreamTuning::input bins2;
+ const C2NumberStreamTuning::output outs2(1u, 100);
+ C2NumberStreamTuning::output bouts2;
+
+ {
+ static_assert(canCallSetPort(bins3), "should be able to");
+ static_assert(canCallSetPort(bins1), "should be able to");
+ static_assert(!canCallSetPort(ins1), "should not be able to (const)");
+ static_assert(!canCallSetPort(ins2), "should not be able to (const & type)");
+ static_assert(!canCallSetPort(bins2), "should not be able to (type)");
+
+ // flags & invariables
+ for (const auto &p : { outs1, ins1, bouts1 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_TRUE(p.forStream());
+ EXPECT_FALSE(p.forPort());
+ }
+ for (const auto &p : { ins2, bins2 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_TRUE(p.forStream());
+ EXPECT_FALSE(p.forPort());
+ }
+ for (const auto &p : { outs2, bouts2 }) {
+ EXPECT_EQ(12u, p.size());
+ EXPECT_FALSE(p.isVendor());
+ EXPECT_FALSE(p.isFlexible());
+ EXPECT_FALSE(p.isGlobal());
+ EXPECT_TRUE(p.forStream());
+ EXPECT_FALSE(p.forPort());
+ }
+
+ // port specific flags & invariables
+ EXPECT_FALSE(outs1.forInput());
+ EXPECT_TRUE(outs1.forOutput());
+
+ EXPECT_TRUE(ins1.forInput());
+ EXPECT_FALSE(ins1.forOutput());
+
+ for (const auto &p : { outs1, ins1 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+ EXPECT_EQ(100, p.mNumber);
+ EXPECT_EQ(1u, p.stream());
+ }
+ for (const auto &p : { outs2, bouts2 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+
+ EXPECT_FALSE(p.forInput());
+ EXPECT_TRUE(p.forOutput());
+ }
+ for (const auto &p : { ins2, bins2 }) {
+ EXPECT_TRUE((bool)p);
+ EXPECT_FALSE(!p);
+
+ EXPECT_TRUE(p.forInput());
+ EXPECT_FALSE(p.forOutput());
+ }
+ for (const auto &p : { bouts1 } ) {
+ EXPECT_FALSE((bool)p);
+ EXPECT_TRUE(!p);
+
+ EXPECT_FALSE(p.forInput());
+ EXPECT_FALSE(p.forOutput());
+ EXPECT_EQ(0, p.mNumber);
+ }
+
+ // values
+ EXPECT_EQ(100, ins2.mNumber);
+ EXPECT_EQ(100, outs2.mNumber);
+ EXPECT_EQ(0, bins1.mNumber);
+ EXPECT_EQ(0, bins2.mNumber);
+ EXPECT_EQ(0, bouts1.mNumber);
+ EXPECT_EQ(0, bouts2.mNumber);
+
+ EXPECT_EQ(1u, ins2.stream());
+ EXPECT_EQ(1u, outs2.stream());
+ EXPECT_EQ(0u, bins1.stream());
+ EXPECT_EQ(0u, bins2.stream());
+ EXPECT_EQ(0u, bouts1.stream());
+ EXPECT_EQ(0u, bouts2.stream());
+
+ EXPECT_TRUE(ins1 != outs1);
+ EXPECT_TRUE(ins1 == ins2);
+ EXPECT_TRUE(outs1 == outs2);
+ EXPECT_TRUE(bins1 == bouts1);
+ EXPECT_TRUE(bins2 != bouts2);
+
+ EXPECT_TRUE(ins1 != bins1);
+ bins1.mNumber = 100;
+ EXPECT_TRUE(ins1 != bins1);
+ bins1.setPort(false /* output */);
+ EXPECT_TRUE(ins1 != bins1);
+ bins1.setStream(1u);
+ EXPECT_TRUE(ins1 == bins1);
+
+ EXPECT_TRUE(ins2 != bins2);
+ bins2.mNumber = 100;
+ EXPECT_TRUE(ins2 != bins2);
+ bins2.setStream(1u);
+ EXPECT_TRUE(ins2 == bins2);
+
+ bins1.setPort(true /* output */);
+ EXPECT_TRUE(outs1 == bins1);
+
+ EXPECT_TRUE(outs1 != bouts1);
+ bouts1.mNumber = 100;
+ EXPECT_TRUE(outs1 != bouts1);
+ bouts1.setPort(true /* output */);
+ EXPECT_TRUE(outs1 != bouts1);
+ bouts1.setStream(1u);
+ EXPECT_TRUE(outs1 == bouts1);
+
+ EXPECT_TRUE(outs2 != bouts2);
+ bouts2.mNumber = 100;
+ EXPECT_TRUE(outs2 != bouts2);
+ bouts2.setStream(1u);
+ EXPECT_TRUE(outs2 == bouts2);
+
+ bouts1.setPort(false /* output */);
+ EXPECT_TRUE(ins1 == bouts1);
+
+ // index
+ EXPECT_EQ(C2Param::Type(ins1.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(ins1.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(ins1.type(), C2NumberStreamTuning::input::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(ins2.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(ins2.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(ins2.type(), C2NumberStreamTuning::input::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(outs1.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outs1.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(outs1.type(), C2NumberStreamTuning::output::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(outs2.type()).baseIndex(), C2NumberStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outs2.type()).paramIndex(), kParamIndexNumber);
+ EXPECT_EQ(outs2.type(), C2NumberStreamTuning::output::typeIndex);
+
+ C2Param::BaseIndex index = C2NumberStreamTuning::input::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+ index = C2NumberStreamTuning::output::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_FALSE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+ C2Param::Type type = C2NumberStreamTuning::input::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_FALSE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_TRUE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_TRUE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ type = C2NumberStreamTuning::output::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_FALSE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_TRUE(type.forOutput());
+ EXPECT_TRUE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ EXPECT_EQ(C2NumberPortTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&ins1), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&ins2), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&outs1), nullptr);
+ EXPECT_EQ(C2NumberTuning::From(&outs2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::From(&ins1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::From(&ins2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::From(&outs1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::From(&outs2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&ins1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&ins2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&outs1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::input::From(&outs2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&ins1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&ins2), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&outs1), nullptr);
+ EXPECT_EQ(C2NumberPortTuning::output::From(&outs2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::From(&ins1), &ins1);
+ EXPECT_EQ(C2NumberStreamTuning::From(&ins2), (C2NumberStreamTuning*)&ins2);
+ EXPECT_EQ(C2NumberStreamTuning::From(&outs1), &outs1);
+ EXPECT_EQ(C2NumberStreamTuning::From(&outs2), (C2NumberStreamTuning*)&outs2);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&ins1), (C2NumberStreamTuning::input*)&ins1);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&ins2), &ins2);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&outs1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::input::From(&outs2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&ins1), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&ins2), nullptr);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&outs1), (C2NumberStreamTuning::output*)&outs1);
+ EXPECT_EQ(C2NumberStreamTuning::output::From(&outs2), &outs2);
+
+ }
+
+ {
+ uint32_t videoWidth[] = { 12u, C2NumberStreamTuning::output::typeIndex, 100 };
+ C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+ EXPECT_NE(p1, nullptr);
+ EXPECT_EQ(12u, p1->size());
+ EXPECT_EQ(p1->type(), C2NumberStreamTuning::output::typeIndex);
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+ EXPECT_EQ(p1, nullptr);
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+ EXPECT_EQ(p1, nullptr);
+
+ p1 = C2Param::From(videoWidth, 3);
+ EXPECT_EQ(p1, nullptr);
+
+ p1 = C2Param::From(videoWidth, 0);
+ EXPECT_EQ(p1, nullptr);
+ }
+}
+
+void StaticTestAddBaseIndex() {
+ struct nobase {};
+ struct base { enum : uint32_t { baseIndex = 1 }; };
+ static_assert(C2AddBaseIndex<nobase, 2>::baseIndex == 2, "should be 2");
+ static_assert(C2AddBaseIndex<base, 1>::baseIndex == 1, "should be 1");
+}
+
+class TestFlexHelper {
+ struct _Flex {
+ int32_t a;
+ char b[];
+ _Flex() {}
+ FLEX(_Flex, b);
+ };
+
+ struct _BoFlex {
+ _Flex a;
+ _BoFlex() {}
+ FLEX(_BoFlex, a);
+ };
+
+ struct _NonFlex {
+ };
+
+
+ static void StaticTest() {
+ static_assert(std::is_same<_C2FlexHelper<char>::flexType, void>::value, "should be void");
+ static_assert(std::is_same<_C2FlexHelper<char[]>::flexType, char>::value, "should be char");
+ static_assert(std::is_same<_C2FlexHelper<_Flex>::flexType, char>::value, "should be char");
+
+ static_assert(std::is_same<_C2FlexHelper<_BoFlex>::flexType, char>::value, "should be void");
+
+ static_assert(_C2Flexible<_Flex>::value, "should be flexible");
+ static_assert(!_C2Flexible<_NonFlex>::value, "should not be flexible");
+ }
+};
+
+TEST_F(C2ParamTest, FlexParamOpsTest) {
+// const C2NumbersStruct str{100};
+ C2NumbersStruct bstr;
+ {
+// EXPECT_EQ(100, str->m.mNumbers[0]);
+ (void)&bstr.mNumbers[0];
+
+ C2Param::BaseIndex index = C2NumbersStruct::baseIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+ }
+
+ std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::alloc_unique(1);
+ tun_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersTuning> tun = std::move(tun_);
+ std::shared_ptr<C2NumbersTuning> btun = C2NumbersTuning::alloc_shared(1);
+
+ {
+ // flags & invariables
+ const C2NumbersTuning *T[] = { tun.get(), btun.get() };
+ for (const auto p : T) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+ EXPECT_EQ(12u, p->size());
+
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_TRUE(p->isGlobal());
+ EXPECT_FALSE(p->forInput());
+ EXPECT_FALSE(p->forOutput());
+ EXPECT_FALSE(p->forStream());
+ EXPECT_FALSE(p->forPort());
+ }
+
+ // value
+ EXPECT_EQ(100, tun->m.mNumbers[0]);
+ EXPECT_EQ(0, btun->m.mNumbers[0]);
+ EXPECT_FALSE(*tun == *btun);
+ EXPECT_FALSE(tun->operator==(*btun));
+ EXPECT_TRUE(*tun != *btun);
+ EXPECT_TRUE(tun->operator!=(*btun));
+ btun->m.mNumbers[0] = 100;
+ EXPECT_EQ(*tun, *btun);
+
+ // index
+ EXPECT_EQ(C2Param::Type(tun->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(tun->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(tun->type(), C2NumbersTuning::typeIndex);
+ EXPECT_EQ(tun->stream(), ~0u);
+
+ C2Param::BaseIndex index = C2NumbersTuning::baseIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+ C2Param::Type type = C2NumbersTuning::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_TRUE(type.isFlexible());
+ EXPECT_TRUE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ EXPECT_EQ(C2NumbersTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(tun.get()), tun.get());
+ EXPECT_EQ(C2NumbersPortTuning::From(tun.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(tun.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(tun.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::From(tun.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(tun.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(tun.get()), nullptr);
+ }
+
+ std::unique_ptr<C2NumbersPortTuning> outp1_(C2NumbersPortTuning::alloc_unique(1, true)),
+ inp1_ = C2NumbersPortTuning::alloc_unique(1, false);
+ outp1_->m.mNumbers[0] = 100;
+ inp1_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersPortTuning> outp1 = std::move(outp1_);
+ std::unique_ptr<const C2NumbersPortTuning> inp1 = std::move(inp1_);
+ std::shared_ptr<C2NumbersPortTuning> boutp1(C2NumbersPortTuning::alloc_shared(1)),
+ binp1 = C2NumbersPortTuning::alloc_shared(1),
+ binp3 = C2NumbersPortTuning::alloc_shared(1, false);
+ binp3->m.mNumbers[0] = 100;
+ std::unique_ptr<C2NumbersPortTuning::input> inp2_(C2NumbersPortTuning::input::alloc_unique(1));
+ inp2_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersPortTuning::input> inp2 = std::move(inp2_);
+ std::shared_ptr<C2NumbersPortTuning::input> binp2(C2NumbersPortTuning::input::alloc_shared(1));
+ std::unique_ptr<C2NumbersPortTuning::output> outp2_(C2NumbersPortTuning::output::alloc_unique(1));
+ outp2_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersPortTuning::output> outp2 = std::move(outp2_);
+ std::shared_ptr<C2NumbersPortTuning::output> boutp2(C2NumbersPortTuning::output::alloc_shared(1));
+
+ {
+ static_assert(canCallSetPort(*binp3), "should be able to");
+ static_assert(canCallSetPort(*binp1), "should be able to");
+ static_assert(!canCallSetPort(*inp1), "should not be able to (const)");
+ static_assert(!canCallSetPort(*inp2), "should not be able to (const & type)");
+ static_assert(!canCallSetPort(*binp2), "should not be able to (type)");
+
+ // flags & invariables
+ const C2NumbersPortTuning *P[] = { outp1.get(), inp1.get(), boutp1.get() };
+ for (const auto p : P) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_FALSE(p->forStream());
+ EXPECT_TRUE(p->forPort());
+ }
+ const C2NumbersPortTuning::input *PI[] = { inp2.get(), binp2.get() };
+ for (const auto p : PI) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_FALSE(p->forStream());
+ EXPECT_TRUE(p->forPort());
+ }
+ const C2NumbersPortTuning::output *PO[] = { outp2.get(), boutp2.get() };
+ for (const auto p : PO) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_FALSE(p->forStream());
+ EXPECT_TRUE(p->forPort());
+ }
+
+ // port specific flags & invariables
+ EXPECT_FALSE(outp1->forInput());
+ EXPECT_TRUE(outp1->forOutput());
+
+ EXPECT_TRUE(inp1->forInput());
+ EXPECT_FALSE(inp1->forOutput());
+
+ const C2NumbersPortTuning *P2[] = { outp1.get(), inp1.get() };
+ for (const auto p : P2) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+ EXPECT_EQ(100, p->m.mNumbers[0]);
+ }
+ for (const auto p : PO) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+
+ EXPECT_FALSE(p->forInput());
+ EXPECT_TRUE(p->forOutput());
+ }
+ for (const auto p : PI) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+
+ EXPECT_TRUE(p->forInput());
+ EXPECT_FALSE(p->forOutput());
+ }
+ const C2NumbersPortTuning *P3[] = { boutp1.get() };
+ for (const auto p : P3) {
+ EXPECT_FALSE((bool)(*p));
+ EXPECT_TRUE(!(*p));
+
+ EXPECT_FALSE(p->forInput());
+ EXPECT_FALSE(p->forOutput());
+ EXPECT_EQ(0, p->m.mNumbers[0]);
+ }
+
+ // values
+ EXPECT_EQ(100, inp2->m.mNumbers[0]);
+ EXPECT_EQ(100, outp2->m.mNumbers[0]);
+ EXPECT_EQ(0, binp1->m.mNumbers[0]);
+ EXPECT_EQ(0, binp2->m.mNumbers[0]);
+ EXPECT_EQ(0, boutp1->m.mNumbers[0]);
+ EXPECT_EQ(0, boutp2->m.mNumbers[0]);
+
+ EXPECT_TRUE(*inp1 != *outp1);
+ EXPECT_TRUE(*inp1 == *inp2);
+ EXPECT_TRUE(*outp1 == *outp2);
+ EXPECT_TRUE(*binp1 == *boutp1);
+ EXPECT_TRUE(*binp2 != *boutp2);
+
+ EXPECT_TRUE(*inp1 != *binp1);
+ binp1->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*inp1 != *binp1);
+ binp1->setPort(false /* output */);
+ EXPECT_TRUE((bool)*binp1);
+ EXPECT_FALSE(!*binp1);
+ EXPECT_TRUE(*inp1 == *binp1);
+
+ EXPECT_TRUE(*inp2 != *binp2);
+ binp2->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*inp2 == *binp2);
+
+ binp1->setPort(true /* output */);
+ EXPECT_TRUE(*outp1 == *binp1);
+
+ EXPECT_TRUE(*outp1 != *boutp1);
+ boutp1->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*outp1 != *boutp1);
+ boutp1->setPort(true /* output */);
+ EXPECT_TRUE((bool)*boutp1);
+ EXPECT_FALSE(!*boutp1);
+ EXPECT_TRUE(*outp1 == *boutp1);
+
+ EXPECT_TRUE(*outp2 != *boutp2);
+ boutp2->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*outp2 == *boutp2);
+
+ boutp1->setPort(false /* output */);
+ EXPECT_TRUE(*inp1 == *boutp1);
+
+ // index
+ EXPECT_EQ(C2Param::Type(inp1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(inp1->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(inp1->type(), C2NumbersPortTuning::input::typeIndex);
+ EXPECT_EQ(inp1->stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(inp2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(inp2->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(inp2->type(), C2NumbersPortTuning::input::typeIndex);
+ EXPECT_EQ(inp2->stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(outp1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outp1->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(outp1->type(), C2NumbersPortTuning::output::typeIndex);
+ EXPECT_EQ(outp1->stream(), ~0u);
+
+ EXPECT_EQ(C2Param::Type(outp2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outp2->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(outp2->type(), C2NumbersPortTuning::output::typeIndex);
+ EXPECT_EQ(outp2->stream(), ~0u);
+
+ C2Param::BaseIndex index = C2NumbersPortTuning::input::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+ index = C2NumbersPortTuning::output::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+ C2Param::Type type = C2NumbersPortTuning::input::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_TRUE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_TRUE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_TRUE(type.forPort());
+
+ type = C2NumbersPortTuning::output::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_TRUE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_TRUE(type.forOutput());
+ EXPECT_FALSE(type.forStream());
+ EXPECT_TRUE(type.forPort());
+
+ EXPECT_EQ(C2NumbersPortTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(inp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(inp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(outp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(outp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::From(inp1.get()), inp1.get());
+ EXPECT_EQ(C2NumbersPortTuning::From(inp2.get()), (C2NumbersPortTuning*)inp2.get());
+ EXPECT_EQ(C2NumbersPortTuning::From(outp1.get()), outp1.get());
+ EXPECT_EQ(C2NumbersPortTuning::From(outp2.get()), (C2NumbersPortTuning*)outp2.get());
+ EXPECT_EQ(C2NumbersPortTuning::input::From(inp1.get()), (C2NumbersPortTuning::input*)inp1.get());
+ EXPECT_EQ(C2NumbersPortTuning::input::From(inp2.get()), inp2.get());
+ EXPECT_EQ(C2NumbersPortTuning::input::From(outp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(outp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(inp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(inp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(outp1.get()), (C2NumbersPortTuning::output*)outp1.get());
+ EXPECT_EQ(C2NumbersPortTuning::output::From(outp2.get()), outp2.get());
+ EXPECT_EQ(C2NumbersStreamTuning::From(inp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::From(inp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::From(outp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::From(outp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(inp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(inp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(outp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(outp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(inp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(inp2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(outp1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(outp2.get()), nullptr);
+
+ }
+
+ std::unique_ptr<C2NumbersStreamTuning> outs1_(C2NumbersStreamTuning::alloc_unique(1, true, 1u));
+ outs1_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersStreamTuning> outs1 = std::move(outs1_);
+ std::unique_ptr<C2NumbersStreamTuning> ins1_(C2NumbersStreamTuning::alloc_unique(1, false, 1u));
+ ins1_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersStreamTuning> ins1 = std::move(ins1_);
+ std::shared_ptr<C2NumbersStreamTuning> bouts1(C2NumbersStreamTuning::alloc_shared(1));
+ std::shared_ptr<C2NumbersStreamTuning> bins1(C2NumbersStreamTuning::alloc_shared(1));
+ std::shared_ptr<C2NumbersStreamTuning> bins3(C2NumbersStreamTuning::alloc_shared(1, false, 1u));
+ bins3->m.mNumbers[0] = 100;
+ std::unique_ptr<C2NumbersStreamTuning::input> ins2_(C2NumbersStreamTuning::input::alloc_unique(1, 1u));
+ ins2_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersStreamTuning::input> ins2 = std::move(ins2_);
+ std::shared_ptr<C2NumbersStreamTuning::input> bins2(C2NumbersStreamTuning::input::alloc_shared(1));
+ std::unique_ptr<C2NumbersStreamTuning::output> outs2_(C2NumbersStreamTuning::output::alloc_unique(1, 1u));
+ outs2_->m.mNumbers[0] = 100;
+ std::unique_ptr<const C2NumbersStreamTuning::output> outs2 = std::move(outs2_);
+ std::shared_ptr<C2NumbersStreamTuning::output> bouts2(C2NumbersStreamTuning::output::alloc_shared(1));
+
+ {
+ static_assert(canCallSetPort(*bins3), "should be able to");
+ static_assert(canCallSetPort(*bins1), "should be able to");
+ static_assert(!canCallSetPort(*ins1), "should not be able to (const)");
+ static_assert(!canCallSetPort(*ins2), "should not be able to (const & type)");
+ static_assert(!canCallSetPort(*bins2), "should not be able to (type)");
+
+ // flags & invariables
+ const C2NumbersStreamTuning *S[] = { outs1.get(), ins1.get(), bouts1.get() };
+ for (const auto p : S) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_TRUE(p->forStream());
+ EXPECT_FALSE(p->forPort());
+ }
+ const C2NumbersStreamTuning::input *SI[] = { ins2.get(), bins2.get() };
+ for (const auto p : SI) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_TRUE(p->forStream());
+ EXPECT_FALSE(p->forPort());
+ }
+ const C2NumbersStreamTuning::output *SO[] = { outs2.get(), bouts2.get() };
+ for (const auto p : SO) {
+ EXPECT_EQ(12u, p->size());
+ EXPECT_FALSE(p->isVendor());
+ EXPECT_TRUE(p->isFlexible());
+ EXPECT_FALSE(p->isGlobal());
+ EXPECT_TRUE(p->forStream());
+ EXPECT_FALSE(p->forPort());
+ }
+
+ // port specific flags & invariables
+ EXPECT_FALSE(outs1->forInput());
+ EXPECT_TRUE(outs1->forOutput());
+
+ EXPECT_TRUE(ins1->forInput());
+ EXPECT_FALSE(ins1->forOutput());
+
+ const C2NumbersStreamTuning *S2[] = { outs1.get(), ins1.get() };
+ for (const auto p : S2) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+ EXPECT_EQ(100, p->m.mNumbers[0]);
+ EXPECT_EQ(1u, p->stream());
+ }
+ for (const auto p : SO) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+
+ EXPECT_FALSE(p->forInput());
+ EXPECT_TRUE(p->forOutput());
+ }
+ for (const auto p : SI) {
+ EXPECT_TRUE((bool)(*p));
+ EXPECT_FALSE(!(*p));
+
+ EXPECT_TRUE(p->forInput());
+ EXPECT_FALSE(p->forOutput());
+ }
+ const C2NumbersStreamTuning *S3[] = { bouts1.get() };
+ for (const auto p : S3) {
+ EXPECT_FALSE((bool)(*p));
+ EXPECT_TRUE(!(*p));
+
+ EXPECT_FALSE(p->forInput());
+ EXPECT_FALSE(p->forOutput());
+ EXPECT_EQ(0, p->m.mNumbers[0]);
+ }
+
+ // values
+ EXPECT_EQ(100, ins2->m.mNumbers[0]);
+ EXPECT_EQ(100, outs2->m.mNumbers[0]);
+ EXPECT_EQ(0, bins1->m.mNumbers[0]);
+ EXPECT_EQ(0, bins2->m.mNumbers[0]);
+ EXPECT_EQ(0, bouts1->m.mNumbers[0]);
+ EXPECT_EQ(0, bouts2->m.mNumbers[0]);
+
+ EXPECT_EQ(1u, ins2->stream());
+ EXPECT_EQ(1u, outs2->stream());
+ EXPECT_EQ(0u, bins1->stream());
+ EXPECT_EQ(0u, bins2->stream());
+ EXPECT_EQ(0u, bouts1->stream());
+ EXPECT_EQ(0u, bouts2->stream());
+
+ EXPECT_TRUE(*ins1 != *outs1);
+ EXPECT_TRUE(*ins1 == *ins2);
+ EXPECT_TRUE(*outs1 == *outs2);
+ EXPECT_TRUE(*bins1 == *bouts1);
+ EXPECT_TRUE(*bins2 != *bouts2);
+
+ EXPECT_TRUE(*ins1 != *bins1);
+ bins1->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*ins1 != *bins1);
+ bins1->setPort(false /* output */);
+ EXPECT_TRUE(*ins1 != *bins1);
+ bins1->setStream(1u);
+ EXPECT_TRUE(*ins1 == *bins1);
+
+ EXPECT_TRUE(*ins2 != *bins2);
+ bins2->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*ins2 != *bins2);
+ bins2->setStream(1u);
+ EXPECT_TRUE(*ins2 == *bins2);
+
+ bins1->setPort(true /* output */);
+ EXPECT_TRUE(*outs1 == *bins1);
+
+ EXPECT_TRUE(*outs1 != *bouts1);
+ bouts1->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*outs1 != *bouts1);
+ bouts1->setPort(true /* output */);
+ EXPECT_TRUE(*outs1 != *bouts1);
+ bouts1->setStream(1u);
+ EXPECT_TRUE(*outs1 == *bouts1);
+
+ EXPECT_TRUE(*outs2 != *bouts2);
+ bouts2->m.mNumbers[0] = 100;
+ EXPECT_TRUE(*outs2 != *bouts2);
+ bouts2->setStream(1u);
+ EXPECT_TRUE(*outs2 == *bouts2);
+
+ bouts1->setPort(false /* output */);
+ EXPECT_TRUE(*ins1 == *bouts1);
+
+ // index
+ EXPECT_EQ(C2Param::Type(ins1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(ins1->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(ins1->type(), C2NumbersStreamTuning::input::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(ins2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(ins2->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(ins2->type(), C2NumbersStreamTuning::input::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(outs1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outs1->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(outs1->type(), C2NumbersStreamTuning::output::typeIndex);
+
+ EXPECT_EQ(C2Param::Type(outs2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+ EXPECT_EQ(C2Param::Type(outs2->type()).paramIndex(), kParamIndexNumbers);
+ EXPECT_EQ(outs2->type(), C2NumbersStreamTuning::output::typeIndex);
+
+ C2Param::BaseIndex index = C2NumbersStreamTuning::input::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+ index = C2NumbersStreamTuning::output::typeIndex;
+ EXPECT_FALSE(index.isVendor());
+ EXPECT_TRUE(index.isFlexible());
+ EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+ EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+ C2Param::Type type = C2NumbersStreamTuning::input::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_TRUE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_TRUE(type.forInput());
+ EXPECT_FALSE(type.forOutput());
+ EXPECT_TRUE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ type = C2NumbersStreamTuning::output::typeIndex;
+ EXPECT_FALSE(type.isVendor());
+ EXPECT_TRUE(type.isFlexible());
+ EXPECT_FALSE(type.isGlobal());
+ EXPECT_FALSE(type.forInput());
+ EXPECT_TRUE(type.forOutput());
+ EXPECT_TRUE(type.forStream());
+ EXPECT_FALSE(type.forPort());
+
+ EXPECT_EQ(C2NumbersPortTuning::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(nullptr), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(ins1.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(ins2.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(outs1.get()), nullptr);
+ EXPECT_EQ(C2NumbersTuning::From(outs2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::From(ins1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::From(ins2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::From(outs1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::From(outs2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(ins1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(ins2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(outs1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::input::From(outs2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(ins1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(ins2.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(outs1.get()), nullptr);
+ EXPECT_EQ(C2NumbersPortTuning::output::From(outs2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::From(ins1.get()), ins1.get());
+ EXPECT_EQ(C2NumbersStreamTuning::From(ins2.get()), (C2NumbersStreamTuning*)ins2.get());
+ EXPECT_EQ(C2NumbersStreamTuning::From(outs1.get()), outs1.get());
+ EXPECT_EQ(C2NumbersStreamTuning::From(outs2.get()), (C2NumbersStreamTuning*)outs2.get());
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(ins1.get()), (C2NumbersStreamTuning::input*)ins1.get());
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(ins2.get()), ins2.get());
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(outs1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::input::From(outs2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(ins1.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(ins2.get()), nullptr);
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(outs1.get()), (C2NumbersStreamTuning::output*)outs1.get());
+ EXPECT_EQ(C2NumbersStreamTuning::output::From(outs2.get()), outs2.get());
+
+ }
+
+ {
+ C2Int32Value int32Value(INT32_MIN);
+ static_assert(std::is_same<decltype(int32Value.mValue), int32_t>::value, "should be int32_t");
+ EXPECT_EQ(INT32_MIN, int32Value.mValue);
+ std::list<const C2FieldDescriptor> fields = int32Value.fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::INT32, fields.cbegin()->type());
+ EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+ }
+
+ {
+ C2Uint32Value uint32Value(UINT32_MAX);
+ static_assert(std::is_same<decltype(uint32Value.mValue), uint32_t>::value, "should be uint32_t");
+ EXPECT_EQ(UINT32_MAX, uint32Value.mValue);
+ std::list<const C2FieldDescriptor> fields = uint32Value.fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::UINT32, fields.cbegin()->type());
+ EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+ }
+
+ {
+ C2Int64Value int64Value(INT64_MIN);
+ static_assert(std::is_same<decltype(int64Value.mValue), int64_t>::value, "should be int64_t");
+ EXPECT_EQ(INT64_MIN, int64Value.mValue);
+ std::list<const C2FieldDescriptor> fields = int64Value.fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::INT64, fields.cbegin()->type());
+ EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+ }
+
+ {
+ C2Uint64Value uint64Value(UINT64_MAX);
+ static_assert(std::is_same<decltype(uint64Value.mValue), uint64_t>::value, "should be uint64_t");
+ EXPECT_EQ(UINT64_MAX, uint64Value.mValue);
+ std::list<const C2FieldDescriptor> fields = uint64Value.fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::UINT64, fields.cbegin()->type());
+ EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+ }
+
+ {
+ C2FloatValue floatValue(123.4f);
+ static_assert(std::is_same<decltype(floatValue.mValue), float>::value, "should be float");
+ EXPECT_EQ(123.4f, floatValue.mValue);
+ std::list<const C2FieldDescriptor> fields = floatValue.fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::FLOAT, fields.cbegin()->type());
+ EXPECT_EQ(1u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+ }
+
+ {
+ uint8_t initValue[] = "ABCD";
+ typedef C2GlobalParam<C2Setting, C2BlobValue, 0> BlobSetting;
+ std::unique_ptr<BlobSetting> blobValue = BlobSetting::alloc_unique(6, C2ConstMemoryBlock<uint8_t>(initValue));
+ static_assert(std::is_same<decltype(blobValue->m.mValue), uint8_t[]>::value, "should be uint8_t[]");
+ EXPECT_EQ(0, memcmp(blobValue->m.mValue, "ABCD\0", 6));
+ EXPECT_EQ(6u, blobValue->flexCount());
+ std::list<const C2FieldDescriptor> fields = blobValue->fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::BLOB, fields.cbegin()->type());
+ EXPECT_EQ(0u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+
+ blobValue = BlobSetting::alloc_unique(3, C2ConstMemoryBlock<uint8_t>(initValue));
+ EXPECT_EQ(0, memcmp(blobValue->m.mValue, "ABC", 3));
+ EXPECT_EQ(3u, blobValue->flexCount());
+ }
+
+ {
+ constexpr char initValue[] = "ABCD";
+ typedef C2GlobalParam<C2Setting, C2StringValue, 0> StringSetting;
+ std::unique_ptr<StringSetting> stringValue = StringSetting::alloc_unique(6, C2ConstMemoryBlock<char>(initValue));
+ stringValue = StringSetting::alloc_unique(6, initValue);
+ static_assert(std::is_same<decltype(stringValue->m.mValue), char[]>::value, "should be char[]");
+ EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABCD\0", 6));
+ EXPECT_EQ(6u, stringValue->flexCount());
+ std::list<const C2FieldDescriptor> fields = stringValue->fieldList;
+ EXPECT_EQ(1u, fields.size());
+ EXPECT_EQ(FD::STRING, fields.cbegin()->type());
+ EXPECT_EQ(0u, fields.cbegin()->length());
+ EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+
+ stringValue = StringSetting::alloc_unique(3, C2ConstMemoryBlock<char>(initValue));
+ EXPECT_EQ(0, memcmp(stringValue->m.mValue, "AB", 3));
+ EXPECT_EQ(3u, stringValue->flexCount());
+
+ stringValue = StringSetting::alloc_unique(11, "initValue");
+ EXPECT_EQ(0, memcmp(stringValue->m.mValue, "initValue\0", 11));
+ EXPECT_EQ(11u, stringValue->flexCount());
+
+ stringValue = StringSetting::alloc_unique(initValue);
+ EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABCD", 5));
+ EXPECT_EQ(5u, stringValue->flexCount());
+
+ stringValue = StringSetting::alloc_unique({ 'A', 'B', 'C', 'D' });
+ EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABC", 4));
+ EXPECT_EQ(4u, stringValue->flexCount());
+ }
+
+ {
+ uint32_t videoWidth[] = { 12u, C2NumbersStreamTuning::output::typeIndex, 100 };
+ C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+ EXPECT_NE(nullptr, p1);
+ EXPECT_EQ(12u, p1->size());
+ EXPECT_EQ(C2NumbersStreamTuning::output::typeIndex, p1->type());
+
+ C2NumbersStreamTuning::output *vst = C2NumbersStreamTuning::output::From(p1);
+ EXPECT_NE(nullptr, vst);
+ if (vst) {
+ EXPECT_EQ(1u, vst->flexCount());
+ EXPECT_EQ(100, vst->m.mNumbers[0]);
+ }
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, 3);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, 0);
+ EXPECT_EQ(nullptr, p1);
+ }
+
+ {
+ uint32_t videoWidth[] = { 16u, C2NumbersPortTuning::input::typeIndex, 101, 102 };
+
+ C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+ EXPECT_NE(nullptr, p1);
+ EXPECT_EQ(16u, p1->size());
+ EXPECT_EQ(C2NumbersPortTuning::input::typeIndex, p1->type());
+
+ C2NumbersPortTuning::input *vpt = C2NumbersPortTuning::input::From(p1);
+ EXPECT_NE(nullptr, vpt);
+ if (vpt) {
+ EXPECT_EQ(2u, vpt->flexCount());
+ EXPECT_EQ(101, vpt->m.mNumbers[0]);
+ EXPECT_EQ(102, vpt->m.mNumbers[1]);
+ }
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, 3);
+ EXPECT_EQ(nullptr, p1);
+
+ p1 = C2Param::From(videoWidth, 0);
+ EXPECT_EQ(nullptr, p1);
+ }
+}
+
+// ***********************
+
+}
+
+#include <util/C2ParamUtils.h>
+#include <C2Config.h>
+#include <C2Component.h>
+#include <unordered_map>
+
+namespace android {
+
+C2ENUM(
+ MetadataType, int32_t,
+ kInvalid = -1,
+ kNone = 0,
+ kGralloc,
+ kNativeHandle,
+ kANativeWindow,
+ kCamera,
+)
+
+enum {
+ kParamIndexVideoConfig = 0x1234,
+};
+
+struct C2VideoConfigStruct {
+ int32_t mWidth;
+ uint32_t mHeight;
+ MetadataType mMetadataType;
+ int32_t mSupportedFormats[];
+
+ C2VideoConfigStruct() {}
+
+ DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoConfig, mSupportedFormats)
+ C2FIELD(mWidth, "width")
+ C2FIELD(mHeight, "height")
+ C2FIELD(mMetadataType, "metadata-type")
+ C2FIELD(mSupportedFormats, "formats")
+};
+
+typedef C2PortParam<C2Tuning, C2VideoConfigStruct> C2VideoConfigPortTuning;
+
+class MyReflector : public C2ParamReflector {
+private:
+ std::unique_ptr<C2VideoConfigPortTuning::input> inputVideoConfigTuning;
+ std::unique_ptr<C2VideoConfigPortTuning::output> outputVideoConfigTuning;
+
+public:
+ void describeSupportedValues() {
+ C2TypedFieldSupportedValues<int32_t> supportedWidths(16, 1920, 8);
+ C2FieldSupportedValues supportedWidths2(16, 1920, 8);
+
+
+ std::list<C2FieldSupportedValues> supported;
+ //supported.emplace_push(inputVideoConfigTuning->mNumber, range(16, 1920, 8));
+ //supported.emplace_push(inputVideoConfigTuning->mHeight, range(16, 1088, 8));
+ //supported.emplace_push(inputVideoConfigTuning->mMetadataType, all_enums);
+ //supported.emplace_push(inputVideoConfigTuning->mSupportedFormats, { 0, 1, 5, 7 });
+ }
+
+ virtual std::unique_ptr<android::C2StructDescriptor> describe(C2Param::BaseIndex paramType) {
+ switch (paramType.baseIndex()) {
+ case C2VideoConfigPortTuning::baseIndex:
+ return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{
+ paramType.baseIndex(),
+ C2VideoConfigPortTuning::fieldList,
+ });
+ }
+ return nullptr;
+ }
+};
+
+class MyComponentInstance : public C2ComponentInterface {
+public:
+ virtual C2String getName() const {
+ /// \todo this seems too specific
+ return "sample.interface";
+ };
+
+ virtual node_id getId() const {
+ /// \todo how are these shared?
+ return 0;
+ }
+
+ virtual status_t commit_sm(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ (void)params;
+ (void)failures;
+ return C2_UNSUPPORTED;
+ }
+
+ virtual status_t config_nb(
+ const std::vector<C2Param* const> ¶ms,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ (void)params;
+ (void)failures;
+ return C2_UNSUPPORTED;
+ }
+
+ virtual status_t createTunnel_sm(node_id targetComponent) {
+ (void)targetComponent;
+ return C2_UNSUPPORTED;
+ }
+
+ virtual status_t query_nb(
+ const std::vector<C2Param* const> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+ for (C2Param* const param : stackParams) {
+ if (!*param) { // param is already invalid - remember it
+ continue;
+ }
+
+ // note: this does not handle stream params (should use index...)
+ if (!mMyParams.count(param->type())) {
+ continue; // not my param
+ }
+
+ C2Param & myParam = mMyParams.find(param->type())->second;
+ if (myParam.size() != param->size()) { // incorrect size
+ param->invalidate();
+ continue;
+ }
+
+ param->updateFrom(myParam);
+ }
+
+ for (const C2Param::Index index : heapParamIndices) {
+ if (mMyParams.count(index)) {
+ C2Param & myParam = mMyParams.find(index)->second;
+ std::unique_ptr<C2Param> paramCopy(C2Param::From(&myParam, myParam.size()));
+ heapParams->push_back(std::move(paramCopy));
+ }
+ }
+
+ return C2_OK;
+ }
+
+ std::unordered_map<uint32_t, C2Param &> mMyParams;
+
+ C2ComponentDomainInfo mDomainInfo;
+
+ MyComponentInstance() {
+ mMyParams.insert({mDomainInfo.type(), mDomainInfo});
+ }
+
+ virtual status_t releaseTunnel_sm(node_id targetComponent) {
+ (void)targetComponent;
+ return C2_UNSUPPORTED;
+ }
+
+ class MyParamReflector : public C2ParamReflector {
+ const MyComponentInstance *instance;
+
+ public:
+ MyParamReflector(const MyComponentInstance *i) : instance(i) { }
+
+ virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) {
+ switch (paramIndex.baseIndex()) {
+ case decltype(instance->mDomainInfo)::baseIndex:
+ default:
+ return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{
+ instance->mDomainInfo.type(),
+ decltype(instance->mDomainInfo)::fieldList,
+ });
+ }
+ return nullptr;
+ }
+ };
+
+ virtual status_t getSupportedValues(
+ const std::vector<const C2ParamField> fields,
+ std::vector<C2FieldSupportedValues>* const values) const {
+ for (const C2ParamField &field : fields) {
+ if (field == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::mValue)) {
+ values->push_back(C2FieldSupportedValues(
+ false /* flag */,
+ &mDomainInfo.mValue
+ //,
+ //{(int32_t)C2DomainVideo}
+ ));
+ }
+ }
+ return C2_OK;
+ }
+
+ virtual std::shared_ptr<C2ParamReflector> getParamReflector() const {
+ return std::shared_ptr<C2ParamReflector>(new MyParamReflector(this));
+ }
+
+ virtual status_t getSupportedParams(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const {
+ params->push_back(std::make_shared<C2ParamDescriptor>(
+ true /* required */, "_domain", &mDomainInfo));
+ return C2_OK;
+ }
+
+ status_t getSupportedParams2(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) {
+ params->push_back(std::shared_ptr<C2ParamDescriptor>(
+ new C2ParamDescriptor(true /* required */, "_domain", &mDomainInfo)));
+ return C2_OK;
+ }
+
+};
+
+template<typename E, bool S=std::is_enum<E>::value>
+struct getter {
+ int32_t get(const C2FieldSupportedValues::Primitive &p, int32_t*) {
+ return p.i32;
+ }
+ int64_t get(const C2FieldSupportedValues::Primitive &p, int64_t*) {
+ return p.i64;
+ }
+ uint32_t get(const C2FieldSupportedValues::Primitive &p, uint32_t*) {
+ return p.u32;
+ }
+ uint64_t get(const C2FieldSupportedValues::Primitive &p, uint64_t*) {
+ return p.u64;
+ }
+ float get(const C2FieldSupportedValues::Primitive &p, float*) {
+ return p.fp;
+ }
+};
+
+template<typename E>
+struct getter<E, true> {
+ typename std::underlying_type<E>::type get(const C2FieldSupportedValues::Primitive &p, E*) {
+ using u=typename std::underlying_type<E>::type;
+ return getter<u>().get(p, (u*)0);
+ }
+};
+
+template<typename T, bool E=std::is_enum<T>::value>
+struct lax_underlying_type {
+ typedef typename std::underlying_type<T>::type type;
+};
+
+template<typename T>
+struct lax_underlying_type<T, false> {
+ typedef T type;
+};
+
+template<typename E>
+typename lax_underlying_type<E>::type get(
+ const C2FieldSupportedValues::Primitive &p, E*) {
+ return getter<E>().get(p, (E*)0);
+}
+
+template<typename T>
+void dumpFSV(const C2FieldSupportedValues &sv, T*t) {
+ using namespace std;
+ cout << (std::is_enum<T>::value ? (std::is_signed<typename std::underlying_type<T>::type>::value ? "i" : "u")
+ : std::is_integral<T>::value ? std::is_signed<T>::value ? "i" : "u" : "f")
+ << (8 * sizeof(T));
+ if (sv.type == sv.RANGE) {
+ cout << ".range(" << get(sv.range.min, t);
+ if (get(sv.range.step, t) != std::is_integral<T>::value) {
+ cout << ":" << get(sv.range.step, t);
+ }
+ if (get(sv.range.nom, t) != 1 || get(sv.range.denom, t) != 1) {
+ cout << ":" << get(sv.range.nom, t) << "/" << get(sv.range.denom, t);
+ }
+ cout << get(sv.range.max, t) << ")";
+ }
+ if (sv.values.size()) {
+ cout << (sv.type == sv.FLAGS ? ".flags(" : ".list(");
+ const char *sep = "";
+ for (const C2FieldSupportedValues::Primitive &p : sv.values) {
+ cout << sep << get(p, t);
+ sep = ",";
+ }
+ cout << ")";
+ }
+ cout << endl;
+}
+
+void dumpType(C2Param::Type type) {
+ using namespace std;
+ cout << (type.isVendor() ? "Vendor" : "C2");
+ if (type.forInput()) {
+ cout << "Input";
+ } else if (type.forOutput()) {
+ cout << "Output";
+ } else if (type.forPort() && !type.forStream()) {
+ cout << "Port";
+ }
+ if (type.forStream()) {
+ cout << "Stream";
+ }
+
+ if (type.isFlexible()) {
+ cout << "Flex";
+ }
+
+ cout << type.paramIndex();
+
+ switch (type.kind()) {
+ case C2Param::INFO: cout << "Info"; break;
+ case C2Param::SETTING: cout << "Setting"; break;
+ case C2Param::TUNING: cout << "Tuning"; break;
+ case C2Param::STRUCT: cout << "Struct"; break;
+ default: cout << "Kind" << (int32_t)type.kind(); break;
+ }
+}
+
+void dumpType(C2Param::BaseIndex type) {
+ using namespace std;
+ cout << (type.isVendor() ? "Vendor" : "C2");
+ if (type.isFlexible()) {
+ cout << "Flex";
+ }
+
+ cout << type.paramIndex() << "Struct";
+}
+
+void dumpType(FD::Type type) {
+ using namespace std;
+ switch (type) {
+ case FD::BLOB: cout << "blob "; break;
+ case FD::FLOAT: cout << "float "; break;
+ case FD::INT32: cout << "int32_t "; break;
+ case FD::INT64: cout << "int64_t "; break;
+ case FD::UINT32: cout << "uint32_t "; break;
+ case FD::UINT64: cout << "uint64_t "; break;
+ case FD::STRING: cout << "char "; break;
+ default:
+ cout << "struct ";
+ dumpType((C2Param::Type)type);
+ break;
+ }
+}
+
+void dumpStruct(const C2StructDescriptor &sd) {
+ using namespace std;
+ cout << "struct ";
+ dumpType(sd.baseIndex());
+ cout << " {" << endl;
+ //C2FieldDescriptor &f;
+ for (const C2FieldDescriptor &f : sd) {
+ PrintTo(f, &cout);
+ cout << endl;
+
+ if (f.namedValues().size()) {
+ cout << ".named(";
+ const char *sep = "";
+ for (const FD::named_value_type &p : f.namedValues()) {
+ cout << sep << p.first << "=";
+ switch (f.type()) {
+ case C2Value::INT32: cout << get(p.second, (int32_t *)0); break;
+ case C2Value::INT64: cout << get(p.second, (int64_t *)0); break;
+ case C2Value::UINT32: cout << get(p.second, (uint32_t *)0); break;
+ case C2Value::UINT64: cout << get(p.second, (uint64_t *)0); break;
+ case C2Value::FLOAT: cout << get(p.second, (float *)0); break;
+ default: cout << "???"; break;
+ }
+ sep = ",";
+ }
+ cout << ")";
+ }
+ }
+
+ cout << "};" << endl;
+}
+
+void dumpDesc(const C2ParamDescriptor &pd) {
+ using namespace std;
+ if (pd.isRequired()) {
+ cout << "required ";
+ }
+ if (pd.isPersistent()) {
+ cout << "persistent ";
+ }
+ cout << "struct ";
+ dumpType(pd.type());
+ cout << " " << pd.name() << ";" << endl;
+}
+
+TEST_F(C2ParamTest, ReflectorTest) {
+ C2ComponentDomainInfo domainInfo;
+ std::shared_ptr<C2ComponentInterface> comp(new MyComponentInstance);
+ std::vector<C2FieldSupportedValues> values;
+
+ std::unique_ptr<C2StructDescriptor> desc{
+ comp->getParamReflector()->describe(C2ComponentDomainInfo::indexFlags)};
+ dumpStruct(*desc);
+
+ EXPECT_EQ(
+ C2_OK,
+ comp->getSupportedValues(
+ { C2ParamField(&domainInfo, &C2ComponentDomainInfo::mValue) },
+ &values)
+ );
+
+ for (const C2FieldSupportedValues &sv : values) {
+ dumpFSV(sv, &domainInfo.mValue);
+ }
+}
+
+C2ENUM(Enum1, uint32_t,
+ Enum1Value1,
+ Enum1Value2,
+ Enum1Value4 = Enum1Value2 + 2,
+);
+
+C2ENUM_CUSTOM_PREFIX(Enum2, uint32_t, "Enum",
+ Enum2Value1,
+ Enum2Value2,
+ Enum2Value4 = Enum1Value2 + 2,
+);
+
+C2ENUM_CUSTOM_NAMES(Enum3, uint8_t,
+ ({ { "value1", Enum3Value1 },
+ { "value2", Enum3Value2 },
+ { "value4", Enum3Value4 },
+ { "invalid", Invalid } }),
+ Enum3Value1,
+ Enum3Value2,
+ Enum3Value4 = Enum3Value2 + 2,
+ Invalid,
+);
+
+TEST_F(C2ParamTest, EnumUtilsTest) {
+ std::vector<std::pair<C2String, Enum3>> pairs ( { { "value1", Enum3Value1 },
+ { "value2", Enum3Value2 },
+ { "value4", Enum3Value4 },
+ { "invalid", Invalid } });
+ Enum3 e3;
+ FD::namedValuesFor(e3);
+}
+
+TEST_F(C2ParamTest, ParamUtilsTest) {
+ // upper case
+ EXPECT_EQ("yes", C2ParamUtils::camelCaseToDashed("YES"));
+ EXPECT_EQ("no", C2ParamUtils::camelCaseToDashed("NO"));
+ EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YES_NO"));
+ EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YES__NO"));
+ EXPECT_EQ("a2dp", C2ParamUtils::camelCaseToDashed("A2DP"));
+ EXPECT_EQ("mp2-ts", C2ParamUtils::camelCaseToDashed("MP2_TS"));
+ EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("BLOCK_2D"));
+ EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("MPEG_2_TS"));
+ EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_HIDDEN_VALUE"));
+ EXPECT_EQ("__hidden-value2", C2ParamUtils::camelCaseToDashed("__HIDDEN_VALUE2"));
+ EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HIDDEN_VALUE_2"));
+
+ // camel case
+ EXPECT_EQ("yes", C2ParamUtils::camelCaseToDashed("Yes"));
+ EXPECT_EQ("no", C2ParamUtils::camelCaseToDashed("No"));
+ EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YesNo"));
+ EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("Yes_No"));
+ EXPECT_EQ("mp2-ts", C2ParamUtils::camelCaseToDashed("MP2Ts"));
+ EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("Block2D"));
+ EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg2ts"));
+ EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_HiddenValue"));
+ EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HiddenValue2"));
+
+ // mixed case
+ EXPECT_EQ("mp2t-s", C2ParamUtils::camelCaseToDashed("MP2T_s"));
+ EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("Block_2D"));
+ EXPECT_EQ("block-2-d", C2ParamUtils::camelCaseToDashed("Block2_D"));
+ EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg_2ts"));
+ EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg_2_TS"));
+ EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_Hidden__VALUE"));
+ EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HiddenValue_2"));
+ EXPECT_EQ("_2", C2ParamUtils::camelCaseToDashed("_2"));
+ EXPECT_EQ("__23", C2ParamUtils::camelCaseToDashed("__23"));
+}
+
+TEST_F(C2ParamTest, C2ValueTest) {
+ C2Value val;
+ int32_t i32 = -32;
+ int64_t i64 = -64;
+ uint32_t u32 = 32;
+ uint64_t u64 = 64;
+ float fp = 1.5f;
+
+ EXPECT_EQ(C2Value::NO_INIT, val.type());
+ EXPECT_EQ(false, val.get(&i32));
+ EXPECT_EQ(-32, i32);
+ EXPECT_EQ(false, val.get(&i64));
+ EXPECT_EQ(-64, i64);
+ EXPECT_EQ(false, val.get(&u32));
+ EXPECT_EQ(32u, u32);
+ EXPECT_EQ(false, val.get(&u64));
+ EXPECT_EQ(64u, u64);
+ EXPECT_EQ(false, val.get(&fp));
+ EXPECT_EQ(1.5f, fp);
+
+ val = int32_t(-3216);
+ EXPECT_EQ(C2Value::INT32, val.type());
+ EXPECT_EQ(true, val.get(&i32));
+ EXPECT_EQ(-3216, i32);
+ EXPECT_EQ(false, val.get(&i64));
+ EXPECT_EQ(-64, i64);
+ EXPECT_EQ(false, val.get(&u32));
+ EXPECT_EQ(32u, u32);
+ EXPECT_EQ(false, val.get(&u64));
+ EXPECT_EQ(64u, u64);
+ EXPECT_EQ(false, val.get(&fp));
+ EXPECT_EQ(1.5f, fp);
+
+ val = uint32_t(3216);
+ EXPECT_EQ(C2Value::UINT32, val.type());
+ EXPECT_EQ(false, val.get(&i32));
+ EXPECT_EQ(-3216, i32);
+ EXPECT_EQ(false, val.get(&i64));
+ EXPECT_EQ(-64, i64);
+ EXPECT_EQ(true, val.get(&u32));
+ EXPECT_EQ(3216u, u32);
+ EXPECT_EQ(false, val.get(&u64));
+ EXPECT_EQ(64u, u64);
+ EXPECT_EQ(false, val.get(&fp));
+ EXPECT_EQ(1.5f, fp);
+
+ val = int64_t(-6432);
+ EXPECT_EQ(C2Value::INT64, val.type());
+ EXPECT_EQ(false, val.get(&i32));
+ EXPECT_EQ(-3216, i32);
+ EXPECT_EQ(true, val.get(&i64));
+ EXPECT_EQ(-6432, i64);
+ EXPECT_EQ(false, val.get(&u32));
+ EXPECT_EQ(3216u, u32);
+ EXPECT_EQ(false, val.get(&u64));
+ EXPECT_EQ(64u, u64);
+ EXPECT_EQ(false, val.get(&fp));
+ EXPECT_EQ(1.5f, fp);
+
+ val = uint64_t(6432);
+ EXPECT_EQ(C2Value::UINT64, val.type());
+ EXPECT_EQ(false, val.get(&i32));
+ EXPECT_EQ(-3216, i32);
+ EXPECT_EQ(false, val.get(&i64));
+ EXPECT_EQ(-6432, i64);
+ EXPECT_EQ(false, val.get(&u32));
+ EXPECT_EQ(3216u, u32);
+ EXPECT_EQ(true, val.get(&u64));
+ EXPECT_EQ(6432u, u64);
+ EXPECT_EQ(false, val.get(&fp));
+ EXPECT_EQ(1.5f, fp);
+
+ val = 15.25f;
+ EXPECT_EQ(C2Value::FLOAT, val.type());
+ EXPECT_EQ(false, val.get(&i32));
+ EXPECT_EQ(-3216, i32);
+ EXPECT_EQ(false, val.get(&i64));
+ EXPECT_EQ(-6432, i64);
+ EXPECT_EQ(false, val.get(&u32));
+ EXPECT_EQ(3216u, u32);
+ EXPECT_EQ(false, val.get(&u64));
+ EXPECT_EQ(6432u, u64);
+ EXPECT_EQ(true, val.get(&fp));
+ EXPECT_EQ(15.25f, fp);
+}
+
+} // namespace android
diff --git a/media/libstagefright/codec2/tests/C2_test.cpp b/media/libstagefright/codec2/tests/C2_test.cpp
new file mode 100644
index 0000000..92a3d91
--- /dev/null
+++ b/media/libstagefright/codec2/tests/C2_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2_test"
+
+#include <gtest/gtest.h>
+
+#include <C2.h>
+
+namespace android {
+
+/* ======================================= STATIC TESTS ======================================= */
+
+template<int N>
+struct c2_const_checker
+{
+ inline constexpr static int num() { return N; }
+};
+
+constexpr auto min_i32_i32 = c2_min(int32_t(1), int32_t(2));
+static_assert(std::is_same<decltype(min_i32_i32), const int32_t>::value, "should be int32_t");
+constexpr auto min_i32_i64 = c2_min(int32_t(3), int64_t(2));
+static_assert(std::is_same<decltype(min_i32_i64), const int64_t>::value, "should be int64_t");
+constexpr auto min_i8_i32 = c2_min(int8_t(0xff), int32_t(0xffffffff));
+static_assert(std::is_same<decltype(min_i8_i32), const int32_t>::value, "should be int32_t");
+
+static_assert(c2_const_checker<min_i32_i32>::num() == 1, "should be 1");
+static_assert(c2_const_checker<min_i32_i64>::num() == 2, "should be 2");
+static_assert(c2_const_checker<min_i8_i32>::num() == 0xffffffff, "should be 0xffffffff");
+
+constexpr auto min_u32_u32 = c2_min(uint32_t(1), uint32_t(2));
+static_assert(std::is_same<decltype(min_u32_u32), const uint32_t>::value, "should be uint32_t");
+constexpr auto min_u32_u64 = c2_min(uint32_t(3), uint64_t(2));
+static_assert(std::is_same<decltype(min_u32_u64), const uint32_t>::value, "should be uint32_t");
+constexpr auto min_u32_u8 = c2_min(uint32_t(0xffffffff), uint8_t(0xff));
+static_assert(std::is_same<decltype(min_u32_u8), const uint8_t>::value, "should be uint8_t");
+
+static_assert(c2_const_checker<min_u32_u32>::num() == 1, "should be 1");
+static_assert(c2_const_checker<min_u32_u64>::num() == 2, "should be 2");
+static_assert(c2_const_checker<min_u32_u8>::num() == 0xff, "should be 0xff");
+
+constexpr auto max_i32_i32 = c2_max(int32_t(1), int32_t(2));
+static_assert(std::is_same<decltype(max_i32_i32), const int32_t>::value, "should be int32_t");
+constexpr auto max_i32_i64 = c2_max(int32_t(3), int64_t(2));
+static_assert(std::is_same<decltype(max_i32_i64), const int64_t>::value, "should be int64_t");
+constexpr auto max_i8_i32 = c2_max(int8_t(0xff), int32_t(0xffffffff));
+static_assert(std::is_same<decltype(max_i8_i32), const int32_t>::value, "should be int32_t");
+
+static_assert(c2_const_checker<max_i32_i32>::num() == 2, "should be 2");
+static_assert(c2_const_checker<max_i32_i64>::num() == 3, "should be 3");
+static_assert(c2_const_checker<max_i8_i32>::num() == 0xffffffff, "should be 0xffffffff");
+
+constexpr auto max_u32_u32 = c2_max(uint32_t(1), uint32_t(2));
+static_assert(std::is_same<decltype(max_u32_u32), const uint32_t>::value, "should be uint32_t");
+constexpr auto max_u32_u64 = c2_max(uint32_t(3), uint64_t(2));
+static_assert(std::is_same<decltype(max_u32_u64), const uint64_t>::value, "should be uint64_t");
+constexpr auto max_u32_u8 = c2_max(uint32_t(0x7fffffff), uint8_t(0xff));
+static_assert(std::is_same<decltype(max_u32_u8), const uint32_t>::value, "should be uint32_t");
+
+static_assert(c2_const_checker<max_u32_u32>::num() == 2, "should be 2");
+static_assert(c2_const_checker<max_u32_u64>::num() == 3, "should be 3");
+static_assert(c2_const_checker<max_u32_u8>::num() == 0x7fffffff, "should be 0x7fffffff");
+
+} // namespace android
diff --git a/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp b/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp
new file mode 100644
index 0000000..7a1374b
--- /dev/null
+++ b/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <util/_C2MacroUtils.h>
+
+/** \file
+ * Tests for vndk/util.
+ */
+
+/* --------------------------------------- _C2MacroUtils --------------------------------------- */
+
+static_assert(0 == _C2_ARGC(), "should be 0");
+static_assert(1 == _C2_ARGC(1), "should be 1");
+static_assert(2 == _C2_ARGC(1, 2), "should be 2");
+static_assert(64 == _C2_ARGC(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64), "should be 64");
+
+static_assert(0 == _C2_ARGC(,), "should be 0");
+static_assert(1 == _C2_ARGC(1,), "should be 1");
+static_assert(2 == _C2_ARGC(1, 2,), "should be 2");
+static_assert(64 == _C2_ARGC(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,), "should be 64");
+
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
new file mode 100644
index 0000000..edae303
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2016 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 C2UTILS_PARAM_UTILS_H_
+#define C2UTILS_PARAM_UTILS_H_
+
+#include <C2Param.h>
+#include <util/_C2MacroUtils.h>
+
+#include <iostream>
+
+/** \file
+ * Utilities for parameter handling to be used by Codec2 implementations.
+ */
+
+namespace android {
+
+/// \cond INTERNAL
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+/**
+ * Utility class that allows ignoring enum value assignment (e.g. both '(_C2EnumConst)kValue = x'
+ * and '(_C2EnumConst)kValue' will eval to kValue.
+ */
+template<typename T>
+class _C2EnumConst {
+public:
+ // implicit conversion from T
+ inline _C2EnumConst(T value) : _mValue(value) {}
+ // implicit conversion to T
+ inline operator T() { return _mValue; }
+ // implicit conversion to C2Value::Primitive
+ inline operator C2Value::Primitive() { return (T)_mValue; }
+ // ignore assignment and return T here to avoid implicit conversion to T later
+ inline T &operator =(T value __unused) { return _mValue; }
+private:
+ T _mValue;
+};
+
+/// mapper to get name of enum
+/// \note this will contain any initialization, which we will remove when converting to lower-case
+#define _C2_GET_ENUM_NAME(x, y) #x
+/// mapper to get value of enum
+#define _C2_GET_ENUM_VALUE(x, type) (_C2EnumConst<type>)x
+
+/// \endcond
+
+#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
+template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+ return C2ParamUtils::sanitizeEnumValues( \
+ std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
+ { _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
+ prefix); \
+}
+
+#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
+template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+ return C2ParamUtils::customEnumValues( \
+ std::vector<std::pair<C2StringLiteral, name>> names); \
+}
+
+
+class C2ParamUtils {
+private:
+ static size_t countLeadingUnderscores(C2StringLiteral a) {
+ size_t i = 0;
+ while (a[i] == '_') {
+ ++i;
+ }
+ return i;
+ }
+
+ static size_t countMatching(C2StringLiteral a, const C2String &b) {
+ for (size_t i = 0; i < b.size(); ++i) {
+ if (!a[i] || a[i] != b[i]) {
+ return i;
+ }
+ }
+ return b.size();
+ }
+
+ // ABCDef => abc-def
+ // ABCD2ef => abcd2-ef // 0
+ // ABCD2Ef => ancd2-ef // -1
+ // AbcDef => abc-def // -1
+ // Abc2Def => abc-2def
+ // Abc2def => abc-2-def
+ // _Yo => _yo
+ // _yo => _yo
+ // C2_yo => c2-yo
+ // C2__yo => c2-yo
+
+ static C2String camelCaseToDashed(C2String name) {
+ enum {
+ kNone = '.',
+ kLower = 'a',
+ kUpper = 'A',
+ kDigit = '1',
+ kDash = '-',
+ kUnderscore = '_',
+ } type = kNone;
+ size_t word_start = 0;
+ for (size_t ix = 0; ix < name.size(); ++ix) {
+ /* std::cout << name.substr(0, word_start) << "|"
+ << name.substr(word_start, ix - word_start) << "["
+ << name.substr(ix, 1) << "]" << name.substr(ix + 1)
+ << ": " << (char)type << std::endl; */
+ if (isupper(name[ix])) {
+ if (type == kLower) {
+ name.insert(ix++, 1, '-');
+ word_start = ix;
+ }
+ name[ix] = tolower(name[ix]);
+ type = kUpper;
+ } else if (islower(name[ix])) {
+ if (type == kDigit && ix > 0) {
+ name.insert(ix++, 1, '-');
+ word_start = ix;
+ } else if (type == kUpper && ix > word_start + 1) {
+ name.insert(ix++ - 1, 1, '-');
+ word_start = ix - 1;
+ }
+ type = kLower;
+ } else if (isdigit(name[ix])) {
+ if (type == kLower) {
+ name.insert(ix++, 1, '-');
+ word_start = ix;
+ }
+ type = kDigit;
+ } else if (name[ix] == '_') {
+ if (type == kDash) {
+ name.erase(ix--, 1);
+ } else if (type != kNone && type != kUnderscore) {
+ name[ix] = '-';
+ type = kDash;
+ word_start = ix + 1;
+ } else {
+ type = kUnderscore;
+ word_start = ix + 1;
+ }
+ } else {
+ name.resize(ix);
+ }
+ }
+ // std::cout << "=> " << name << std::endl;
+ return name;
+ }
+
+ static std::vector<C2String> sanitizeEnumValueNames(
+ const std::vector<C2StringLiteral> names,
+ C2StringLiteral _prefix = NULL) {
+ std::vector<C2String> sanitizedNames;
+ C2String prefix;
+ size_t extraUnderscores = 0;
+ bool first = true;
+ if (_prefix) {
+ extraUnderscores = countLeadingUnderscores(_prefix);
+ prefix = _prefix + extraUnderscores;
+ first = false;
+ // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
+ }
+
+ // calculate prefix and minimum leading underscores
+ for (C2StringLiteral s : names) {
+ // std::cout << s << std::endl;
+ size_t underscores = countLeadingUnderscores(s);
+ if (first) {
+ extraUnderscores = underscores;
+ prefix = s + underscores;
+ first = false;
+ } else {
+ size_t matching = countMatching(
+ s + underscores,
+ prefix);
+ prefix.resize(matching);
+ extraUnderscores = std::min(underscores, extraUnderscores);
+ }
+ // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
+ if (prefix.size() == 0 && extraUnderscores == 0) {
+ break;
+ }
+ }
+
+ // we swallow the first underscore after upper case prefixes
+ bool upperCasePrefix = true;
+ for (size_t i = 0; i < prefix.size(); ++i) {
+ if (islower(prefix[i])) {
+ upperCasePrefix = false;
+ break;
+ }
+ }
+
+ for (C2StringLiteral s : names) {
+ size_t underscores = countLeadingUnderscores(s);
+ C2String sanitized = C2String(s, underscores - extraUnderscores);
+ sanitized.append(s + prefix.size() + underscores +
+ (upperCasePrefix && s[prefix.size() + underscores] == '_'));
+ sanitizedNames.push_back(camelCaseToDashed(sanitized));
+ }
+
+ for (C2String s : sanitizedNames) {
+ std::cout << s << std::endl;
+ }
+
+ return sanitizedNames;
+ }
+
+ friend class C2ParamTest_ParamUtilsTest_Test;
+
+public:
+ static std::vector<C2String> getEnumValuesFromString(C2StringLiteral value) {
+ std::vector<C2String> foundNames;
+ size_t pos = 0, len = strlen(value);
+ do {
+ size_t endPos = strcspn(value + pos, " ,=") + pos;
+ if (endPos > pos) {
+ foundNames.emplace_back(value + pos, endPos - pos);
+ }
+ if (value[endPos] && value[endPos] != ',') {
+ endPos += strcspn(value + endPos, ",");
+ }
+ pos = strspn(value + endPos, " ,") + endPos;
+ } while (pos < len);
+ return foundNames;
+ }
+
+ template<typename T>
+ static C2FieldDescriptor::named_values_type sanitizeEnumValues(
+ std::vector<T> values,
+ std::vector<C2StringLiteral> names,
+ C2StringLiteral prefix = NULL) {
+ C2FieldDescriptor::named_values_type namedValues;
+ std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
+ for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
+ namedValues.emplace_back(sanitizedNames[i], values[i]);
+ }
+ return namedValues;
+ }
+
+ template<typename E>
+ static C2FieldDescriptor::named_values_type customEnumValues(
+ std::vector<std::pair<C2StringLiteral, E>> items) {
+ C2FieldDescriptor::named_values_type namedValues;
+ for (auto &item : items) {
+ namedValues.emplace_back(item.first, item.second);
+ }
+ return namedValues;
+ }
+};
+
+/* ---------------------------- UTILITIES FOR PARAMETER REFLECTION ---------------------------- */
+
+/* ======================== UTILITY TEMPLATES FOR PARAMETER REFLECTION ======================== */
+
+#if 1
+template<typename... Params>
+class C2_HIDE _C2Tuple { };
+
+C2_HIDE
+void addC2Params(std::list<const C2FieldDescriptor> &, _C2Tuple<> *) {
+}
+
+template<typename T, typename... Params>
+C2_HIDE
+void addC2Params(std::list<const C2FieldDescriptor> &fields, _C2Tuple<T, Params...> *)
+{
+ //C2Param::index_t index = T::baseIndex;
+ //(void)index;
+ fields.insert(fields.end(), T::fieldList);
+ addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
+}
+
+template<typename... Params>
+C2_HIDE
+std::list<const C2FieldDescriptor> describeC2Params() {
+ std::list<const C2FieldDescriptor> fields;
+ addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
+ return fields;
+}
+
+#endif
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+} // namespace android
+
+#endif // C2UTILS_PARAM_UTILS_H_
+
diff --git a/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h b/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h
new file mode 100644
index 0000000..04e9ba5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 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 C2UTILS_MACRO_UTILS_H_
+#define C2UTILS_MACRO_UTILS_H_
+
+/** \file
+ * Macro utilities for the utils library used by Codec2 implementations.
+ */
+
+/// \if 0
+
+/* --------------------------------- VARIABLE ARGUMENT COUNTING --------------------------------- */
+
+// remove empty arguments - _C2_ARG() expands to '', while _C2_ARG(x) expands to ', x'
+// _C2_ARGn(...) does the same for n arguments
+#define _C2_ARG(...) , ##__VA_ARGS__
+#define _C2_ARG2(_1, _2) _C2_ARG(_1) _C2_ARG(_2)
+#define _C2_ARG4(_1, _2, _3, _4) _C2_ARG2(_1, _2) _C2_ARG2(_3, _4)
+#define _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG4(_1, _2, _3, _4) _C2_ARG4(_5, _6, _7, _8)
+#define _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG8(_9, _10, _11, _12, _13, _14, _15, _16)
+
+// return the 65th argument
+#define _C2_ARGC_3(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+ _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+ _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+ _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) _64
+
+/// \endif
+
+/**
+ * Returns the number of arguments.
+ */
+// We do this by prepending 1 and appending 65 designed values such that the 65th element
+// will be the number of arguments.
+#define _C2_ARGC(...) _C2_ARGC_1(0, ##__VA_ARGS__, \
+ 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, \
+ 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
+ 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+/// \if 0
+
+// step 1. remove empty arguments - this is needed to allow trailing comma in enum definitions
+// (NOTE: we don't know which argument will have this trailing comma so we have to try all)
+#define _C2_ARGC_1(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+ _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+ _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+ _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) \
+ _C2_ARGC_2(_ _C2_ARG(_0) \
+ _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+ _C2_ARG16(_17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) \
+ _C2_ARG16(_33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) \
+ _C2_ARG16(_49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64), \
+ ##__VA_ARGS__)
+
+// step 2. this is needed as removed arguments cannot be passed directly as empty into a macro
+#define _C2_ARGC_2(...) _C2_ARGC_3(__VA_ARGS__)
+
+/// \endif
+
+/* -------------------------------- VARIABLE ARGUMENT CONVERSION -------------------------------- */
+
+/// \if 0
+
+// macros that convert _1, _2, _3, ... to fn(_1, arg), fn(_2, arg), fn(_3, arg), ...
+#define _C2_MAP_64(fn, arg, head, ...) fn(head, arg), _C2_MAP_63(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_63(fn, arg, head, ...) fn(head, arg), _C2_MAP_62(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_62(fn, arg, head, ...) fn(head, arg), _C2_MAP_61(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_61(fn, arg, head, ...) fn(head, arg), _C2_MAP_60(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_60(fn, arg, head, ...) fn(head, arg), _C2_MAP_59(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_59(fn, arg, head, ...) fn(head, arg), _C2_MAP_58(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_58(fn, arg, head, ...) fn(head, arg), _C2_MAP_57(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_57(fn, arg, head, ...) fn(head, arg), _C2_MAP_56(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_56(fn, arg, head, ...) fn(head, arg), _C2_MAP_55(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_55(fn, arg, head, ...) fn(head, arg), _C2_MAP_54(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_54(fn, arg, head, ...) fn(head, arg), _C2_MAP_53(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_53(fn, arg, head, ...) fn(head, arg), _C2_MAP_52(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_52(fn, arg, head, ...) fn(head, arg), _C2_MAP_51(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_51(fn, arg, head, ...) fn(head, arg), _C2_MAP_50(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_50(fn, arg, head, ...) fn(head, arg), _C2_MAP_49(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_49(fn, arg, head, ...) fn(head, arg), _C2_MAP_48(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_48(fn, arg, head, ...) fn(head, arg), _C2_MAP_47(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_47(fn, arg, head, ...) fn(head, arg), _C2_MAP_46(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_46(fn, arg, head, ...) fn(head, arg), _C2_MAP_45(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_45(fn, arg, head, ...) fn(head, arg), _C2_MAP_44(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_44(fn, arg, head, ...) fn(head, arg), _C2_MAP_43(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_43(fn, arg, head, ...) fn(head, arg), _C2_MAP_42(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_42(fn, arg, head, ...) fn(head, arg), _C2_MAP_41(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_41(fn, arg, head, ...) fn(head, arg), _C2_MAP_40(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_40(fn, arg, head, ...) fn(head, arg), _C2_MAP_39(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_39(fn, arg, head, ...) fn(head, arg), _C2_MAP_38(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_38(fn, arg, head, ...) fn(head, arg), _C2_MAP_37(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_37(fn, arg, head, ...) fn(head, arg), _C2_MAP_36(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_36(fn, arg, head, ...) fn(head, arg), _C2_MAP_35(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_35(fn, arg, head, ...) fn(head, arg), _C2_MAP_34(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_34(fn, arg, head, ...) fn(head, arg), _C2_MAP_33(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_33(fn, arg, head, ...) fn(head, arg), _C2_MAP_32(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_32(fn, arg, head, ...) fn(head, arg), _C2_MAP_31(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_31(fn, arg, head, ...) fn(head, arg), _C2_MAP_30(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_30(fn, arg, head, ...) fn(head, arg), _C2_MAP_29(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_29(fn, arg, head, ...) fn(head, arg), _C2_MAP_28(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_28(fn, arg, head, ...) fn(head, arg), _C2_MAP_27(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_27(fn, arg, head, ...) fn(head, arg), _C2_MAP_26(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_26(fn, arg, head, ...) fn(head, arg), _C2_MAP_25(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_25(fn, arg, head, ...) fn(head, arg), _C2_MAP_24(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_24(fn, arg, head, ...) fn(head, arg), _C2_MAP_23(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_23(fn, arg, head, ...) fn(head, arg), _C2_MAP_22(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_22(fn, arg, head, ...) fn(head, arg), _C2_MAP_21(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_21(fn, arg, head, ...) fn(head, arg), _C2_MAP_20(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_20(fn, arg, head, ...) fn(head, arg), _C2_MAP_19(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_19(fn, arg, head, ...) fn(head, arg), _C2_MAP_18(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_18(fn, arg, head, ...) fn(head, arg), _C2_MAP_17(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_17(fn, arg, head, ...) fn(head, arg), _C2_MAP_16(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_16(fn, arg, head, ...) fn(head, arg), _C2_MAP_15(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_15(fn, arg, head, ...) fn(head, arg), _C2_MAP_14(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_14(fn, arg, head, ...) fn(head, arg), _C2_MAP_13(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_13(fn, arg, head, ...) fn(head, arg), _C2_MAP_12(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_12(fn, arg, head, ...) fn(head, arg), _C2_MAP_11(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_11(fn, arg, head, ...) fn(head, arg), _C2_MAP_10(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_10(fn, arg, head, ...) fn(head, arg), _C2_MAP_9(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_9(fn, arg, head, ...) fn(head, arg), _C2_MAP_8(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_8(fn, arg, head, ...) fn(head, arg), _C2_MAP_7(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_7(fn, arg, head, ...) fn(head, arg), _C2_MAP_6(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_6(fn, arg, head, ...) fn(head, arg), _C2_MAP_5(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_5(fn, arg, head, ...) fn(head, arg), _C2_MAP_4(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_4(fn, arg, head, ...) fn(head, arg), _C2_MAP_3(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_3(fn, arg, head, ...) fn(head, arg), _C2_MAP_2(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_2(fn, arg, head, ...) fn(head, arg), _C2_MAP_1(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_1(fn, arg, head, ...) fn(head, arg)
+
+/// \endif
+
+/**
+ * Maps each argument using another macro x -> fn(x, arg)
+ */
+// use wrapper to call the proper mapper based on the number of arguments
+#define _C2_MAP(fn, arg, ...) _C2_MAP__(_C2_ARGC(__VA_ARGS__), fn, arg, ##__VA_ARGS__)
+
+/// \if 0
+
+// evaluate _n so it becomes a number
+#define _C2_MAP__(_n, fn, arg, ...) _C2_MAP_(_n, fn, arg, __VA_ARGS__)
+// call the proper mapper
+#define _C2_MAP_(_n, fn, arg, ...) _C2_MAP_##_n (fn, arg, __VA_ARGS__)
+
+/// \endif
+
+#endif // C2UTILS_MACRO_UTILS_H_
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index 8249d66..cfca608 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -61,12 +61,10 @@
LOCAL_SHARED_LIBRARIES := \
libmedia \
libstagefright_avc_common \
- libstagefright_enc_common \
libstagefright_foundation \
libstagefright_omx \
libutils \
liblog \
- libui
LOCAL_MODULE := libstagefright_soft_h264enc
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index cecc52b..e2bba25 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -49,58 +49,10 @@
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
static const CodecProfileLevel kProfileLevels[] = {
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5 },
- { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
{ OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel42 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel5 },
- { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel51 },
{ OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1b },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel11 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel12 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel13 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel2 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel21 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel22 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel3 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel31 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel32 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel4 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel41 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel42 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel5 },
- { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel51 },
{ OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 },
};
@@ -120,7 +72,8 @@
mIvColorFormat(IV_YUV_420P),
mChangingResolution(false),
mSignalledError(false),
- mStride(mWidth){
+ mStride(mWidth),
+ mInputOffset(0){
initPorts(
1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE,
1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE);
@@ -215,6 +168,7 @@
status_t SoftAVC::resetPlugin() {
mIsInFlush = false;
mReceivedEOS = false;
+ mInputOffset = 0;
memset(mTimeStamps, 0, sizeof(mTimeStamps));
memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
@@ -443,8 +397,8 @@
if (inHeader) {
ps_dec_ip->u4_ts = timeStampIx;
ps_dec_ip->pv_stream_buffer =
- inHeader->pBuffer + inHeader->nOffset;
- ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+ inHeader->pBuffer + inHeader->nOffset + mInputOffset;
+ ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen - mInputOffset;
} else {
ps_dec_ip->u4_ts = 0;
ps_dec_ip->pv_stream_buffer = NULL;
@@ -515,6 +469,8 @@
void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
UNUSED(portIndex);
+ OMX_BUFFERHEADERTYPE *inHeader = NULL;
+ BufferInfo *inInfo = NULL;
if (mSignalledError) {
return;
@@ -541,17 +497,11 @@
List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
while (!outQueue.empty()) {
- BufferInfo *inInfo;
- OMX_BUFFERHEADERTYPE *inHeader;
-
BufferInfo *outInfo;
OMX_BUFFERHEADERTYPE *outHeader;
- size_t timeStampIx;
+ size_t timeStampIx = 0;
- inInfo = NULL;
- inHeader = NULL;
-
- if (!mIsInFlush) {
+ if (!mIsInFlush && (NULL == inHeader)) {
if (!inQueue.empty()) {
inInfo = *inQueue.begin();
inHeader = inInfo->mHeader;
@@ -618,7 +568,7 @@
return;
}
// If input dump is enabled, then write to file
- DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
GETTIME(&mTimeStart, NULL);
/* Compute time elapsed between end of previous decode()
@@ -734,24 +684,26 @@
resetPlugin();
}
}
+ mInputOffset += s_dec_op.u4_num_bytes_consumed;
}
-
- /* If input EOS is seen and decoder is not in flush mode,
- * set the decoder in flush mode.
- * There can be a case where EOS is sent along with last picture data
- * In that case, only after decoding that input data, decoder has to be
- * put in flush. This case is handled here */
-
- if (mReceivedEOS && !mIsInFlush) {
- setFlushMode();
- }
-
- if (inHeader != NULL) {
+ // If more than 4 bytes are remaining in input, then do not release it
+ if (inHeader != NULL && ((inHeader->nFilledLen - mInputOffset) <= 4)) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
+ mInputOffset = 0;
+
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+
+ if (mReceivedEOS && !mIsInFlush) {
+ setFlushMode();
+ }
}
}
}
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
index b333190..2a71188 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -99,6 +99,7 @@
bool mFlushNeeded;
bool mSignalledError;
size_t mStride;
+ size_t mInputOffset;
status_t initDecoder();
status_t deInitDecoder();
@@ -143,10 +144,10 @@
ALOGD("Could not open file %s", m_filename); \
} \
}
-#define DUMP_TO_FILE(m_filename, m_buf, m_size) \
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\
{ \
FILE *fp = fopen(m_filename, "ab"); \
- if (fp != NULL && m_buf != NULL) { \
+ if (fp != NULL && m_buf != NULL && m_offset == 0) { \
int i; \
i = fwrite(m_buf, 1, m_size, fp); \
ALOGD("fwrite ret %d to write %d", i, m_size); \
@@ -154,10 +155,12 @@
ALOGD("Error in fwrite, returned %d", i); \
perror("Error in write to file"); \
} \
- fclose(fp); \
- } else { \
+ } else if (fp == NULL) { \
ALOGD("Could not write to file %s", m_filename);\
} \
+ if (fp) { \
+ fclose(fp); \
+ } \
}
#else /* FILE_DUMP_ENABLE */
#define INPUT_DUMP_PATH
@@ -166,7 +169,7 @@
#define OUTPUT_DUMP_EXT
#define GENERATE_FILE_NAMES()
#define CREATE_DUMP_FILE(m_filename)
-#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)
#endif /* FILE_DUMP_ENABLE */
} // namespace android
diff --git a/media/libstagefright/codecs/avcenc/Android.mk b/media/libstagefright/codecs/avcenc/Android.mk
index 2d24584..1b1a1a0 100644
--- a/media/libstagefright/codecs/avcenc/Android.mk
+++ b/media/libstagefright/codecs/avcenc/Android.mk
@@ -17,7 +17,6 @@
LOCAL_SHARED_LIBRARIES := libmedia
LOCAL_SHARED_LIBRARIES += libstagefright_omx
-LOCAL_SHARED_LIBRARIES += libstagefright_foundation
LOCAL_SHARED_LIBRARIES += libutils
LOCAL_SHARED_LIBRARIES += liblog
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index b002208..11978a1 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -9,7 +9,7 @@
frameworks/native/include/media/openmax
LOCAL_SHARED_LIBRARIES := \
- libmedia libstagefright_omx libstagefright_foundation libutils liblog
+ libmedia libstagefright_omx libutils liblog
LOCAL_MODULE := libstagefright_soft_g711dec
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk
index 5d6c289..eed1348 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.mk
+++ b/media/libstagefright/codecs/gsm/dec/Android.mk
@@ -13,7 +13,7 @@
LOCAL_SANITIZE := signed-integer-overflow unsigned-integer-overflow
LOCAL_SHARED_LIBRARIES := \
- libmedia libstagefright_omx libstagefright_foundation libutils liblog
+ libmedia libstagefright_omx libutils liblog
LOCAL_STATIC_LIBRARIES := \
libgsm
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
index 5c70387..a3fd336 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -48,14 +48,6 @@
(IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
static const CodecProfileLevel kProfileLevels[] = {
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel1 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel2 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel21 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel3 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel31 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel4 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel41 },
- { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel5 },
{ OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel51 },
};
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 1ff8b47..7b706fe 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -64,13 +64,9 @@
LOCAL_SHARED_LIBRARIES := \
libmedia \
- libstagefright_enc_common \
- libstagefright_foundation \
libstagefright_omx \
libutils \
liblog \
- libui
-
LOCAL_MODULE := libstagefright_soft_mpeg4enc
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 5ed037a..ce28faf 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -47,14 +47,8 @@
(IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
static const CodecProfileLevel kProfileLevels[] = {
- { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelLL },
- { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelML },
- { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelH14 },
{ OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL },
- { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelLL },
- { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelML },
- { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelH14 },
{ OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelHL },
};
diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk
index f8019d0..5c85bbd 100644
--- a/media/libstagefright/codecs/on2/enc/Android.mk
+++ b/media/libstagefright/codecs/on2/enc/Android.mk
@@ -20,7 +20,6 @@
LOCAL_SHARED_LIBRARIES := \
libmedia libstagefright_omx libstagefright_foundation libutils liblog \
- libhardware \
LOCAL_MODULE := libstagefright_soft_vpxenc
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index f26c840..30e3643 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -360,25 +360,6 @@
ALOGV("Sent kWhatEOS.");
}
-void MediaFilter::sendFormatChange() {
- sp<AMessage> notify = mNotify->dup();
-
- notify->setInt32("what", kWhatOutputFormatChanged);
-
- AString mime;
- CHECK(mOutputFormat->findString("mime", &mime));
- notify->setString("mime", mime.c_str());
-
- notify->setInt32("stride", mStride);
- notify->setInt32("slice-height", mSliceHeight);
- notify->setInt32("color-format", mColorFormatOut);
- notify->setRect("crop", 0, 0, mStride - 1, mSliceHeight - 1);
- notify->setInt32("width", mWidth);
- notify->setInt32("height", mHeight);
-
- notify->post();
-}
-
void MediaFilter::requestFillEmptyInput() {
if (mPortEOS[kPortIndexInput]) {
return;
@@ -553,8 +534,6 @@
notify->post();
mState = CONFIGURED;
ALOGV("Handled kWhatConfigureComponent.");
-
- sendFormatChange();
}
void MediaFilter::onStart() {
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8b9472e..e654a01 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -518,9 +518,10 @@
return err;
}
-status_t LiveSession::seekTo(int64_t timeUs) {
+status_t LiveSession::seekTo(int64_t timeUs, bool precise) {
sp<AMessage> msg = new AMessage(kWhatSeek, this);
msg->setInt64("timeUs", timeUs);
+ msg->setInt32("precise", precise);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
@@ -1441,7 +1442,10 @@
void LiveSession::onSeek(const sp<AMessage> &msg) {
int64_t timeUs;
+ int32_t precise;
CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("precise", &precise));
+ // TODO: add "precise" to changeConfiguration.
changeConfiguration(timeUs);
}
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 65a824e..4dc529c 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -86,7 +86,7 @@
status_t disconnect();
// Blocks until seek is complete.
- status_t seekTo(int64_t timeUs);
+ status_t seekTo(int64_t timeUs, bool precise);
status_t getDuration(int64_t *durationUs) const;
size_t getTrackCount() const;
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index bda29fa..85ee4ee 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -28,6 +28,7 @@
class IOMXBufferSource;
class IOMXObserver;
struct OMXMaster;
+class OMXBuffer;
struct OMXNodeInstance : public BnOMXNode {
OMXNodeInstance(
@@ -64,22 +65,6 @@
OMX_U32 portIndex, OMX_BOOL tunneled,
OMX_U32 audioHwSync, native_handle_t **sidebandHandle);
- status_t useBuffer(
- OMX_U32 portIndex, const sp<IMemory> ¶ms,
- OMX::buffer_id *buffer, OMX_U32 allottedSize);
-
- status_t useGraphicBuffer(
- OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id *buffer);
-
- status_t updateGraphicBufferInMeta(
- OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
- OMX::buffer_id buffer);
-
- status_t updateNativeHandleInMeta(
- OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
- OMX::buffer_id buffer);
-
status_t setInputSurface(
const sp<IOMXBufferSource> &bufferSource);
@@ -87,19 +72,18 @@
OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
void **buffer_data, sp<NativeHandle> *native_handle);
- status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer);
+ status_t useBuffer(
+ OMX_U32 portIndex, const OMXBuffer &omxBuf, buffer_id *buffer);
- status_t fillBuffer(OMX::buffer_id buffer, int fenceFd);
+ status_t freeBuffer(
+ OMX_U32 portIndex, buffer_id buffer);
+
+ status_t fillBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd = -1);
status_t emptyBuffer(
- OMX::buffer_id buffer,
- OMX_U32 rangeOffset, OMX_U32 rangeLength,
- OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
-
- status_t emptyGraphicBuffer(
- OMX::buffer_id buffer,
- const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
- OMX_TICKS timestamp, int fenceFd);
+ buffer_id buffer, const OMXBuffer &omxBuf,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1);
status_t getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index);
@@ -196,9 +180,43 @@
bool isProhibitedIndex_l(OMX_INDEXTYPE index);
+ status_t useBuffer(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer, OMX_U32 allottedSize);
+
+ status_t useBuffer_l(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer, OMX_U32 allottedSize);
+
+ status_t useGraphicBuffer(
+ OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+ OMX::buffer_id *buffer);
+
+ status_t useGraphicBufferWithMetadata_l(
+ OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+ OMX::buffer_id *buffer);
+
status_t useGraphicBuffer2_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
OMX::buffer_id *buffer);
+
+ status_t emptyBuffer(
+ OMX::buffer_id buffer,
+ OMX_U32 rangeOffset, OMX_U32 rangeLength,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+ status_t emptyGraphicBuffer(
+ OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+ status_t emptyNativeHandleBuffer(
+ OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+ status_t emptyBuffer_l(
+ OMX_BUFFERHEADERTYPE *header,
+ OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd);
+
static OMX_ERRORTYPE OnEvent(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
@@ -229,10 +247,6 @@
int retrieveFenceFromMeta_l(
OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex);
- status_t emptyBuffer_l(
- OMX_BUFFERHEADERTYPE *header,
- OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd);
-
// Updates the graphic buffer handle in the metadata buffer for |buffer| and |header| to
// |graphicBuffer|'s handle. If |updateCodecBuffer| is true, the update will happen in
// the actual codec buffer (use this if not using emptyBuffer (with no _l) later to
@@ -242,6 +256,10 @@
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+ status_t updateNativeHandleInMeta_l(
+ OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
+ OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+
sp<IOMXBufferSource> getBufferSource();
void setBufferSource(const sp<IOMXBufferSource> &bufferSource);
// Called when omx_message::FILL_BUFFER_DONE is received. (Currently the
diff --git a/media/libstagefright/include/SecureBuffer.h b/media/libstagefright/include/SecureBuffer.h
index 4bb1418..ac7399a 100644
--- a/media/libstagefright/include/SecureBuffer.h
+++ b/media/libstagefright/include/SecureBuffer.h
@@ -34,7 +34,7 @@
*/
class SecureBuffer : public MediaCodecBuffer {
public:
- SecureBuffer(const sp<AMessage> &format, void *ptr, size_t size);
+ SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size);
SecureBuffer(const sp<AMessage> &format, const sp<NativeHandle> &handle, size_t size);
virtual ~SecureBuffer() = default;
@@ -42,6 +42,8 @@
void *getDestinationPointer();
ICrypto::DestinationType getDestinationType();
+ virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format) override;
+
private:
SecureBuffer() = delete;
diff --git a/media/libstagefright/include/SharedMemoryBuffer.h b/media/libstagefright/include/SharedMemoryBuffer.h
index 1d7f7a6..c52e5c5 100644
--- a/media/libstagefright/include/SharedMemoryBuffer.h
+++ b/media/libstagefright/include/SharedMemoryBuffer.h
@@ -34,6 +34,8 @@
virtual ~SharedMemoryBuffer() = default;
+ virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format) override;
+
private:
SharedMemoryBuffer() = delete;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index b060628..a974671 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -377,6 +377,16 @@
*actualFrameTimeUs = -1ll;
+ if (seekTimeUs > INT64_MAX / 1000ll ||
+ seekTimeUs < INT64_MIN / 1000ll ||
+ (mExtractor->mSeekPreRollNs > 0 &&
+ (seekTimeUs * 1000ll) < INT64_MIN + mExtractor->mSeekPreRollNs) ||
+ (mExtractor->mSeekPreRollNs < 0 &&
+ (seekTimeUs * 1000ll) > INT64_MAX + mExtractor->mSeekPreRollNs)) {
+ ALOGE("cannot seek to %lld", (long long) seekTimeUs);
+ return;
+ }
+
const int64_t seekTimeNs = seekTimeUs * 1000ll - mExtractor->mSeekPreRollNs;
mkvparser::Segment* const pSegment = mExtractor->mSegment;
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index ee4fc05..72f8043 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -21,7 +21,6 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
- libhardware \
libmedia \
libutils \
liblog \
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 4575d28..4909100 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -34,6 +34,7 @@
#include "omx/OMXUtils.h"
#include <OMX_Component.h>
#include <OMX_IndexExt.h>
+#include "OMXBuffer.h"
#include <inttypes.h>
#include "FrameDropper.h"
@@ -524,7 +525,8 @@
if (mPrevCaptureUs < 0ll) {
// first capture
mPrevCaptureUs = timeUs;
- mPrevFrameUs = timeUs;
+ // adjust the first sample timestamp.
+ mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
} else {
// snap to nearest capture point
int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
@@ -578,7 +580,7 @@
const sp<GraphicBuffer> &buffer = codecBuffer.mGraphicBuffer;
int fenceID = item.mFence->isValid() ? item.mFence->dup() : -1;
- status_t err = mOMXNode->emptyGraphicBuffer(
+ status_t err = mOMXNode->emptyBuffer(
bufferID, buffer, OMX_BUFFERFLAG_ENDOFFRAME, codecTimeUs, fenceID);
if (err != OK) {
@@ -611,8 +613,8 @@
CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
IOMX::buffer_id bufferID = codecBuffer.mBufferID;
- status_t err = mOMXNode->emptyGraphicBuffer(
- bufferID, NULL /* buffer */,
+ status_t err = mOMXNode->emptyBuffer(
+ bufferID, (sp<GraphicBuffer>)NULL,
OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
0 /* timestamp */, -1 /* fenceFd */);
if (err != OK) {
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 41f3e4a..fdc9d7f 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -40,6 +40,7 @@
#include <media/stagefright/MediaErrors.h>
#include <utils/misc.h>
#include <utils/NativeHandle.h>
+#include <media/OMXBuffer.h>
static const OMX_U32 kPortIndexInput = 0;
static const OMX_U32 kPortIndexOutput = 1;
@@ -901,6 +902,21 @@
}
status_t OMXNodeInstance::useBuffer(
+ OMX_U32 portIndex,
+ const OMXBuffer &omxBuffer, OMX::buffer_id *buffer) {
+ // TODO: the allotted size is probably no longer needed.
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeSharedMem) {
+ return useBuffer(portIndex, omxBuffer.mMem, buffer, omxBuffer.mAllottedSize);
+ }
+
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+ return useGraphicBuffer(portIndex, omxBuffer.mGraphicBuffer, buffer);
+ }
+
+ return BAD_VALUE;
+}
+
+status_t OMXNodeInstance::useBuffer(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
OMX::buffer_id *buffer, OMX_U32 allottedSize) {
if (params == NULL || buffer == NULL) {
@@ -913,6 +929,12 @@
return BAD_VALUE;
}
+ return useBuffer_l(portIndex, params, buffer, allottedSize);
+}
+
+status_t OMXNodeInstance::useBuffer_l(
+ OMX_U32 portIndex, const sp<IMemory> ¶ms,
+ OMX::buffer_id *buffer, OMX_U32 allottedSize) {
BufferMeta *buffer_meta;
OMX_BUFFERHEADERTYPE *header;
OMX_ERRORTYPE err = OMX_ErrorNone;
@@ -937,16 +959,18 @@
if (err != OMX_ErrorNone) {
CLOG_ERROR(allocateBuffer, err,
- SIMPLE_BUFFER(portIndex, (size_t)allottedSize, params->pointer()));
+ SIMPLE_BUFFER(portIndex, (size_t)allottedSize,
+ params != NULL ? params->pointer() : NULL));
}
} else {
- OMX_U8 *data = static_cast<OMX_U8 *>(params->pointer());
+ OMX_U8 *data = NULL;
// metadata buffers are not connected cross process
// use a backup buffer instead of the actual buffer
if (isMetadata) {
+ // TODO: this logic is very fishy, should it be removed?
// if we are not connecting the buffers, the sizes must match
- if (allottedSize != params->size()) {
+ if (params != NULL && allottedSize != params->size()) {
CLOG_ERROR(useBuffer, BAD_VALUE, SIMPLE_BUFFER(portIndex, (size_t)allottedSize, data));
return BAD_VALUE;
}
@@ -960,6 +984,10 @@
buffer_meta = new BufferMeta(
params, portIndex, false /* copy */, data);
} else {
+ // NULL params is allowed only in metadata mode.
+ CHECK(params != NULL);
+ data = static_cast<OMX_U8 *>(params->pointer());
+
buffer_meta = new BufferMeta(
params, portIndex, false /* copy */, NULL);
}
@@ -1064,6 +1092,13 @@
}
Mutex::Autolock autoLock(mLock);
+ // First, see if we're in metadata mode. We could be running an experiment to simulate
+ // legacy behavior (preallocated buffers) on devices that supports meta.
+ if (mMetadataType[portIndex] != kMetadataBufferTypeInvalid) {
+ return useGraphicBufferWithMetadata_l(
+ portIndex, graphicBuffer, buffer);
+ }
+
// See if the newer version of the extension is present.
OMX_INDEXTYPE index;
if (OMX_GetExtensionIndex(
@@ -1119,6 +1154,33 @@
return OK;
}
+status_t OMXNodeInstance::useGraphicBufferWithMetadata_l(
+ OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+ OMX::buffer_id *buffer) {
+ if (portIndex != kPortIndexOutput) {
+ return BAD_VALUE;
+ }
+
+ OMX_U32 allottedSize = 0;
+ if (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource) {
+ allottedSize = sizeof(VideoGrallocMetadata);
+ } else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer) {
+ allottedSize = sizeof(VideoNativeMetadata);
+ } else {
+ return BAD_VALUE;
+ }
+
+ status_t err = useBuffer_l(portIndex, NULL, buffer, allottedSize);
+ if (err != OK) {
+ return err;
+ }
+
+ OMX_BUFFERHEADERTYPE *header = findBufferHeader(*buffer, portIndex);
+
+ return updateGraphicBufferInMeta_l(portIndex, graphicBuffer, *buffer, header);
+
+}
+
status_t OMXNodeInstance::updateGraphicBufferInMeta_l(
OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
@@ -1159,20 +1221,9 @@
return OK;
}
-status_t OMXNodeInstance::updateGraphicBufferInMeta(
- OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
- OMX::buffer_id buffer) {
- Mutex::Autolock autoLock(mLock);
- OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, portIndex);
-
- return updateGraphicBufferInMeta_l(
- portIndex, graphicBuffer, buffer, header);
-}
-
-status_t OMXNodeInstance::updateNativeHandleInMeta(
- OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle, OMX::buffer_id buffer) {
- Mutex::Autolock autoLock(mLock);
- OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, portIndex);
+status_t OMXNodeInstance::updateNativeHandleInMeta_l(
+ OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle,
+ OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
// No need to check |nativeHandle| since NULL is valid for it as below.
if (header == NULL) {
ALOGE("b/25884056");
@@ -1340,7 +1391,8 @@
return StatusFromOMXError(err);
}
-status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer, int fenceFd) {
+status_t OMXNodeInstance::fillBuffer(
+ OMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
Mutex::Autolock autoLock(mLock);
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexOutput);
@@ -1348,6 +1400,20 @@
ALOGE("b/25884056");
return BAD_VALUE;
}
+
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+ status_t err = updateGraphicBufferInMeta_l(
+ kPortIndexOutput, omxBuffer.mGraphicBuffer, buffer, header);
+
+ if (err != OK) {
+ CLOG_ERROR(fillBuffer, err, FULL_BUFFER(
+ (intptr_t)header->pBuffer, header, fenceFd));
+ return err;
+ }
+ } else if (omxBuffer.mBufferType != OMXBuffer::kBufferTypePreset) {
+ return BAD_VALUE;
+ }
+
header->nFilledLen = 0;
header->nOffset = 0;
header->nFlags = 0;
@@ -1375,6 +1441,27 @@
}
status_t OMXNodeInstance::emptyBuffer(
+ buffer_id buffer, const OMXBuffer &omxBuffer,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypePreset) {
+ return emptyBuffer(
+ buffer, 0, omxBuffer.mRangeLength, flags, timestamp, fenceFd);
+ }
+
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+ return emptyGraphicBuffer(
+ buffer, omxBuffer.mGraphicBuffer, flags, timestamp, fenceFd);
+ }
+
+ if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeNativeHandle) {
+ return emptyNativeHandleBuffer(
+ buffer, omxBuffer.mNativeHandle, flags, timestamp, fenceFd);
+ }
+
+ return BAD_VALUE;
+}
+
+status_t OMXNodeInstance::emptyBuffer(
OMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
@@ -1601,6 +1688,31 @@
return timestamp;
}
+status_t OMXNodeInstance::emptyNativeHandleBuffer(
+ OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
+ if (header == NULL) {
+ ALOGE("b/25884056");
+ return BAD_VALUE;
+ }
+
+ status_t err = updateNativeHandleInMeta_l(
+ kPortIndexInput, nativeHandle, buffer, header);
+ if (err != OK) {
+ CLOG_ERROR(emptyNativeHandleBuffer, err, FULL_BUFFER(
+ (intptr_t)header->pBuffer, header, fenceFd));
+ return err;
+ }
+
+ header->nOffset = 0;
+ header->nFilledLen = (nativeHandle == NULL) ? 0 : sizeof(VideoNativeMetadata);
+
+ return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer, fenceFd);
+}
+
void OMXNodeInstance::codecBufferFilled(omx_message &msg) {
Mutex::Autolock autoLock(mBufferIDLock);
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index ea99e53..935d7bf 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -38,6 +38,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/SimpleDecodingSource.h>
+#include <media/OMXBuffer.h>
#define DEFAULT_TIMEOUT 500000
@@ -210,8 +211,7 @@
buffer.mFlags = 0;
CHECK(buffer.mMemory != NULL);
- err = mOMXNode->useBuffer(
- portIndex, buffer.mMemory, &buffer.mID, buffer.mMemory->size());
+ err = mOMXNode->useBuffer(portIndex, buffer.mMemory, &buffer.mID);
EXPECT_SUCCESS(err, "useBuffer");
buffers->push(buffer);
@@ -338,7 +338,7 @@
"executing state.");
for (size_t i = 0; i < outputBuffers.size(); ++i) {
- err = mOMXNode->fillBuffer(outputBuffers[i].mID);
+ err = mOMXNode->fillBuffer(outputBuffers[i].mID, OMXBuffer::sPreset);
EXPECT_SUCCESS(err, "fillBuffer");
outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
@@ -363,7 +363,7 @@
}
for (size_t i = 0; i < outputBuffers.size(); ++i) {
- err = mOMXNode->fillBuffer(outputBuffers[i].mID);
+ err = mOMXNode->fillBuffer(outputBuffers[i].mID, OMXBuffer::sPreset);
EXPECT_SUCCESS(err, "fillBuffer");
outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 902f66c..c5322a4 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -24,7 +24,6 @@
libbinder \
libcutils \
liblog \
- libgui \
libmedia \
libstagefright \
libstagefright_foundation \
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
deleted file mode 100644
index 08d202b..0000000
--- a/media/libstagefright/yuv/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- YUVImage.cpp \
- YUVCanvas.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog
-
-LOCAL_MODULE:= libstagefright_yuv
-
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_SANITIZE := signed-integer-overflow
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/yuv/MODULE_LICENSE_APACHE2 b/media/libstagefright/yuv/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/media/libstagefright/yuv/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/media/libstagefright/yuv/NOTICE b/media/libstagefright/yuv/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/media/libstagefright/yuv/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
deleted file mode 100644
index 4c9fee8..0000000
--- a/media/libstagefright/yuv/YUVCanvas.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "YUVCanvas"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/YUVCanvas.h>
-#include <media/stagefright/YUVImage.h>
-#include <ui/Rect.h>
-
-namespace android {
-
-YUVCanvas::YUVCanvas(YUVImage &yuvImage)
- : mYUVImage(yuvImage) {
-}
-
-YUVCanvas::~YUVCanvas() {
-}
-
-void YUVCanvas::FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue) {
- for (int32_t y = 0; y < mYUVImage.height(); ++y) {
- for (int32_t x = 0; x < mYUVImage.width(); ++x) {
- mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
- }
- }
-}
-
-void YUVCanvas::FillYUVRectangle(const Rect& rect,
- uint8_t yValue, uint8_t uValue, uint8_t vValue) {
- for (int32_t y = rect.top; y < rect.bottom; ++y) {
- for (int32_t x = rect.left; x < rect.right; ++x) {
- mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
- }
- }
-}
-
-void YUVCanvas::CopyImageRect(
- const Rect& srcRect,
- int32_t destStartX, int32_t destStartY,
- const YUVImage &srcImage) {
-
- // Try fast copy first
- if (YUVImage::fastCopyRectangle(
- srcRect,
- destStartX, destStartY,
- srcImage, mYUVImage)) {
- return;
- }
-
- int32_t srcStartX = srcRect.left;
- int32_t srcStartY = srcRect.top;
- for (int32_t offsetY = 0; offsetY < srcRect.height(); ++offsetY) {
- for (int32_t offsetX = 0; offsetX < srcRect.width(); ++offsetX) {
- int32_t srcX = srcStartX + offsetX;
- int32_t srcY = srcStartY + offsetY;
-
- int32_t destX = destStartX + offsetX;
- int32_t destY = destStartY + offsetY;
-
- uint8_t yValue;
- uint8_t uValue;
- uint8_t vValue;
-
- srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
- mYUVImage.setPixelValue(destX, destY, yValue, uValue, vValue);
- }
- }
-}
-
-void YUVCanvas::downsample(
- int32_t srcOffsetX, int32_t srcOffsetY,
- int32_t skipX, int32_t skipY,
- const YUVImage &srcImage) {
- // TODO: Add a low pass filter for downsampling.
-
- // Check that srcImage is big enough to fill mYUVImage.
- CHECK((srcOffsetX + (mYUVImage.width() - 1) * skipX) < srcImage.width());
- CHECK((srcOffsetY + (mYUVImage.height() - 1) * skipY) < srcImage.height());
-
- uint8_t yValue;
- uint8_t uValue;
- uint8_t vValue;
-
- int32_t srcY = srcOffsetY;
- for (int32_t y = 0; y < mYUVImage.height(); ++y) {
- int32_t srcX = srcOffsetX;
- for (int32_t x = 0; x < mYUVImage.width(); ++x) {
- srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
- mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
-
- srcX += skipX;
- }
- srcY += skipY;
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
deleted file mode 100644
index c098135..0000000
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "YUVImage"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/YUVImage.h>
-#include <ui/Rect.h>
-
-namespace android {
-
-YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height) {
- mYUVFormat = yuvFormat;
- mWidth = width;
- mHeight = height;
-
- size_t numberOfBytes = bufferSize(yuvFormat, width, height);
- uint8_t *buffer = new uint8_t[numberOfBytes];
- mBuffer = buffer;
- mOwnBuffer = true;
-
- initializeYUVPointers();
-}
-
-YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer) {
- mYUVFormat = yuvFormat;
- mWidth = width;
- mHeight = height;
- mBuffer = buffer;
- mOwnBuffer = false;
-
- initializeYUVPointers();
-}
-
-//static
-size_t YUVImage::bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height) {
- int32_t numberOfPixels = width*height;
- size_t numberOfBytes = 0;
- if (yuvFormat == YUV420Planar || yuvFormat == YUV420SemiPlanar) {
- // Y takes numberOfPixels bytes and U/V take numberOfPixels/4 bytes each.
- numberOfBytes = (size_t)(numberOfPixels + (numberOfPixels >> 1));
- } else {
- ALOGE("Format not supported");
- }
- return numberOfBytes;
-}
-
-bool YUVImage::initializeYUVPointers() {
- int32_t numberOfPixels = mWidth * mHeight;
-
- if (mYUVFormat == YUV420Planar) {
- mYdata = (uint8_t *)mBuffer;
- mUdata = mYdata + numberOfPixels;
- mVdata = mUdata + (numberOfPixels >> 2);
- } else if (mYUVFormat == YUV420SemiPlanar) {
- // U and V channels are interleaved as VUVUVU.
- // So V data starts at the end of Y channel and
- // U data starts right after V's start.
- mYdata = (uint8_t *)mBuffer;
- mVdata = mYdata + numberOfPixels;
- mUdata = mVdata + 1;
- } else {
- ALOGE("Format not supported");
- return false;
- }
- return true;
-}
-
-YUVImage::~YUVImage() {
- if (mOwnBuffer) delete[] mBuffer;
-}
-
-bool YUVImage::getOffsets(int32_t x, int32_t y,
- int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const {
- *yOffset = y*mWidth + x;
-
- int32_t uvOffset = (y >> 1) * (mWidth >> 1) + (x >> 1);
- if (mYUVFormat == YUV420Planar) {
- *uOffset = uvOffset;
- *vOffset = uvOffset;
- } else if (mYUVFormat == YUV420SemiPlanar) {
- // Since U and V channels are interleaved, offsets need
- // to be doubled.
- *uOffset = 2*uvOffset;
- *vOffset = 2*uvOffset;
- } else {
- ALOGE("Format not supported");
- return false;
- }
-
- return true;
-}
-
-bool YUVImage::getOffsetIncrementsPerDataRow(
- int32_t *yDataOffsetIncrement,
- int32_t *uDataOffsetIncrement,
- int32_t *vDataOffsetIncrement) const {
- *yDataOffsetIncrement = mWidth;
-
- int32_t uvDataOffsetIncrement = mWidth >> 1;
-
- if (mYUVFormat == YUV420Planar) {
- *uDataOffsetIncrement = uvDataOffsetIncrement;
- *vDataOffsetIncrement = uvDataOffsetIncrement;
- } else if (mYUVFormat == YUV420SemiPlanar) {
- // Since U and V channels are interleaved, offsets need
- // to be doubled.
- *uDataOffsetIncrement = 2*uvDataOffsetIncrement;
- *vDataOffsetIncrement = 2*uvDataOffsetIncrement;
- } else {
- ALOGE("Format not supported");
- return false;
- }
-
- return true;
-}
-
-uint8_t* YUVImage::getYAddress(int32_t offset) const {
- return mYdata + offset;
-}
-
-uint8_t* YUVImage::getUAddress(int32_t offset) const {
- return mUdata + offset;
-}
-
-uint8_t* YUVImage::getVAddress(int32_t offset) const {
- return mVdata + offset;
-}
-
-bool YUVImage::getYUVAddresses(int32_t x, int32_t y,
- uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const {
- int32_t yOffset;
- int32_t uOffset;
- int32_t vOffset;
- if (!getOffsets(x, y, &yOffset, &uOffset, &vOffset)) return false;
-
- *yAddr = getYAddress(yOffset);
- *uAddr = getUAddress(uOffset);
- *vAddr = getVAddress(vOffset);
-
- return true;
-}
-
-bool YUVImage::validPixel(int32_t x, int32_t y) const {
- return (x >= 0 && x < mWidth &&
- y >= 0 && y < mHeight);
-}
-
-bool YUVImage::getPixelValue(int32_t x, int32_t y,
- uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
- CHECK(validPixel(x, y));
-
- uint8_t *yAddr;
- uint8_t *uAddr;
- uint8_t *vAddr;
- if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
-
- *yPtr = *yAddr;
- *uPtr = *uAddr;
- *vPtr = *vAddr;
-
- return true;
-}
-
-bool YUVImage::setPixelValue(int32_t x, int32_t y,
- uint8_t yValue, uint8_t uValue, uint8_t vValue) {
- CHECK(validPixel(x, y));
-
- uint8_t *yAddr;
- uint8_t *uAddr;
- uint8_t *vAddr;
- if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
-
- *yAddr = yValue;
- *uAddr = uValue;
- *vAddr = vValue;
-
- return true;
-}
-
-void YUVImage::fastCopyRectangle420Planar(
- const Rect& srcRect,
- int32_t destStartX, int32_t destStartY,
- const YUVImage &srcImage, YUVImage &destImage) {
- CHECK(srcImage.mYUVFormat == YUV420Planar);
- CHECK(destImage.mYUVFormat == YUV420Planar);
-
- int32_t srcStartX = srcRect.left;
- int32_t srcStartY = srcRect.top;
- int32_t width = srcRect.width();
- int32_t height = srcRect.height();
-
- // Get source and destination start addresses
- uint8_t *ySrcAddrBase;
- uint8_t *uSrcAddrBase;
- uint8_t *vSrcAddrBase;
- srcImage.getYUVAddresses(srcStartX, srcStartY,
- &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
-
- uint8_t *yDestAddrBase;
- uint8_t *uDestAddrBase;
- uint8_t *vDestAddrBase;
- destImage.getYUVAddresses(destStartX, destStartY,
- &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
-
- // Get source and destination offset increments incurred in going
- // from one data row to next.
- int32_t ySrcOffsetIncrement;
- int32_t uSrcOffsetIncrement;
- int32_t vSrcOffsetIncrement;
- srcImage.getOffsetIncrementsPerDataRow(
- &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
-
- int32_t yDestOffsetIncrement;
- int32_t uDestOffsetIncrement = 0;
- int32_t vDestOffsetIncrement = 0;
- destImage.getOffsetIncrementsPerDataRow(
- &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
-
- // Copy Y
- {
- size_t numberOfYBytesPerRow = (size_t) width;
- uint8_t *ySrcAddr = ySrcAddrBase;
- uint8_t *yDestAddr = yDestAddrBase;
- for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
- memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
-
- ySrcAddr += ySrcOffsetIncrement;
- yDestAddr += yDestOffsetIncrement;
- }
- }
-
- // Copy U
- {
- size_t numberOfUBytesPerRow = (size_t) (width >> 1);
- uint8_t *uSrcAddr = uSrcAddrBase;
- uint8_t *uDestAddr = uDestAddrBase;
- // Every other row has an entry for U/V channel values. Hence only
- // go half the height.
- for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
- memcpy(uDestAddr, uSrcAddr, numberOfUBytesPerRow);
-
- uSrcAddr += uSrcOffsetIncrement;
- uDestAddr += uDestOffsetIncrement;
- }
- }
-
- // Copy V
- {
- size_t numberOfVBytesPerRow = (size_t) (width >> 1);
- uint8_t *vSrcAddr = vSrcAddrBase;
- uint8_t *vDestAddr = vDestAddrBase;
- // Every other pixel row has a U/V data row. Hence only go half the height.
- for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
- memcpy(vDestAddr, vSrcAddr, numberOfVBytesPerRow);
-
- vSrcAddr += vSrcOffsetIncrement;
- vDestAddr += vDestOffsetIncrement;
- }
- }
-}
-
-void YUVImage::fastCopyRectangle420SemiPlanar(
- const Rect& srcRect,
- int32_t destStartX, int32_t destStartY,
- const YUVImage &srcImage, YUVImage &destImage) {
- CHECK(srcImage.mYUVFormat == YUV420SemiPlanar);
- CHECK(destImage.mYUVFormat == YUV420SemiPlanar);
-
- int32_t srcStartX = srcRect.left;
- int32_t srcStartY = srcRect.top;
- int32_t width = srcRect.width();
- int32_t height = srcRect.height();
-
- // Get source and destination start addresses
- uint8_t *ySrcAddrBase;
- uint8_t *uSrcAddrBase;
- uint8_t *vSrcAddrBase;
- srcImage.getYUVAddresses(srcStartX, srcStartY,
- &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
-
- uint8_t *yDestAddrBase;
- uint8_t *uDestAddrBase;
- uint8_t *vDestAddrBase;
- destImage.getYUVAddresses(destStartX, destStartY,
- &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
-
- // Get source and destination offset increments incurred in going
- // from one data row to next.
- int32_t ySrcOffsetIncrement;
- int32_t uSrcOffsetIncrement;
- int32_t vSrcOffsetIncrement;
- srcImage.getOffsetIncrementsPerDataRow(
- &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
-
- int32_t yDestOffsetIncrement;
- int32_t uDestOffsetIncrement;
- int32_t vDestOffsetIncrement = 0;
- destImage.getOffsetIncrementsPerDataRow(
- &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
-
- // Copy Y
- {
- size_t numberOfYBytesPerRow = (size_t) width;
- uint8_t *ySrcAddr = ySrcAddrBase;
- uint8_t *yDestAddr = yDestAddrBase;
- for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
- memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
-
- ySrcAddr = ySrcAddr + ySrcOffsetIncrement;
- yDestAddr = yDestAddr + yDestOffsetIncrement;
- }
- }
-
- // Copy UV
- {
- // UV are interleaved. So number of UV bytes per row is 2*(width/2).
- size_t numberOfUVBytesPerRow = (size_t) width;
- uint8_t *vSrcAddr = vSrcAddrBase;
- uint8_t *vDestAddr = vDestAddrBase;
- // Every other pixel row has a U/V data row. Hence only go half the height.
- for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
- memcpy(vDestAddr, vSrcAddr, numberOfUVBytesPerRow);
-
- vSrcAddr += vSrcOffsetIncrement;
- vDestAddr += vDestOffsetIncrement;
- }
- }
-}
-
-// static
-bool YUVImage::fastCopyRectangle(
- const Rect& srcRect,
- int32_t destStartX, int32_t destStartY,
- const YUVImage &srcImage, YUVImage &destImage) {
- if (srcImage.mYUVFormat == destImage.mYUVFormat) {
- if (srcImage.mYUVFormat == YUV420Planar) {
- fastCopyRectangle420Planar(
- srcRect,
- destStartX, destStartY,
- srcImage, destImage);
- } else if (srcImage.mYUVFormat == YUV420SemiPlanar) {
- fastCopyRectangle420SemiPlanar(
- srcRect,
- destStartX, destStartY,
- srcImage, destImage);
- }
- return true;
- }
- return false;
-}
-
-uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) {
- CHECK(maxValue >= minValue);
-
- if (v < minValue) return minValue;
- else if (v > maxValue) return maxValue;
- else return v;
-}
-
-void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
- uint8_t *r, uint8_t *g, uint8_t *b) const {
- int rTmp = yValue + (1.370705 * (vValue-128));
- int gTmp = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
- int bTmp = yValue + (1.732446 * (uValue-128));
-
- *r = clamp(rTmp, 0, 255);
- *g = clamp(gTmp, 0, 255);
- *b = clamp(bTmp, 0, 255);
-}
-
-bool YUVImage::writeToPPM(const char *filename) const {
- FILE *fp = fopen(filename, "w");
- if (fp == NULL) {
- return false;
- }
- fprintf(fp, "P3\n");
- fprintf(fp, "%d %d\n", mWidth, mHeight);
- fprintf(fp, "255\n");
- for (int32_t y = 0; y < mHeight; ++y) {
- for (int32_t x = 0; x < mWidth; ++x) {
- uint8_t yValue = 0u;
- uint8_t uValue = 0u;
- uint8_t vValue = 0u;
- getPixelValue(x, y, &yValue, &uValue, & vValue);
-
- uint8_t rValue;
- uint8_t gValue;
- uint8_t bValue;
- yuv2rgb(yValue, uValue, vValue, &rValue, &gValue, &bValue);
-
- fprintf(fp, "%d %d %d\n", (int32_t)rValue, (int32_t)gValue, (int32_t)bValue);
- }
- }
- fclose(fp);
- return true;
-}
-
-} // namespace android
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index f3f3b1a..ffbfcbb 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -15,13 +15,9 @@
main_mediaserver.cpp
LOCAL_SHARED_LIBRARIES := \
- libcamera_metadata \
- libcamera_client \
- libcameraservice \
libresourcemanagerservice \
liblog \
libcutils \
- libmedia \
libmediaplayerservice \
libutils \
libbinder \
@@ -33,7 +29,6 @@
LOCAL_C_INCLUDES := \
frameworks/av/media/libmediaplayerservice \
- frameworks/av/services/camera/libcameraservice \
frameworks/av/services/mediaresourcemanager \
LOCAL_MODULE:= mediaserver
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 0e9bc34..0356753 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -519,7 +519,7 @@
// Wait for result of readDataAsync
int MtpDataPacket::readDataWait(struct usb_device *device) {
- struct usb_request *req = usb_request_wait(device);
+ struct usb_request *req = usb_request_wait(device, -1);
return (req ? req->actual_length : -1);
}
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index e3d2ec6..82a2627 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -39,6 +39,12 @@
namespace android {
+namespace {
+
+static constexpr int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
+} // namespace
+
#if 0
static bool isMtpDevice(uint16_t vendor, uint16_t product) {
// Sandisk Sansa Fuze
@@ -84,15 +90,18 @@
interface->bInterfaceSubClass == 1 && // Still Image Capture
interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
{
- char* manufacturerName = usb_device_get_manufacturer_name(device);
- char* productName = usb_device_get_product_name(device);
+ char* manufacturerName = usb_device_get_manufacturer_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char* productName = usb_device_get_product_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
ALOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
free(manufacturerName);
free(productName);
} else if (interface->bInterfaceClass == 0xFF &&
interface->bInterfaceSubClass == 0xFF &&
interface->bInterfaceProtocol == 0) {
- char* interfaceName = usb_device_get_string(device, interface->iInterface);
+ char* interfaceName = usb_device_get_string(device, interface->iInterface,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
if (!interfaceName) {
continue;
} else if (strcmp(interfaceName, "MTP")) {
@@ -102,8 +111,10 @@
free(interfaceName);
// Looks like an android style MTP device
- char* manufacturerName = usb_device_get_manufacturer_name(device);
- char* productName = usb_device_get_product_name(device);
+ char* manufacturerName = usb_device_get_manufacturer_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char* productName = usb_device_get_product_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
ALOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
free(manufacturerName);
free(productName);
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
index 8e13ea9..d9ef311 100644
--- a/media/mtp/MtpEventPacket.cpp
+++ b/media/mtp/MtpEventPacket.cpp
@@ -66,7 +66,7 @@
}
int MtpEventPacket::readResponse(struct usb_device *device) {
- struct usb_request* const req = usb_request_wait(device);
+ struct usb_request* const req = usb_request_wait(device, -1);
if (req) {
mPacketSize = req->actual_length;
return req->actual_length;
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index 74729e4..a4f999f 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -45,7 +45,6 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
libmedia \
- libmediadrm \
libstagefright \
libstagefright_foundation \
liblog \
diff --git a/media/utils/BatteryNotifier.cpp b/media/utils/BatteryNotifier.cpp
index 341d391..7a7321f 100644
--- a/media/utils/BatteryNotifier.cpp
+++ b/media/utils/BatteryNotifier.cpp
@@ -29,7 +29,7 @@
BatteryNotifier::getInstance().onBatteryStatServiceDied();
}
-BatteryNotifier::BatteryNotifier() : mVideoRefCount(0), mAudioRefCount(0) {}
+BatteryNotifier::BatteryNotifier() {}
BatteryNotifier::~BatteryNotifier() {
Mutex::Autolock _l(mLock);
@@ -38,67 +38,73 @@
}
}
-void BatteryNotifier::noteStartVideo() {
+void BatteryNotifier::noteStartVideo(int uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- if (mVideoRefCount == 0 && batteryService != nullptr) {
- batteryService->noteStartVideo(AID_MEDIA);
+ if (mVideoRefCounts[uid] == 0 && batteryService != nullptr) {
+ batteryService->noteStartVideo(uid);
}
- mVideoRefCount++;
+ mVideoRefCounts[uid]++;
}
-void BatteryNotifier::noteStopVideo() {
+void BatteryNotifier::noteStopVideo(int uid) {
Mutex::Autolock _l(mLock);
- if (mVideoRefCount == 0) {
- ALOGW("%s: video refcount is broken.", __FUNCTION__);
+ if (mVideoRefCounts.find(uid) == mVideoRefCounts.end()) {
+ ALOGW("%s: video refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
return;
}
sp<IBatteryStats> batteryService = getBatteryService_l();
- mVideoRefCount--;
- if (mVideoRefCount == 0 && batteryService != nullptr) {
- batteryService->noteStopVideo(AID_MEDIA);
+ mVideoRefCounts[uid]--;
+ if (mVideoRefCounts[uid] == 0) {
+ if (batteryService != nullptr) {
+ batteryService->noteStopVideo(uid);
+ }
+ mVideoRefCounts.erase(uid);
}
}
void BatteryNotifier::noteResetVideo() {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- mVideoRefCount = 0;
+ mVideoRefCounts.clear();
if (batteryService != nullptr) {
batteryService->noteResetVideo();
}
}
-void BatteryNotifier::noteStartAudio() {
+void BatteryNotifier::noteStartAudio(int uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- if (mAudioRefCount == 0 && batteryService != nullptr) {
- batteryService->noteStartAudio(AID_AUDIOSERVER);
+ if (mAudioRefCounts[uid] == 0 && batteryService != nullptr) {
+ batteryService->noteStartAudio(uid);
}
- mAudioRefCount++;
+ mAudioRefCounts[uid]++;
}
-void BatteryNotifier::noteStopAudio() {
+void BatteryNotifier::noteStopAudio(int uid) {
Mutex::Autolock _l(mLock);
- if (mAudioRefCount == 0) {
- ALOGW("%s: audio refcount is broken.", __FUNCTION__);
+ if (mAudioRefCounts.find(uid) == mAudioRefCounts.end()) {
+ ALOGW("%s: audio refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
return;
}
sp<IBatteryStats> batteryService = getBatteryService_l();
- mAudioRefCount--;
- if (mAudioRefCount == 0 && batteryService != nullptr) {
- batteryService->noteStopAudio(AID_AUDIOSERVER);
+ mAudioRefCounts[uid]--;
+ if (mAudioRefCounts[uid] == 0) {
+ if (batteryService != nullptr) {
+ batteryService->noteStopAudio(uid);
+ }
+ mAudioRefCounts.erase(uid);
}
}
void BatteryNotifier::noteResetAudio() {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- mAudioRefCount = 0;
+ mAudioRefCounts.clear();
if (batteryService != nullptr) {
batteryService->noteResetAudio();
}
@@ -176,7 +182,7 @@
Mutex::Autolock _l(mLock);
mBatteryStatService.clear();
mDeathNotifier.clear();
- // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+ // Do not reset mVideoRefCounts and mAudioRefCounts here. The ref
// counting is independent of the battery service availability.
// We need this if battery service becomes available after media
// started.
@@ -205,11 +211,13 @@
// Notify start now if mediaserver or audioserver is already started.
// 1) mediaserver and audioserver is started before batterystats service
// 2) batterystats server may have crashed.
- if (mVideoRefCount > 0) {
- mBatteryStatService->noteStartVideo(AID_MEDIA);
+ std::map<int, int>::iterator it = mVideoRefCounts.begin();
+ for (; it != mVideoRefCounts.end(); ++it) {
+ mBatteryStatService->noteStartVideo(it->first);
}
- if (mAudioRefCount > 0) {
- mBatteryStatService->noteStartAudio(AID_AUDIOSERVER);
+ it = mAudioRefCounts.begin();
+ for (; it != mAudioRefCounts.end(); ++it) {
+ mBatteryStatService->noteStartAudio(it->first);
}
// TODO: Notify for camera and flashlight state as well?
}
diff --git a/media/utils/include/mediautils/BatteryNotifier.h b/media/utils/include/mediautils/BatteryNotifier.h
index 49048042..2ba4c76 100644
--- a/media/utils/include/mediautils/BatteryNotifier.h
+++ b/media/utils/include/mediautils/BatteryNotifier.h
@@ -37,11 +37,11 @@
public:
~BatteryNotifier();
- void noteStartVideo();
- void noteStopVideo();
+ void noteStartVideo(int uid);
+ void noteStopVideo(int uid);
void noteResetVideo();
- void noteStartAudio();
- void noteStopAudio();
+ void noteStartAudio(int uid);
+ void noteStopAudio(int uid);
void noteResetAudio();
void noteFlashlightOn(const String8& id, int uid);
void noteFlashlightOff(const String8& id, int uid);
@@ -58,8 +58,8 @@
};
Mutex mLock;
- int mVideoRefCount;
- int mAudioRefCount;
+ std::map<int, int> mVideoRefCounts;
+ std::map<int, int> mAudioRefCounts;
std::map<std::pair<String8, int>, bool> mFlashlightState;
std::map<std::pair<String8, int>, bool> mCameraState;
sp<IBatteryStats> mBatteryStatService;
diff --git a/radio/Android.mk b/radio/Android.mk
index 0377328..be5d283 100644
--- a/radio/Android.mk
+++ b/radio/Android.mk
@@ -27,7 +27,6 @@
libutils \
liblog \
libbinder \
- libhardware \
libradio_metadata
#LOCAL_C_INCLUDES += \
diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp
index 0881a91..ebf3859 100644
--- a/radio/IRadio.cpp
+++ b/radio/IRadio.cpp
@@ -112,7 +112,7 @@
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
if (status == NO_ERROR) {
- int muteread = reply.readInt32();
+ int32_t muteread = reply.readInt32();
*mute = muteread != 0;
}
}
@@ -145,12 +145,12 @@
return status;
}
- virtual status_t tune(unsigned int channel, unsigned int subChannel)
+ virtual status_t tune(uint32_t channel, uint32_t subChannel)
{
Parcel data, reply;
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
- data.writeInt32(channel);
- data.writeInt32(subChannel);
+ data.writeUint32(channel);
+ data.writeUint32(subChannel);
status_t status = remote()->transact(TUNE, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
@@ -177,27 +177,29 @@
}
radio_metadata_t *metadata = info->metadata;
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ if (metadata != NULL) {
+ data.writeUint32(1);
+ } else {
+ data.writeUint32(0);
+ }
status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
if (status == NO_ERROR) {
reply.read(info, sizeof(struct radio_program_info));
+ // restore local metadata pointer
info->metadata = metadata;
- if (metadata == NULL) {
- return status;
+
+ uint32_t metatataSize = reply.readUint32();
+ if ((metadata != NULL) && (metatataSize != 0)) {
+ radio_metadata_t *newMetadata = (radio_metadata_t *)malloc(metatataSize);
+ if (newMetadata == NULL) {
+ return NO_MEMORY;
+ }
+ reply.read(newMetadata, metatataSize);
+ status = radio_metadata_add_metadata(&info->metadata, newMetadata);
+ free(newMetadata);
}
- size_t size = (size_t)reply.readInt32();
- if (size == 0) {
- return status;
- }
- metadata =
- (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int));
- if (metadata == NULL) {
- return NO_MEMORY;
- }
- reply.read(metadata, size);
- status = radio_metadata_add_metadata(&info->metadata, metadata);
- free(metadata);
}
}
return status;
@@ -288,8 +290,8 @@
}
case TUNE: {
CHECK_INTERFACE(IRadio, data, reply);
- unsigned int channel = (unsigned int)data.readInt32();
- unsigned int subChannel = (unsigned int)data.readInt32();
+ uint32_t channel = data.readUint32();
+ uint32_t subChannel = data.readUint32();
status_t status = tune(channel, subChannel);
reply->writeInt32(status);
return NO_ERROR;
@@ -303,22 +305,27 @@
case GET_PROGRAM_INFORMATION: {
CHECK_INTERFACE(IRadio, data, reply);
struct radio_program_info info;
-
- status_t status = radio_metadata_allocate(&info.metadata, 0, 0);
- if (status != NO_ERROR) {
- return status;
+ status_t status;
+ // query metadata only if requested by remote side
+ if (data.readUint32() == 1) {
+ status = radio_metadata_allocate(&info.metadata, 0, 0);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ } else {
+ info.metadata = NULL;
}
status = getProgramInformation(&info);
+
reply->writeInt32(status);
if (status == NO_ERROR) {
reply->write(&info, sizeof(struct radio_program_info));
- int count = radio_metadata_get_count(info.metadata);
- if (count > 0) {
+ if ((info.metadata != NULL) && (radio_metadata_get_count(info.metadata) > 0)) {
size_t size = radio_metadata_get_size(info.metadata);
- reply->writeInt32(size);
+ reply->writeUint32((uint32_t)size);
reply->write(info.metadata, size);
} else {
- reply->writeInt32(0);
+ reply->writeUint32(0);
}
}
radio_metadata_deallocate(info.metadata);
diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp
index be7d21e..72e3a61 100644
--- a/radio/IRadioService.cpp
+++ b/radio/IRadioService.cpp
@@ -16,8 +16,7 @@
*/
#define LOG_TAG "BpRadioService"
-//
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -58,12 +57,12 @@
}
Parcel data, reply;
data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
- unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules;
+ uint32_t numModulesReq = (properties == NULL) ? 0 : *numModules;
data.writeInt32(numModulesReq);
status_t status = remote()->transact(LIST_MODULES, data, &reply);
if (status == NO_ERROR) {
status = (status_t)reply.readInt32();
- *numModules = (unsigned int)reply.readInt32();
+ *numModules = (uint32_t)reply.readInt32();
}
ALOGV("listModules() status %d got *numModules %d", status, *numModules);
if (status == NO_ERROR) {
@@ -120,11 +119,11 @@
switch(code) {
case LIST_MODULES: {
CHECK_INTERFACE(IRadioService, data, reply);
- unsigned int numModulesReq = data.readInt32();
+ uint32_t numModulesReq = data.readInt32();
if (numModulesReq > MAX_ITEMS_PER_LIST) {
numModulesReq = MAX_ITEMS_PER_LIST;
}
- unsigned int numModules = numModulesReq;
+ uint32_t numModules = numModulesReq;
struct radio_properties *properties =
(struct radio_properties *)calloc(numModulesReq,
sizeof(struct radio_properties));
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
index 3c04fb0..fa39589 100644
--- a/radio/Radio.cpp
+++ b/radio/Radio.cpp
@@ -240,20 +240,31 @@
return;
}
+ // The event layout in shared memory is:
+ // sizeof(struct radio_event) bytes : the event itself
+ // 4 bytes : metadata size or 0
+ // N bytes : metadata if present
struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+ uint32_t metadataOffset = sizeof(struct radio_event) + sizeof(uint32_t);
+ uint32_t metadataSize = *(uint32_t *)((uint8_t *)event + metadataOffset - sizeof(uint32_t));
+
// restore local metadata pointer from offset
switch (event->type) {
case RADIO_EVENT_TUNED:
case RADIO_EVENT_AF_SWITCH:
- if (event->info.metadata != NULL) {
+ if (metadataSize != 0) {
event->info.metadata =
- (radio_metadata_t *)((char *)event + (size_t)event->info.metadata);
+ (radio_metadata_t *)((uint8_t *)event + metadataOffset);
+ } else {
+ event->info.metadata = 0;
}
break;
case RADIO_EVENT_METADATA:
- if (event->metadata != NULL) {
+ if (metadataSize != 0) {
event->metadata =
- (radio_metadata_t *)((char *)event + (size_t)event->metadata);
+ (radio_metadata_t *)((uint8_t *)event + metadataOffset);
+ } else {
+ event->metadata = 0;
}
break;
default:
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index f35373f..1657c08 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -31,7 +31,8 @@
AudioMixer.cpp.arm \
BufferProviders.cpp \
PatchPanel.cpp \
- StateQueue.cpp
+ StateQueue.cpp \
+ BufLog.cpp
LOCAL_C_INCLUDES := \
$(TOPDIR)frameworks/av/services/audiopolicy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 04fb8ba..7e10e48 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -67,6 +67,9 @@
#include <mediautils/BatteryNotifier.h>
#include <private/android_filesystem_config.h>
+//#define BUFLOG_NDEBUG 0
+#include <BufLog.h>
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -441,6 +444,8 @@
}
#endif
+ BUFLOG_RESET;
+
if (locked) {
mLock.unlock();
}
diff --git a/services/audioflinger/BufLog.cpp b/services/audioflinger/BufLog.cpp
new file mode 100644
index 0000000..9680eb5
--- /dev/null
+++ b/services/audioflinger/BufLog.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+
+#include "BufLog.h"
+#define LOG_TAG "BufLog"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include "log/log.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+// ------------------------------
+// BufLogSingleton
+// ------------------------------
+pthread_once_t onceControl = PTHREAD_ONCE_INIT;
+
+BufLog *BufLogSingleton::mInstance = NULL;
+
+void BufLogSingleton::initOnce() {
+ mInstance = new BufLog();
+ ALOGW("=====================================\n" \
+ "Warning: BUFLOG is defined in some part of your code.\n" \
+ "This will create large audio dumps in %s.\n" \
+ "=====================================\n", BUFLOG_BASE_PATH);
+}
+
+BufLog *BufLogSingleton::instance() {
+ pthread_once(&onceControl, initOnce);
+ return mInstance;
+}
+
+bool BufLogSingleton::instanceExists() {
+ return mInstance != NULL;
+}
+
+// ------------------------------
+// BufLog
+// ------------------------------
+
+BufLog::BufLog() {
+ memset(mStreams, 0, sizeof(mStreams));
+}
+
+BufLog::~BufLog() {
+ android::Mutex::Autolock autoLock(mLock);
+
+ for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+ BufLogStream *pBLStream = mStreams[id];
+ if (pBLStream != NULL) {
+ delete pBLStream ;
+ mStreams[id] = NULL;
+ }
+ }
+}
+
+size_t BufLog::write(int streamid, const char *tag, int format, int channels,
+ int samplingRate, size_t maxBytes, const void *buf, size_t size) {
+ unsigned int id = streamid % BUFLOG_MAXSTREAMS;
+ android::Mutex::Autolock autoLock(mLock);
+
+ BufLogStream *pBLStream = mStreams[id];
+
+ if (pBLStream == NULL) {
+ pBLStream = mStreams[id] = new BufLogStream(id, tag, format, channels,
+ samplingRate, maxBytes);
+ ALOG_ASSERT(pBLStream != NULL, "BufLogStream Failed to be created");
+ }
+
+ return pBLStream->write(buf, size);
+}
+
+void BufLog::reset() {
+ android::Mutex::Autolock autoLock(mLock);
+ ALOGV("Resetting all BufLogs");
+ int count = 0;
+
+ for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+ BufLogStream *pBLStream = mStreams[id];
+ if (pBLStream != NULL) {
+ delete pBLStream;
+ mStreams[id] = NULL;
+ count++;
+ }
+ }
+ ALOGV("Reset %d BufLogs", count);
+}
+
+// ------------------------------
+// BufLogStream
+// ------------------------------
+
+BufLogStream::BufLogStream(unsigned int id,
+ const char *tag,
+ unsigned int format,
+ unsigned int channels,
+ unsigned int samplingRate,
+ size_t maxBytes = 0) : mId(id), mFormat(format), mChannels(channels),
+ mSamplingRate(samplingRate), mMaxBytes(maxBytes) {
+ mByteCount = 0l;
+ mPaused = false;
+ if (tag != NULL) {
+ strncpy(mTag, tag, BUFLOGSTREAM_MAX_TAGSIZE);
+ } else {
+ mTag[0] = 0;
+ }
+ ALOGV("Creating BufLogStream id:%d tag:%s format:%d ch:%d sr:%d maxbytes:%zu", mId, mTag,
+ mFormat, mChannels, mSamplingRate, mMaxBytes);
+
+ //open file (s), info about tag, format, etc.
+ //timestamp
+ char timeStr[16]; //size 16: format %Y%m%d%H%M%S 14 chars + string null terminator
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ struct tm tm;
+ localtime_r(&tv.tv_sec, &tm);
+ strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tm);
+ char logPath[BUFLOG_MAX_PATH_SIZE];
+ snprintf(logPath, BUFLOG_MAX_PATH_SIZE, "%s/%s_%d_%s_%d_%d_%d.raw", BUFLOG_BASE_PATH, timeStr,
+ mId, mTag, mFormat, mChannels, mSamplingRate);
+ ALOGV("data output: %s", logPath);
+
+ mFile = fopen(logPath, "wb");
+ if (mFile != NULL) {
+ ALOGV("Success creating file at: %p", mFile);
+ } else {
+ ALOGE("Error: could not create file BufLogStream %s", strerror(errno));
+ }
+}
+
+void BufLogStream::closeStream_l() {
+ ALOGV("Closing BufLogStream id:%d tag:%s", mId, mTag);
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+BufLogStream::~BufLogStream() {
+ ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag);
+ android::Mutex::Autolock autoLock(mLock);
+ closeStream_l();
+}
+
+size_t BufLogStream::write(const void *buf, size_t size) {
+
+ size_t bytes = 0;
+ if (!mPaused && mFile != NULL) {
+ if (size > 0 && buf != NULL) {
+ android::Mutex::Autolock autoLock(mLock);
+ if (mMaxBytes > 0) {
+ size = MIN(size, mMaxBytes - mByteCount);
+ }
+ bytes = fwrite(buf, 1, size, mFile);
+ mByteCount += bytes;
+ if (mMaxBytes > 0 && mMaxBytes == mByteCount) {
+ closeStream_l();
+ }
+ }
+ ALOGV("wrote %zu/%zu bytes to BufLogStream %d tag:%s. Total Bytes: %zu", bytes, size, mId,
+ mTag, mByteCount);
+ } else {
+ ALOGV("Warning: trying to write to %s BufLogStream id:%d tag:%s",
+ mPaused ? "paused" : "closed", mId, mTag);
+ }
+ return bytes;
+}
+
+bool BufLogStream::setPause(bool pause) {
+ bool old = mPaused;
+ mPaused = pause;
+ return old;
+}
+
+void BufLogStream::finalize() {
+ android::Mutex::Autolock autoLock(mLock);
+ closeStream_l();
+}
diff --git a/services/audioflinger/BufLog.h b/services/audioflinger/BufLog.h
new file mode 100644
index 0000000..1b402f4
--- /dev/null
+++ b/services/audioflinger/BufLog.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_AUDIO_BUFLOG_H
+#define ANDROID_AUDIO_BUFLOG_H
+
+/*
+ * BUFLOG creates up to BUFLOG_MAXSTREAMS simultaneous streams [0:15] of audio buffer data
+ * and saves them to disk. The files are stored in the path specified in BUFLOG_BASE_PATH and
+ * are named following this format:
+ * YYYYMMDDHHMMSS_id_format_channels_samplingrate.raw
+ *
+ * Normally we strip BUFLOG dumps from release builds.
+ * You can modify this (for example with "#define BUFLOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ *
+ * usage:
+ * - Add this to the top of the source file you want to debug:
+ * #define BUFLOG_NDEBUG 0
+ * #include "BufLog.h"
+ *
+ * - dump an audio buffer
+ * BUFLOG(buff_id, buff_tag, format, channels, sampling_rate, max_bytes, buff_pointer, buff_size);
+ *
+ * buff_id: int [0:15] buffer id. If a buffer doesn't exist, it is created the first time.
+ * buff_tag: char* string tag used on stream filename and logs
+ * format: int Audio format (audio_format_t see audio.h)
+ * channels: int Channel Count
+ * sampling_rate: int Sampling rate in Hz. e.g. 8000, 16000, 44100, 48000, etc
+ * max_bytes: int [0 or positive number]
+ * Maximum size of the file (in bytes) to be output.
+ * If the value is 0, no limit.
+ * buff_pointer: void * Pointer to audio buffer.
+ * buff_size: int Size (in bytes) of the current audio buffer to be stored.
+ *
+ *
+ * Example usage:
+ * int format = mConfig.outputCfg.format;
+ * int channels = audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
+ * int samplingRate = mConfig.outputCfg.samplingRate;
+ * int frameCount = mConfig.outputCfg.buffer.frameCount;
+ * int frameSize = audio_bytes_per_sample((audio_format_t)format) * channels;
+ * int buffSize = frameCount * frameSize;
+ * long maxBytes = 10 * samplingRate * frameSize; //10 seconds max
+ * BUFLOG(11, "loudnes_enhancer_out", format, channels, samplingRate, maxBytes,
+ * mConfig.outputCfg.buffer.raw, buffSize);
+ *
+ * Other macros:
+ * BUFLOG_EXISTS returns true if there is an instance of BufLog
+ *
+ * BUFLOG_RESET If an instance of BufLog exists, it stops the capture and closes all
+ * streams.
+ * If a new call to BUFLOG(..) is done, new streams are created.
+ */
+
+#ifndef BUFLOG_NDEBUG
+#ifdef NDEBUG
+#define BUFLOG_NDEBUG 1
+#else
+#define BUFLOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * Simplified macro to send a buffer.
+ */
+#ifndef BUFLOG
+#define __BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+ BufLogSingleton::instance()->write(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, \
+ BUF, SIZE)
+#if BUFLOG_NDEBUG
+#define BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+ do { if (0) { } } while (0)
+#else
+#define BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+ __BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE)
+#endif
+#endif
+
+#ifndef BUFLOG_EXISTS
+#define BUFLOG_EXISTS BufLogSingleton::instanceExists()
+#endif
+
+#ifndef BUFLOG_RESET
+#define BUFLOG_RESET do { if (BufLogSingleton::instanceExists()) { \
+ BufLogSingleton::instance()->reset(); } } while (0)
+#endif
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <utils/Mutex.h>
+
+//BufLog configuration
+#define BUFLOGSTREAM_MAX_TAGSIZE 32
+#define BUFLOG_BASE_PATH "/data/misc/audioserver"
+#define BUFLOG_MAX_PATH_SIZE 300
+
+class BufLogStream {
+public:
+ BufLogStream(unsigned int id,
+ const char *tag,
+ unsigned int format,
+ unsigned int channels,
+ unsigned int samplingRate,
+ size_t maxBytes);
+ ~BufLogStream();
+
+ // write buffer to stream
+ // buf: pointer to buffer
+ // size: number of bytes to write
+ size_t write(const void *buf, size_t size);
+
+ // pause/resume stream
+ // pause: true = paused, false = not paused
+ // return value: previous state of stream (paused or not).
+ bool setPause(bool pause);
+
+ // will stop the stream and close any open file
+ // the stream can't be reopen. Instead, a new stream (and file) should be created.
+ void finalize();
+
+private:
+ bool mPaused;
+ const unsigned int mId;
+ char mTag[BUFLOGSTREAM_MAX_TAGSIZE + 1];
+ const unsigned int mFormat;
+ const unsigned int mChannels;
+ const unsigned int mSamplingRate;
+ const size_t mMaxBytes;
+ size_t mByteCount;
+ FILE *mFile;
+ mutable android::Mutex mLock;
+
+ void closeStream_l();
+};
+
+
+class BufLog {
+public:
+ BufLog();
+ ~BufLog();
+ BufLog(BufLog const&) {};
+
+ // streamid: int [0:BUFLOG_MAXSTREAMS-1] buffer id.
+ // If a buffer doesn't exist, it is created the first time is referenced
+ // tag: char* string tag used on stream filename and logs
+ // format: int Audio format (audio_format_t see audio.h)
+ // channels: int Channel Count
+ // samplingRate: int Sampling rate in Hz. e.g. 8000, 16000, 44100, 48000, etc
+ // maxBytes: int [0 or positive number]
+ // Maximum size of the file (in bytes) to be output.
+ // If the value is 0, no limit.
+ // size: int Size (in bytes) of the current audio buffer to be written.
+ // buf: void * Pointer to audio buffer.
+ size_t write(int streamid,
+ const char *tag,
+ int format,
+ int channels,
+ int samplingRate,
+ size_t maxBytes,
+ const void *buf,
+ size_t size);
+
+ // reset will stop and close all active streams, thus finalizing any open file.
+ // New streams will be created if write() is called again.
+ void reset();
+
+protected:
+ static const unsigned int BUFLOG_MAXSTREAMS = 16;
+ BufLogStream *mStreams[BUFLOG_MAXSTREAMS];
+ mutable android::Mutex mLock;
+};
+
+class BufLogSingleton {
+public:
+ static BufLog *instance();
+ static bool instanceExists();
+
+private:
+ static void initOnce();
+ static BufLog *mInstance;
+};
+
+#endif //ANDROID_AUDIO_BUFLOG_H
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 6b23e56..4ae5a02 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -33,6 +33,7 @@
#include <utils/Trace.h>
#include <private/media/AudioTrackShared.h>
+#include <private/android_filesystem_config.h>
#include <audio_utils/conversion.h>
#include <audio_utils/primitives.h>
#include <audio_utils/format.h>
@@ -1040,7 +1041,8 @@
}
if (!mNotifiedBatteryStart) {
- BatteryNotifier::getInstance().noteStartAudio();
+ // TODO: call this function for each track when it becomes active.
+ BatteryNotifier::getInstance().noteStartAudio(AID_AUDIOSERVER);
mNotifiedBatteryStart = true;
}
gBoottime.acquire(mWakeLockToken);
@@ -1067,7 +1069,8 @@
}
if (mNotifiedBatteryStart) {
- BatteryNotifier::getInstance().noteStopAudio();
+ // TODO: call this function for each track when it becomes inactive.
+ BatteryNotifier::getInstance().noteStopAudio(AID_AUDIOSERVER);
mNotifiedBatteryStart = false;
}
}
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index d598e7e..91cc3d2 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -31,7 +31,6 @@
liblog \
libbinder \
libmedia \
- libhardware \
libhardware_legacy \
libserviceutility
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 4b32691..c60b49a 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -234,9 +234,9 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle,
+ audio_patch_handle_t *handle,
uid_t uid) = 0;
- virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
+ virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0;
virtual status_t setMasterMono(bool mono) = 0;
virtual status_t getMasterMono(bool *mono) = 0;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
index 4ab7cf0..7e1e24d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
@@ -50,7 +50,7 @@
};
class AudioSourceCollection :
- public DefaultKeyedVector< audio_io_handle_t, sp<AudioSourceDescriptor> >
+ public DefaultKeyedVector< audio_patch_handle_t, sp<AudioSourceDescriptor> >
{
public:
status_t dump(int fd) const;
diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index b247e21..e6de8ae 100644
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -43,6 +43,5 @@
liblog \
libcutils \
libutils \
- libaudioutils \
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7c955e5..2f01b02 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3026,7 +3026,7 @@
status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle,
+ audio_patch_handle_t *handle,
uid_t uid)
{
ALOGV("%s source %p attributes %p handle %p", __FUNCTION__, source, attributes, handle);
@@ -3034,7 +3034,7 @@
return BAD_VALUE;
}
- *handle = AUDIO_IO_HANDLE_NONE;
+ *handle = AUDIO_PATCH_HANDLE_NONE;
if (source->role != AUDIO_PORT_ROLE_SOURCE ||
source->type != AUDIO_PORT_TYPE_DEVICE) {
@@ -3141,7 +3141,7 @@
return NO_ERROR;
}
-status_t AudioPolicyManager::stopAudioSource(audio_io_handle_t handle __unused)
+status_t AudioPolicyManager::stopAudioSource(audio_patch_handle_t handle __unused)
{
sp<AudioSourceDescriptor> sourceDesc = mAudioSources.valueFor(handle);
ALOGV("%s handle %d", __FUNCTION__, handle);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 46f0625..52fa082 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -228,9 +228,9 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle,
+ audio_patch_handle_t *handle,
uid_t uid);
- virtual status_t stopAudioSource(audio_io_handle_t handle);
+ virtual status_t stopAudioSource(audio_patch_handle_t handle);
virtual status_t setMasterMono(bool mono);
virtual status_t getMasterMono(bool *mono);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index f6da920..53c54bb 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -712,7 +712,7 @@
status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle)
+ audio_patch_handle_t *handle)
{
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
@@ -723,7 +723,7 @@
IPCThreadState::self()->getCallingUid());
}
-status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle)
{
Mutex::Autolock _l(mLock);
if (mAudioPolicyManager == NULL) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 517fba1..a5b96fe 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -610,12 +610,12 @@
status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle)
+ audio_patch_handle_t *handle)
{
return INVALID_OPERATION;
}
-status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle)
{
return INVALID_OPERATION;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index f34473c..a310735 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -200,8 +200,8 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
- audio_io_handle_t *handle);
- virtual status_t stopAudioSource(audio_io_handle_t handle);
+ audio_patch_handle_t *handle);
+ virtual status_t stopAudioSource(audio_patch_handle_t handle);
virtual status_t setMasterMono(bool mono);
virtual status_t getMasterMono(bool *mono);
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 89a1bb6..7feed6b 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -65,12 +65,11 @@
libcamera_client \
libgui \
libhardware \
- libsync \
libcamera_metadata \
libjpeg \
libmemunreachable
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libcamera_client
LOCAL_C_INCLUDES += \
system/media/private/camera/include \
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 9d5f33c..9a7839b 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -238,6 +238,10 @@
{
String8 supportedPreviewFpsRange;
for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+ if (!isFpsSupported(availablePreviewSizes,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+ continue;
+ }
if (i != 0) supportedPreviewFpsRange += ",";
supportedPreviewFpsRange += String8::format("(%d,%d)",
availableFpsRanges.data.i32[i] * kFpsToApiScale,
@@ -255,7 +259,10 @@
// from the [min, max] fps range use the max value
int fps = fpsFromRange(availableFpsRanges.data.i32[i],
availableFpsRanges.data.i32[i+1]);
-
+ if (!isFpsSupported(availablePreviewSizes,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, fps)) {
+ continue;
+ }
// de-dupe frame rates
if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) {
sortedPreviewFrameRates.add(fps);
@@ -951,21 +958,40 @@
return NO_INIT;
}
+ // Get supported preview fps ranges.
+ Vector<Size> supportedPreviewSizes;
+ Vector<FpsRange> supportedPreviewFpsRanges;
+ const Size PREVIEW_SIZE_BOUND = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+ status_t res = getFilteredSizes(PREVIEW_SIZE_BOUND, &supportedPreviewSizes);
+ if (res != OK) return res;
+ for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+ if (!isFpsSupported(supportedPreviewSizes,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+ continue;
+ }
+ FpsRange fpsRange = {availableFpsRanges.data.i32[i], availableFpsRanges.data.i32[i+1]};
+ supportedPreviewFpsRanges.add(fpsRange);
+ }
+ if (supportedPreviewFpsRanges.size() == 0) {
+ ALOGE("Supported preview fps range is empty");
+ return NO_INIT;
+ }
+
int32_t bestStillCaptureFpsRange[2] = {
- availableFpsRanges.data.i32[0], availableFpsRanges.data.i32[1]
+ supportedPreviewFpsRanges[0].low, supportedPreviewFpsRanges[0].high
};
int32_t curRange =
bestStillCaptureFpsRange[1] - bestStillCaptureFpsRange[0];
- for (size_t i = 2; i < availableFpsRanges.count; i += 2) {
+ for (size_t i = 1; i < supportedPreviewFpsRanges.size(); i ++) {
int32_t nextRange =
- availableFpsRanges.data.i32[i + 1] -
- availableFpsRanges.data.i32[i];
+ supportedPreviewFpsRanges[i].high -
+ supportedPreviewFpsRanges[i].low;
if ( (nextRange > curRange) || // Maximize size of FPS range first
(nextRange == curRange && // Then minimize low-end FPS
- bestStillCaptureFpsRange[0] > availableFpsRanges.data.i32[i])) {
+ bestStillCaptureFpsRange[0] > supportedPreviewFpsRanges[i].low)) {
- bestStillCaptureFpsRange[0] = availableFpsRanges.data.i32[i];
- bestStillCaptureFpsRange[1] = availableFpsRanges.data.i32[i + 1];
+ bestStillCaptureFpsRange[0] = supportedPreviewFpsRanges[i].low;
+ bestStillCaptureFpsRange[1] = supportedPreviewFpsRanges[i].high;
curRange = nextRange;
}
}
@@ -2836,22 +2862,7 @@
int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) {
if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- const int STREAM_DURATION_SIZE = 4;
- const int STREAM_FORMAT_OFFSET = 0;
- const int STREAM_WIDTH_OFFSET = 1;
- const int STREAM_HEIGHT_OFFSET = 2;
- const int STREAM_DURATION_OFFSET = 3;
- camera_metadata_ro_entry_t availableStreamMinDurations =
- staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
- for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
- int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
- int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
- int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
- int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
- if (format == HAL_PIXEL_FORMAT_BLOB && width == size.width && height == size.height) {
- return duration;
- }
- }
+ return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
} else {
Vector<Size> availableJpegSizes = getAvailableJpegSizes();
size_t streamIdx = availableJpegSizes.size();
@@ -2875,6 +2886,57 @@
return -1;
}
+int64_t Parameters::getMinFrameDurationNs(Parameters::Size size, int fmt) {
+ if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+ ALOGE("Min frame duration for HAL 3.1 or lower is not supported");
+ return -1;
+ }
+
+ const int STREAM_DURATION_SIZE = 4;
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_DURATION_OFFSET = 3;
+ camera_metadata_ro_entry_t availableStreamMinDurations =
+ staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+ for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
+ int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
+ int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
+ int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
+ int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
+ if (format == fmt && width == size.width && height == size.height) {
+ return duration;
+ }
+ }
+
+ return -1;
+}
+
+bool Parameters::isFpsSupported(const Vector<Size> &sizes, int format, int32_t fps) {
+ // Skip the check for older HAL version, as the min duration is not supported.
+ if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+ return true;
+ }
+
+ // Get min frame duration for each size and check if the given fps range can be supported.
+ const int32_t FPS_MARGIN = 1;
+ for (size_t i = 0 ; i < sizes.size(); i++) {
+ int64_t minFrameDuration = getMinFrameDurationNs(sizes[i], format);
+ if (minFrameDuration <= 0) {
+ ALOGE("Min frame duration (%" PRId64") for size (%dx%d) and format 0x%x is wrong!",
+ minFrameDuration, sizes[i].width, sizes[i].height, format);
+ return false;
+ }
+ int32_t maxSupportedFps = 1e9 / minFrameDuration;
+ // Add some margin here for the case where the hal supports 29.xxxfps.
+ maxSupportedFps += FPS_MARGIN;
+ if (fps > maxSupportedFps) {
+ return false;
+ }
+ }
+ return true;
+}
+
SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
SortedVector<int32_t> outputFormats; // Non-duplicated output formats
if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 798aab5..c8ecbba 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -115,6 +115,11 @@
int32_t height;
};
+ struct FpsRange {
+ int32_t low;
+ int32_t high;
+ };
+
int32_t exposureCompensation;
bool autoExposureLock;
bool autoWhiteBalanceLock;
@@ -390,6 +395,15 @@
// return -1 if input jpeg size cannot be found in supported size list
int64_t getJpegStreamMinFrameDurationNs(Parameters::Size size);
+ // Helper function to get minimum frame duration for a size/format combination
+ // return -1 if input size/format combination cannot be found.
+ int64_t getMinFrameDurationNs(Parameters::Size size, int format);
+
+ // Helper function to check if a given fps is supported by all the sizes with
+ // the same format.
+ // return true if the device doesn't support min frame duration metadata tag.
+ bool isFpsSupported(const Vector<Size> &size, int format, int32_t fps);
+
// Helper function to get non-duplicated available output formats
SortedVector<int32_t> getAvailableOutputFormats();
// Helper function to get available output jpeg sizes
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index b4f8e21..5166eb5 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1210,6 +1210,14 @@
}
}
+void CameraDeviceClient::notifyRequestQueueEmpty() {
+ // Thread safe. Don't bother locking.
+ sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+ if (remoteCb != 0) {
+ remoteCb->onRequestQueueEmpty();
+ }
+}
+
void CameraDeviceClient::detachDevice() {
if (mDevice == 0) return;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index de283ea..68e453c 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -164,6 +164,7 @@
const CaptureResultExtras& resultExtras);
virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
virtual void notifyPrepared(int streamId);
+ virtual void notifyRequestQueueEmpty();
virtual void notifyRepeatingRequestError(long lastFrameNumber);
/**
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index ccd1e4d..7e26153 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -307,6 +307,12 @@
}
template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyRequestQueueEmpty() {
+
+ ALOGV("%s: Request queue now empty", __FUNCTION__);
+}
+
+template <typename TClientBase>
void Camera2ClientBase<TClientBase>::notifyRepeatingRequestError(long lastFrameNumber) {
(void)lastFrameNumber;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index dbbf638..9fd0a78 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -73,6 +73,7 @@
virtual void notifyAutoWhitebalance(uint8_t newState,
int triggerId);
virtual void notifyPrepared(int streamId);
+ virtual void notifyRequestQueueEmpty();
virtual void notifyRepeatingRequestError(long lastFrameNumber);
int getCameraId() const;
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 984d84b..f30afe3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -204,6 +204,7 @@
virtual void notifyShutter(const CaptureResultExtras &resultExtras,
nsecs_t timestamp) = 0;
virtual void notifyPrepared(int streamId) = 0;
+ virtual void notifyRequestQueueEmpty() = 0;
// Required only for API1
virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 73a4124..2f3251f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -686,7 +686,8 @@
}
status_t Camera3Device::convertMetadataListToRequestListLocked(
- const List<const CameraMetadata> &metadataList, RequestList *requestList) {
+ const List<const CameraMetadata> &metadataList, bool repeating,
+ RequestList *requestList) {
if (requestList == NULL) {
CLOGE("requestList cannot be NULL.");
return BAD_VALUE;
@@ -701,6 +702,8 @@
return BAD_VALUE;
}
+ newRequest->mRepeating = repeating;
+
// Setup burst Id and request Id
newRequest->mResultExtras.burstId = burstId++;
if (it->exists(ANDROID_REQUEST_ID)) {
@@ -757,7 +760,8 @@
RequestList requestList;
- res = convertMetadataListToRequestListLocked(requests, /*out*/&requestList);
+ res = convertMetadataListToRequestListLocked(requests, repeating,
+ /*out*/&requestList);
if (res != OK) {
// error logged by previous call
return res;
@@ -3535,6 +3539,12 @@
mRequestQueue.begin();
nextRequest = *firstRequest;
mRequestQueue.erase(firstRequest);
+ if (mRequestQueue.empty() && !nextRequest->mRepeating) {
+ sp<NotificationListener> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->notifyRequestQueueEmpty();
+ }
+ }
}
// In case we've been unpaused by setPaused clearing mDoPause, need to
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 33429a6..31901bc 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -297,6 +297,8 @@
// requests will be submitted to HAL at a time. The batch size for
// the following 7 requests will be ignored by the request thread.
int mBatchSize;
+ // Whether this request is from a repeating or repeating burst.
+ bool mRepeating;
};
typedef List<sp<CaptureRequest> > RequestList;
@@ -304,6 +306,7 @@
status_t convertMetadataListToRequestListLocked(
const List<const CameraMetadata> &metadataList,
+ bool repeating,
/*out*/
RequestList *requestList);
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 8baaf13..4ce5c38 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -27,14 +27,8 @@
liblog \
libmedia \
libmediadrm \
- libmediaplayerservice \
- libstagefright \
- libui \
libutils \
-LOCAL_C_INCLUDES := \
- frameworks/av/media/libmediaplayerservice \
-
LOCAL_CFLAGS += -Wall -Wextra -Werror
LOCAL_MODULE:= mediadrmserver
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
index f5d74d3..219c372 100644
--- a/services/radio/Android.mk
+++ b/services/radio/Android.mk
@@ -18,10 +18,10 @@
LOCAL_SRC_FILES:= \
- RadioService.cpp
+ RadioService.cpp \
+ RadioHalLegacy.cpp
LOCAL_SHARED_LIBRARIES:= \
- libui \
liblog \
libutils \
libbinder \
diff --git a/services/radio/RadioHalLegacy.cpp b/services/radio/RadioHalLegacy.cpp
new file mode 100644
index 0000000..d50ccd4
--- /dev/null
+++ b/services/radio/RadioHalLegacy.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 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 "RadioHalLegacy"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include "RadioHalLegacy.h"
+
+namespace android {
+
+const char *RadioHalLegacy::sClassModuleNames[] = {
+ RADIO_HARDWARE_MODULE_ID_FM, /* corresponds to RADIO_CLASS_AM_FM */
+ RADIO_HARDWARE_MODULE_ID_SAT, /* corresponds to RADIO_CLASS_SAT */
+ RADIO_HARDWARE_MODULE_ID_DT, /* corresponds to RADIO_CLASS_DT */
+};
+
+/* static */
+sp<RadioInterface> RadioInterface::connectModule(radio_class_t classId)
+{
+ return new RadioHalLegacy(classId);
+}
+
+RadioHalLegacy::RadioHalLegacy(radio_class_t classId)
+ : RadioInterface(), mClassId(classId), mHwDevice(NULL)
+{
+}
+
+void RadioHalLegacy::onFirstRef()
+{
+ const hw_module_t *mod;
+ int rc;
+ ALOGI("%s mClassId %d", __FUNCTION__, mClassId);
+
+ mHwDevice = NULL;
+
+ if ((mClassId < 0) ||
+ (mClassId >= NELEM(sClassModuleNames))) {
+ ALOGE("invalid class ID %d", mClassId);
+ return;
+ }
+
+ ALOGI("%s RADIO_HARDWARE_MODULE_ID %s %s",
+ __FUNCTION__, RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId]);
+
+ rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId], &mod);
+ if (rc != 0) {
+ ALOGE("couldn't load radio module %s.%s (%s)",
+ RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId], strerror(-rc));
+ return;
+ }
+ rc = radio_hw_device_open(mod, &mHwDevice);
+ if (rc != 0) {
+ ALOGE("couldn't open radio hw device in %s.%s (%s)",
+ RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+ mHwDevice = NULL;
+ return;
+ }
+ if (mHwDevice->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("wrong radio hw device version %04x", mHwDevice->common.version);
+ radio_hw_device_close(mHwDevice);
+ mHwDevice = NULL;
+ }
+}
+
+RadioHalLegacy::~RadioHalLegacy()
+{
+ if (mHwDevice != NULL) {
+ radio_hw_device_close(mHwDevice);
+ }
+}
+
+int RadioHalLegacy::getProperties(radio_hal_properties_t *properties)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+
+ int rc = mHwDevice->get_properties(mHwDevice, properties);
+ if (rc != 0) {
+ ALOGE("could not read implementation properties");
+ }
+
+ return rc;
+}
+
+int RadioHalLegacy::openTuner(const radio_hal_band_config_t *config,
+ bool audio,
+ sp<TunerCallbackInterface> callback,
+ sp<TunerInterface>& tuner)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ sp<Tuner> tunerImpl = new Tuner(callback);
+
+ const struct radio_tuner *halTuner;
+ int rc = mHwDevice->open_tuner(mHwDevice, config, audio,
+ RadioHalLegacy::Tuner::callback, tunerImpl.get(),
+ &halTuner);
+ if (rc == 0) {
+ tunerImpl->setHalTuner(halTuner);
+ tuner = tunerImpl;
+ }
+ return rc;
+}
+
+int RadioHalLegacy::closeTuner(sp<TunerInterface>& tuner)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ if (tuner == 0) {
+ return -EINVAL;
+ }
+ sp<Tuner> tunerImpl = (Tuner *)tuner.get();
+ return mHwDevice->close_tuner(mHwDevice, tunerImpl->getHalTuner());
+}
+
+int RadioHalLegacy::Tuner::setConfiguration(const radio_hal_band_config_t *config)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->set_configuration(mHalTuner, config);
+}
+
+int RadioHalLegacy::Tuner::getConfiguration(radio_hal_band_config_t *config)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->get_configuration(mHalTuner, config);
+}
+
+int RadioHalLegacy::Tuner::scan(radio_direction_t direction, bool skip_sub_channel)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->scan(mHalTuner, direction, skip_sub_channel);
+}
+
+int RadioHalLegacy::Tuner::step(radio_direction_t direction, bool skip_sub_channel)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->step(mHalTuner, direction, skip_sub_channel);
+}
+
+int RadioHalLegacy::Tuner::tune(unsigned int channel, unsigned int sub_channel)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->tune(mHalTuner, channel, sub_channel);
+}
+
+int RadioHalLegacy::Tuner::cancel()
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->cancel(mHalTuner);
+}
+
+int RadioHalLegacy::Tuner::getProgramInformation(radio_program_info_t *info)
+{
+ if (mHalTuner == NULL) {
+ return -ENODEV;
+ }
+ return mHalTuner->get_program_information(mHalTuner, info);
+}
+
+void RadioHalLegacy::Tuner::onCallback(radio_hal_event_t *halEvent)
+{
+ if (mCallback != 0) {
+ mCallback->onEvent(halEvent);
+ }
+}
+
+//static
+void RadioHalLegacy::Tuner::callback(radio_hal_event_t *halEvent, void *cookie)
+{
+ wp<RadioHalLegacy::Tuner> weak = wp<RadioHalLegacy::Tuner>((RadioHalLegacy::Tuner *)cookie);
+ sp<RadioHalLegacy::Tuner> tuner = weak.promote();
+ if (tuner != 0) {
+ tuner->onCallback(halEvent);
+ }
+}
+
+RadioHalLegacy::Tuner::Tuner(sp<TunerCallbackInterface> callback)
+ : TunerInterface(), mHalTuner(NULL), mCallback(callback)
+{
+}
+
+
+RadioHalLegacy::Tuner::~Tuner()
+{
+}
+
+
+} // namespace android
diff --git a/services/radio/RadioHalLegacy.h b/services/radio/RadioHalLegacy.h
new file mode 100644
index 0000000..7d4831b
--- /dev/null
+++ b/services/radio/RadioHalLegacy.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
+#define ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
+
+#include <utils/RefBase.h>
+#include <hardware/radio.h>
+#include "RadioInterface.h"
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
+
+namespace android {
+
+class RadioHalLegacy : public RadioInterface
+{
+public:
+ RadioHalLegacy(radio_class_t classId);
+
+ // RadioInterface
+ virtual int getProperties(radio_hal_properties_t *properties);
+ virtual int openTuner(const radio_hal_band_config_t *config,
+ bool audio,
+ sp<TunerCallbackInterface> callback,
+ sp<TunerInterface>& tuner);
+ virtual int closeTuner(sp<TunerInterface>& tuner);
+
+ // RefBase
+ virtual void onFirstRef();
+
+ class Tuner : public TunerInterface
+ {
+ public:
+ Tuner(sp<TunerCallbackInterface> callback);
+
+ virtual int setConfiguration(const radio_hal_band_config_t *config);
+ virtual int getConfiguration(radio_hal_band_config_t *config);
+ virtual int scan(radio_direction_t direction, bool skip_sub_channel);
+ virtual int step(radio_direction_t direction, bool skip_sub_channel);
+ virtual int tune(unsigned int channel, unsigned int sub_channel);
+ virtual int cancel();
+ virtual int getProgramInformation(radio_program_info_t *info);
+
+ static void callback(radio_hal_event_t *halEvent, void *cookie);
+ void onCallback(radio_hal_event_t *halEvent);
+
+ void setHalTuner(const struct radio_tuner *halTuner) { mHalTuner = halTuner; }
+ const struct radio_tuner *getHalTuner() { return mHalTuner; }
+
+ private:
+ virtual ~Tuner();
+
+ const struct radio_tuner *mHalTuner;
+ sp<TunerCallbackInterface> mCallback;
+ };
+
+protected:
+ virtual ~RadioHalLegacy();
+
+private:
+ static const char * sClassModuleNames[];
+
+ radio_class_t mClassId;
+ struct radio_hw_device *mHwDevice;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
diff --git a/services/radio/RadioInterface.h b/services/radio/RadioInterface.h
new file mode 100644
index 0000000..fcfb4d5
--- /dev/null
+++ b/services/radio/RadioInterface.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_RADIO_INTERFACE_H
+#define ANDROID_HARDWARE_RADIO_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
+
+namespace android {
+
+class RadioInterface : public virtual RefBase
+{
+public:
+ /* get a sound trigger HAL instance */
+ static sp<RadioInterface> connectModule(radio_class_t classId);
+
+ /*
+ * Retrieve implementation properties.
+ *
+ * arguments:
+ * - properties: where to return the module properties
+ *
+ * returns:
+ * 0 if no error
+ * -EINVAL if invalid arguments are passed
+ */
+ virtual int getProperties(radio_hal_properties_t *properties) = 0;
+
+ /*
+ * Open a tuner interface for the requested configuration.
+ * If no other tuner is opened, this will activate the radio module.
+ *
+ * arguments:
+ * - config: the band configuration to apply
+ * - audio: this tuner will be used for live radio listening and should be connected to
+ * the radio audio source.
+ * - callback: the event callback
+ * - cookie: the cookie to pass when calling the callback
+ * - tuner: where to return the tuner interface
+ *
+ * returns:
+ * 0 if HW was powered up and configuration could be applied
+ * -EINVAL if configuration requested is invalid
+ * -ENOSYS if called out of sequence
+ *
+ * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+ * configuration is applied or a failure occurs or after a time out.
+ */
+ virtual int openTuner(const radio_hal_band_config_t *config,
+ bool audio,
+ sp<TunerCallbackInterface> callback,
+ sp<TunerInterface>& tuner) = 0;
+
+ /*
+ * Close a tuner interface.
+ * If the last tuner is closed, the radio module is deactivated.
+ *
+ * arguments:
+ * - tuner: the tuner interface to close
+ *
+ * returns:
+ * 0 if powered down successfully.
+ * -EINVAL if an invalid argument is passed
+ * -ENOSYS if called out of sequence
+ */
+ virtual int closeTuner(sp<TunerInterface>& tuner) = 0;
+
+protected:
+ RadioInterface() {}
+ virtual ~RadioInterface() {}
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_INTERFACE_H
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index 5a3f750..a73ed8f 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -51,31 +51,15 @@
void RadioService::onFirstRef()
{
- const hw_module_t *mod;
- int rc;
- struct radio_hw_device *dev;
-
ALOGI("%s", __FUNCTION__);
- rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
- if (rc != 0) {
- ALOGE("couldn't load radio module %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
- return;
- }
- rc = radio_hw_device_open(mod, &dev);
- if (rc != 0) {
- ALOGE("couldn't open radio hw device in %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
- return;
- }
- if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
- ALOGE("wrong radio hw device version %04x", dev->common.version);
- return;
- }
+ sp<RadioInterface> dev = RadioInterface::connectModule(RADIO_CLASS_AM_FM);
+ if (dev == 0) {
+ return;
+ }
struct radio_hal_properties halProperties;
- rc = dev->get_properties(dev, &halProperties);
+ int rc = dev->getProperties(&halProperties);
if (rc != 0) {
ALOGE("could not read implementation properties");
return;
@@ -94,9 +78,6 @@
RadioService::~RadioService()
{
- for (size_t i = 0; i < mModules.size(); i++) {
- radio_hw_device_close(mModules.valueAt(i)->hwDevice());
- }
}
status_t RadioService::listModules(struct radio_properties *properties,
@@ -108,7 +89,7 @@
if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
return BAD_VALUE;
}
- size_t maxModules = *numModules;
+ uint32_t maxModules = *numModules;
*numModules = mModules.size();
for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
properties[i] = mModules.valueAt(i)->properties();
@@ -192,16 +173,6 @@
}
-// static
-void RadioService::callback(radio_hal_event_t *halEvent, void *cookie)
-{
- CallbackThread *callbackThread = (CallbackThread *)cookie;
- if (callbackThread == NULL) {
- return;
- }
- callbackThread->sendEvent(halEvent);
-}
-
/* static */
void RadioService::convertProperties(radio_properties_t *properties,
const radio_hal_properties_t *halProperties)
@@ -305,32 +276,40 @@
{
sp<IMemory> eventMemory;
- size_t headerSize =
- (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int);
- size_t metadataSize = 0;
+ // The event layout in shared memory is:
+ // sizeof(struct radio_event) bytes : the event itself
+ // 4 bytes : metadata size or 0
+ // N bytes : metadata if present
+ uint32_t metadataOffset = sizeof(struct radio_event) + sizeof(uint32_t);
+ uint32_t metadataSize = 0;
+
switch (halEvent->type) {
case RADIO_EVENT_TUNED:
case RADIO_EVENT_AF_SWITCH:
if (radio_metadata_check(halEvent->info.metadata) == 0) {
- metadataSize = radio_metadata_get_size(halEvent->info.metadata);
+ metadataSize = (uint32_t)radio_metadata_get_size(halEvent->info.metadata);
}
break;
case RADIO_EVENT_METADATA:
if (radio_metadata_check(halEvent->metadata) != 0) {
return eventMemory;
}
- metadataSize = radio_metadata_get_size(halEvent->metadata);
+ metadataSize = (uint32_t)radio_metadata_get_size(halEvent->metadata);
break;
default:
break;
}
- size_t size = headerSize + metadataSize;
- eventMemory = mMemoryDealer->allocate(size);
+
+ eventMemory = mMemoryDealer->allocate(metadataOffset + metadataSize);
if (eventMemory == 0 || eventMemory->pointer() == NULL) {
eventMemory.clear();
return eventMemory;
}
+
struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+
+ *(uint32_t *)((uint8_t *)event + metadataOffset - sizeof(uint32_t)) = metadataSize;
+
event->type = halEvent->type;
event->status = halEvent->status;
@@ -342,10 +321,7 @@
case RADIO_EVENT_AF_SWITCH:
event->info = halEvent->info;
if (metadataSize != 0) {
- memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize);
- // replace meta data pointer by offset while in shared memory so that receiving side
- // can restore the pointer in destination process.
- event->info.metadata = (radio_metadata_t *)headerSize;
+ memcpy((uint8_t *)event + metadataOffset, halEvent->info.metadata, metadataSize);
}
break;
case RADIO_EVENT_TA:
@@ -355,10 +331,9 @@
event->on = halEvent->on;
break;
case RADIO_EVENT_METADATA:
- memcpy((char *)event + headerSize, halEvent->metadata, metadataSize);
- // replace meta data pointer by offset while in shared memory so that receiving side
- // can restore the pointer in destination process.
- event->metadata = (radio_metadata_t *)headerSize;
+ if (metadataSize != 0) {
+ memcpy((uint8_t *)event + metadataOffset, halEvent->metadata, metadataSize);
+ }
break;
case RADIO_EVENT_HW_FAILURE:
default:
@@ -385,12 +360,13 @@
#undef LOG_TAG
#define LOG_TAG "RadioService::Module"
-RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties)
+RadioService::Module::Module(sp<RadioInterface> hwDevice, radio_properties properties)
: mHwDevice(hwDevice), mProperties(properties), mMute(true)
{
}
RadioService::Module::~Module() {
+ mHwDevice.clear();
mModuleClients.clear();
}
@@ -404,10 +380,15 @@
bool audio)
{
ALOGV("addClient() %p config %p product %s", this, config, mProperties.product);
+
AutoMutex lock(mLock);
sp<ModuleClient> moduleClient;
int ret;
+ if (mHwDevice == 0) {
+ return moduleClient;
+ }
+
for (size_t i = 0; i < mModuleClients.size(); i++) {
if (mModuleClients[i]->client() == client) {
// client already connected: reject
@@ -464,7 +445,7 @@
}
}
- const struct radio_tuner *halTuner;
+ sp<TunerInterface> halTuner;
sp<ModuleClient> preemtedClient;
if (audio) {
if (allocatedAudio >= mProperties.num_audio_sources) {
@@ -484,18 +465,19 @@
}
if (preemtedClient != 0) {
halTuner = preemtedClient->getTuner();
- preemtedClient->setTuner(NULL);
- mHwDevice->close_tuner(mHwDevice, halTuner);
+ sp<TunerInterface> clear;
+ preemtedClient->setTuner(clear);
+ mHwDevice->closeTuner(halTuner);
if (preemtedClient->audio()) {
notifyDeviceConnection(false, "");
}
}
- ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
- RadioService::callback, moduleClient->callbackThread().get(),
- &halTuner);
+ ret = mHwDevice->openTuner(&halConfig, audio,
+ moduleClient,
+ halTuner);
if (ret == 0) {
- ALOGV("addClient() setTuner %p", halTuner);
+ ALOGV("addClient() setTuner %p", halTuner.get());
moduleClient->setTuner(halTuner);
mModuleClients.add(moduleClient);
if (audio) {
@@ -527,12 +509,15 @@
}
mModuleClients.removeAt(index);
- const struct radio_tuner *halTuner = moduleClient->getTuner();
+ sp<TunerInterface> halTuner = moduleClient->getTuner();
if (halTuner == NULL) {
return;
}
- mHwDevice->close_tuner(mHwDevice, halTuner);
+ if (mHwDevice != 0) {
+ mHwDevice->closeTuner(halTuner);
+ }
+
if (moduleClient->audio()) {
notifyDeviceConnection(false, "");
}
@@ -543,6 +528,10 @@
return;
}
+ if (mHwDevice == 0) {
+ return;
+ }
+
// Tuner reallocation logic:
// When a client is removed and was controlling a tuner, this tuner will be allocated to a
// previously preempted client. This client will be notified by a callback with
@@ -591,9 +580,9 @@
ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner");
struct radio_hal_band_config halConfig = youngestClient->halConfig();
- ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(),
- RadioService::callback, moduleClient->callbackThread().get(),
- &halTuner);
+ ret = mHwDevice->openTuner(&halConfig, youngestClient->audio(),
+ moduleClient,
+ halTuner);
if (ret == 0) {
youngestClient->setTuner(halTuner);
@@ -646,7 +635,7 @@
const sp<IRadioClient>& client,
const struct radio_band_config *config,
bool audio)
- : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL)
+ : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(0)
{
}
@@ -666,6 +655,11 @@
}
}
+void RadioService::ModuleClient::onEvent(radio_hal_event_t *halEvent)
+{
+ mCallbackThread->sendEvent(halEvent);
+}
+
status_t RadioService::ModuleClient::dump(int fd __unused,
const Vector<String16>& args __unused) {
String8 result;
@@ -696,14 +690,14 @@
return mConfig.band;
}
-const struct radio_tuner *RadioService::ModuleClient::getTuner() const
+sp<TunerInterface>& RadioService::ModuleClient::getTuner()
{
AutoMutex lock(mLock);
ALOGV("%s locked", __FUNCTION__);
return mTuner;
}
-void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner)
+void RadioService::ModuleClient::setTuner(sp<TunerInterface>& tuner)
{
ALOGV("%s %p", __FUNCTION__, this);
@@ -714,7 +708,7 @@
radio_hal_event_t event;
event.type = RADIO_EVENT_CONTROL;
event.status = 0;
- event.on = mTuner != NULL;
+ event.on = mTuner != 0;
mCallbackThread->sendEvent(&event);
ALOGV("%s DONE", __FUNCTION__);
@@ -726,10 +720,10 @@
status_t status = NO_ERROR;
ALOGV("%s locked", __FUNCTION__);
- if (mTuner != NULL) {
+ if (mTuner != 0) {
struct radio_hal_band_config halConfig;
halConfig = config->band;
- status = (status_t)mTuner->set_configuration(mTuner, &halConfig);
+ status = (status_t)mTuner->setConfiguration(&halConfig);
if (status == NO_ERROR) {
mConfig = *config;
}
@@ -747,9 +741,9 @@
status_t status = NO_ERROR;
ALOGV("%s locked", __FUNCTION__);
- if (mTuner != NULL) {
+ if (mTuner != 0) {
struct radio_hal_band_config halConfig;
- status = (status_t)mTuner->get_configuration(mTuner, &halConfig);
+ status = (status_t)mTuner->getConfiguration(&halConfig);
if (status == NO_ERROR) {
mConfig.band = halConfig;
}
@@ -765,7 +759,7 @@
{
Mutex::Autolock _l(mLock);
ALOGV("%s locked", __FUNCTION__);
- if (mTuner == NULL || !mAudio) {
+ if (mTuner == 0 || !mAudio) {
return INVALID_OPERATION;
}
module = mModule.promote();
@@ -796,8 +790,8 @@
AutoMutex lock(mLock);
ALOGV("%s locked", __FUNCTION__);
status_t status;
- if (mTuner != NULL) {
- status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel);
+ if (mTuner != 0) {
+ status = (status_t)mTuner->scan(direction, skipSubChannel);
} else {
status = INVALID_OPERATION;
}
@@ -809,21 +803,21 @@
AutoMutex lock(mLock);
ALOGV("%s locked", __FUNCTION__);
status_t status;
- if (mTuner != NULL) {
- status = (status_t)mTuner->step(mTuner, direction, skipSubChannel);
+ if (mTuner != 0) {
+ status = (status_t)mTuner->step(direction, skipSubChannel);
} else {
status = INVALID_OPERATION;
}
return status;
}
-status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel)
+status_t RadioService::ModuleClient::tune(uint32_t channel, uint32_t subChannel)
{
AutoMutex lock(mLock);
ALOGV("%s locked", __FUNCTION__);
status_t status;
- if (mTuner != NULL) {
- status = (status_t)mTuner->tune(mTuner, channel, subChannel);
+ if (mTuner != 0) {
+ status = (status_t)mTuner->tune(channel, subChannel);
} else {
status = INVALID_OPERATION;
}
@@ -835,8 +829,8 @@
AutoMutex lock(mLock);
ALOGV("%s locked", __FUNCTION__);
status_t status;
- if (mTuner != NULL) {
- status = (status_t)mTuner->cancel(mTuner);
+ if (mTuner != 0) {
+ status = (status_t)mTuner->cancel();
} else {
status = INVALID_OPERATION;
}
@@ -849,10 +843,11 @@
ALOGV("%s locked", __FUNCTION__);
status_t status;
if (mTuner != NULL) {
- status = (status_t)mTuner->get_program_information(mTuner, info);
+ status = (status_t)mTuner->getProgramInformation(info);
} else {
status = INVALID_OPERATION;
}
+
return status;
}
@@ -860,7 +855,7 @@
{
Mutex::Autolock lock(mLock);
ALOGV("%s locked", __FUNCTION__);
- *hasControl = mTuner != NULL;
+ *hasControl = mTuner != 0;
return NO_ERROR;
}
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
index ac3481e..444eb7a 100644
--- a/services/radio/RadioService.h
+++ b/services/radio/RadioService.h
@@ -27,6 +27,9 @@
#include <radio/IRadioClient.h>
#include <system/radio.h>
#include <hardware/radio.h>
+#include "RadioInterface.h"
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
namespace android {
@@ -66,7 +69,7 @@
class Module : public virtual RefBase {
public:
- Module(radio_hw_device* hwDevice,
+ Module(sp<RadioInterface> hwDevice,
struct radio_properties properties);
virtual ~Module();
@@ -83,7 +86,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
- const struct radio_hw_device *hwDevice() const { return mHwDevice; }
+ sp<RadioInterface> hwDevice() const { return mHwDevice; }
const struct radio_properties properties() const { return mProperties; }
const struct radio_band_config *getDefaultConfig() const ;
@@ -92,7 +95,7 @@
void notifyDeviceConnection(bool connected, const char *address);
Mutex mLock; // protects mModuleClients
- const struct radio_hw_device *mHwDevice; // HAL hardware device
+ sp<RadioInterface> mHwDevice; // HAL hardware device
const struct radio_properties mProperties; // cached hardware module properties
Vector< sp<ModuleClient> > mModuleClients; // list of attached clients
bool mMute; // radio audio source state
@@ -128,7 +131,8 @@
}; // class CallbackThread
class ModuleClient : public BnRadio,
- public IBinder::DeathRecipient {
+ public IBinder::DeathRecipient,
+ public TunerCallbackInterface {
public:
ModuleClient(const sp<Module>& module,
@@ -167,8 +171,8 @@
wp<Module> module() const { return mModule; }
radio_hal_band_config_t halConfig() const;
sp<CallbackThread> callbackThread() const { return mCallbackThread; }
- void setTuner(const struct radio_tuner *tuner);
- const struct radio_tuner *getTuner() const;
+ void setTuner(sp<TunerInterface>& tuner);
+ sp<TunerInterface>& getTuner();
bool audio() const { return mAudio; }
void onCallbackEvent(const sp<IMemory>& event);
@@ -179,6 +183,9 @@
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
+ // TunerCallbackInterface
+ virtual void onEvent(radio_hal_event_t *event);
+
private:
mutable Mutex mLock; // protects mClient, mConfig and mTuner
@@ -187,7 +194,7 @@
radio_band_config_t mConfig; // current band configuration
sp<CallbackThread> mCallbackThread; // event callback thread
const bool mAudio;
- const struct radio_tuner *mTuner; // HAL tuner interface. NULL indicates that
+ sp<TunerInterface> mTuner; // HAL tuner interface. NULL indicates that
// this client does not have control on any
// tuner
}; // class ModuleClient
diff --git a/services/radio/TunerCallbackInterface.h b/services/radio/TunerCallbackInterface.h
new file mode 100644
index 0000000..4973cce
--- /dev/null
+++ b/services/radio/TunerCallbackInterface.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
+#define ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class TunerCallbackInterface : public virtual RefBase
+{
+public:
+ virtual void onEvent(radio_hal_event_t *event) = 0;
+
+protected:
+ TunerCallbackInterface() {}
+ virtual ~TunerCallbackInterface() {}
+
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
diff --git a/services/radio/TunerInterface.h b/services/radio/TunerInterface.h
new file mode 100644
index 0000000..4e657d3
--- /dev/null
+++ b/services/radio/TunerInterface.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_TUNER_INTERFACE_H
+#define ANDROID_HARDWARE_TUNER_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class TunerInterface : public virtual RefBase
+{
+public:
+ /*
+ * Apply current radio band configuration (band, range, channel spacing ...).
+ *
+ * arguments:
+ * - config: the band configuration to apply
+ *
+ * returns:
+ * 0 if configuration could be applied
+ * -EINVAL if configuration requested is invalid
+ *
+ * Automatically cancels pending scan, step or tune.
+ *
+ * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+ * configuration is applied or a failure occurs or after a time out.
+ */
+ virtual int setConfiguration(const radio_hal_band_config_t *config) = 0;
+
+ /*
+ * Retrieve current radio band configuration.
+ *
+ * arguments:
+ * - config: where to return the band configuration
+ *
+ * returns:
+ * 0 if valid configuration is returned
+ * -EINVAL if invalid arguments are passed
+ */
+ virtual int getConfiguration(radio_hal_band_config_t *config) = 0;
+
+ /*
+ * Start scanning up to next valid station.
+ * Must be called when a valid configuration has been applied.
+ *
+ * arguments:
+ * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+ * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+ * (e.g SPS for HD radio).
+ *
+ * returns:
+ * 0 if scan successfully started
+ * -ENOSYS if called out of sequence
+ * -ENODEV if another error occurs
+ *
+ * Automatically cancels pending scan, step or tune.
+ *
+ * Callback function with event RADIO_EVENT_TUNED MUST be called once
+ * locked on a station or after a time out or full frequency scan if
+ * no station found. The event status should indicate if a valid station
+ * is tuned or not.
+ */
+ virtual int scan(radio_direction_t direction, bool skip_sub_channel) = 0;
+
+ /*
+ * Move one channel spacing up or down.
+ * Must be called when a valid configuration has been applied.
+ *
+ * arguments:
+ * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+ * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+ * (e.g SPS for HD radio).
+ *
+ * returns:
+ * 0 if step successfully started
+ * -ENOSYS if called out of sequence
+ * -ENODEV if another error occurs
+ *
+ * Automatically cancels pending scan, step or tune.
+ *
+ * Callback function with event RADIO_EVENT_TUNED MUST be called once
+ * step completed or after a time out. The event status should indicate
+ * if a valid station is tuned or not.
+ */
+ virtual int step(radio_direction_t direction, bool skip_sub_channel) = 0;
+
+ /*
+ * Tune to specified frequency.
+ * Must be called when a valid configuration has been applied.
+ *
+ * arguments:
+ * - channel: channel to tune to. A frequency in kHz for AM/FM/HD Radio bands.
+ * - sub_channel: valid for HD radio or digital radios only: (e.g SPS number for HD radio).
+ *
+ * returns:
+ * 0 if tune successfully started
+ * -ENOSYS if called out of sequence
+ * -EINVAL if invalid arguments are passed
+ * -ENODEV if another error occurs
+ *
+ * Automatically cancels pending scan, step or tune.
+ *
+ * Callback function with event RADIO_EVENT_TUNED MUST be called once
+ * tuned or after a time out. The event status should indicate
+ * if a valid station is tuned or not.
+ */
+ virtual int tune(unsigned int channel, unsigned int sub_channel) = 0;
+
+ /*
+ * Cancel a scan, step or tune operation.
+ * Must be called while a scan, step or tune operation is pending
+ * (callback not yet sent).
+ *
+ * returns:
+ * 0 if successful
+ * -ENOSYS if called out of sequence
+ * -ENODEV if another error occurs
+ *
+ * The callback is not sent.
+ */
+ virtual int cancel() = 0;
+
+ /*
+ * Retrieve current station information.
+ *
+ * arguments:
+ * - info: where to return the program info.
+ * If info->metadata is NULL. no meta data should be returned.
+ * If meta data must be returned, they should be added to or cloned to
+ * info->metadata, not passed from a newly created meta data buffer.
+ *
+ * returns:
+ * 0 if tuned and information available
+ * -EINVAL if invalid arguments are passed
+ * -ENODEV if another error occurs
+ */
+ virtual int getProgramInformation(radio_program_info_t *info) = 0;
+
+protected:
+ TunerInterface() {}
+ virtual ~TunerInterface() {}
+
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TUNER_INTERFACE_H
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index c55ac7f..2533132 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -16,7 +16,6 @@
include $(CLEAR_VARS)
-
ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE
endif
@@ -25,7 +24,6 @@
SoundTriggerHwService.cpp
LOCAL_SHARED_LIBRARIES:= \
- libui \
liblog \
libutils \
libbinder \
@@ -35,12 +33,32 @@
libmedia \
libserviceutility
+
+ifeq ($(ENABLE_TREBLE),true)
+# Treble configuration
+LOCAL_CFLAGS += -DENABLE_TREBLE
+LOCAL_SRC_FILES += \
+ SoundTriggerHalHidl.cpp
+
+LOCAL_SHARED_LIBRARIES += \
+ libhwbinder \
+ libhidl \
+ libbase \
+ android.hardware.soundtrigger@2.0 \
+ android.hardware.audio.common@2.0
+else
+# libhardware configuration
+LOCAL_SRC_FILES += \
+ SoundTriggerHalLegacy.cpp
+endif
+
+
LOCAL_C_INCLUDES += \
$(TOPDIR)frameworks/av/services/audioflinger
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
-LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -Wall -Werror
LOCAL_MODULE:= libsoundtriggerservice
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
new file mode 100644
index 0000000..e71d742
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2016 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 "SoundTriggerHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include "SoundTriggerHalHidl.h"
+#include <hidl/IServiceManager.h>
+#include <hwbinder/IPCThreadState.h>
+#include <hwbinder/ProcessState.h>
+
+namespace android {
+
+using android::hardware::Return;
+using android::hardware::ProcessState;
+using android::hardware::audio::common::V2_0::AudioDevice;
+
+pthread_once_t SoundTriggerHalHidl::sOnceControl = PTHREAD_ONCE_INIT;
+
+void SoundTriggerHalHidl::sOnceInit()
+{
+ ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->startThreadPool();
+}
+
+/* static */
+sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
+{
+ return new SoundTriggerHalHidl(moduleName);
+}
+
+int SoundTriggerHalHidl::getProperties(struct sound_trigger_properties *properties)
+{
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ ISoundTriggerHw::Properties halProperties;
+ Return<void> hidlReturn;
+ int32_t halReturn;
+ {
+ AutoMutex lock(mHalLock);
+ hidlReturn = soundtrigger->getProperties([&](int rc, auto res) {
+ halReturn = rc;
+ halProperties = res;
+ ALOGI("getProperties res implementor %s", res.implementor.c_str());
+ });
+ }
+
+ int ret = 0;
+ if (hidlReturn.getStatus().isOk()) {
+ convertPropertiesFromHal(properties, &halProperties);
+ } else {
+ ret = (int)hidlReturn.getStatus().transactionError();
+ if (ret == -EPIPE) {
+ clearService();
+ }
+ }
+
+ return ret;
+}
+
+int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_model,
+ sound_model_callback_t callback,
+ void *cookie,
+ sound_model_handle_t *handle)
+{
+ if (handle == NULL) {
+ return -EINVAL;
+ }
+
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ uint32_t modelId;
+ {
+ AutoMutex lock(mLock);
+ do {
+ modelId = nextUniqueId();
+ ALOGI("loadSoundModel modelId %u", modelId);
+ sp<SoundModel> model = mSoundModels.valueFor(modelId);
+ ALOGI("loadSoundModel model %p", model.get());
+ } while (mSoundModels.valueFor(modelId) != 0 && modelId != 0);
+ }
+ LOG_ALWAYS_FATAL_IF(modelId == 0,
+ "loadSoundModel(): wrap around in sound model IDs, num loaded models %zd",
+ mSoundModels.size());
+
+ ISoundTriggerHw::SoundModel *halSoundModel =
+ convertSoundModelToHal(sound_model);
+ if (halSoundModel == NULL) {
+ return -EINVAL;
+ }
+
+ Return<void> hidlReturn;
+ int32_t halReturn;
+ SoundModelHandle halHandle;
+ {
+ AutoMutex lock(mHalLock);
+ if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ hidlReturn = soundtrigger->loadPhraseSoundModel(
+ *(const ISoundTriggerHw::PhraseSoundModel *)halSoundModel,
+ this, modelId, [&](int32_t retval, auto res) {
+ halReturn = retval;
+ halHandle = res;
+ });
+
+ } else {
+ hidlReturn = soundtrigger->loadSoundModel(*halSoundModel,
+ this, modelId, [&](int32_t retval, auto res) {
+ halReturn = retval;
+ halHandle = res;
+ });
+ }
+ }
+
+ delete halSoundModel;
+
+ int ret = 0;
+ if (hidlReturn.getStatus().isOk()) {
+ AutoMutex lock(mLock);
+ *handle = (sound_model_handle_t)modelId;
+ sp<SoundModel> model = new SoundModel(*handle, callback, cookie, halHandle);
+ mSoundModels.add(*handle, model);
+ } else {
+ ret = (int)hidlReturn.getStatus().transactionError();
+ ALOGE("loadSoundModel error %d", ret);
+ if (ret == -EPIPE) {
+ clearService();
+ }
+ }
+
+
+ return ret;
+}
+
+int SoundTriggerHalHidl::unloadSoundModel(sound_model_handle_t handle)
+{
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ sp<SoundModel> model = removeModel(handle);
+ if (model == 0) {
+ ALOGE("unloadSoundModel model not found for handle %u", handle);
+ return -EINVAL;
+ }
+
+ Return<int32_t> halReturn(0);
+ {
+ AutoMutex lock(mHalLock);
+ halReturn = soundtrigger->unloadSoundModel(model->mHalHandle);
+ }
+
+ int ret = (int)halReturn.getStatus().transactionError();
+ ALOGE_IF(ret != 0, "unloadSoundModel error %d", ret);
+ if (ret == -EPIPE) {
+ clearService();
+ }
+
+ return ret;
+}
+
+int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle,
+ const struct sound_trigger_recognition_config *config,
+ recognition_callback_t callback,
+ void *cookie)
+{
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ sp<SoundModel> model = getModel(handle);
+ if (model == 0) {
+ ALOGE("startRecognition model not found for handle %u", handle);
+ return -EINVAL;
+ }
+
+ model->mRecognitionCallback = callback;
+ model->mRecognitionCookie = cookie;
+
+ ISoundTriggerHw::RecognitionConfig *halConfig =
+ convertRecognitionConfigToHal(config);
+
+ Return<int32_t> halReturn(0);
+ {
+ AutoMutex lock(mHalLock);
+ halReturn = soundtrigger->startRecognition(model->mHalHandle, *halConfig, this, handle);
+ }
+
+ delete halConfig;
+
+ int ret = (int)halReturn.getStatus().transactionError();
+ ALOGE_IF(ret != 0, "startRecognition error %d", ret);
+ if (ret == -EPIPE) {
+ clearService();
+ }
+ return ret;
+}
+
+int SoundTriggerHalHidl::stopRecognition(sound_model_handle_t handle)
+{
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ sp<SoundModel> model = getModel(handle);
+ if (model == 0) {
+ ALOGE("stopRecognition model not found for handle %u", handle);
+ return -EINVAL;
+ }
+
+ Return<int32_t> halReturn(0);
+ {
+ AutoMutex lock(mHalLock);
+ halReturn = soundtrigger->stopRecognition(model->mHalHandle);
+ }
+
+ int ret = (int)halReturn.getStatus().transactionError();
+ ALOGE_IF(ret != 0, "stopRecognition error %d", ret);
+ if (ret == -EPIPE) {
+ clearService();
+ }
+ return ret;
+}
+
+int SoundTriggerHalHidl::stopAllRecognitions()
+{
+ sp<ISoundTriggerHw> soundtrigger = getService();
+ if (soundtrigger == 0) {
+ return -ENODEV;
+ }
+
+ Return<int32_t> halReturn(0);
+ {
+ AutoMutex lock(mHalLock);
+ Return<int32_t> halReturn = soundtrigger->stopAllRecognitions();
+ }
+
+ int ret = (int)halReturn.getStatus().transactionError();
+ ALOGE_IF(ret != 0, "stopAllRecognitions error %d", ret);
+ if (ret == -EPIPE) {
+ clearService();
+ }
+ return ret;
+}
+
+SoundTriggerHalHidl::SoundTriggerHalHidl(const char *moduleName)
+ : mModuleName(moduleName), mNextUniqueId(1)
+{
+}
+
+void SoundTriggerHalHidl::onFirstRef()
+{
+ pthread_once(&sOnceControl, &sOnceInit);
+}
+
+SoundTriggerHalHidl::~SoundTriggerHalHidl()
+{
+}
+
+sp<ISoundTriggerHw> SoundTriggerHalHidl::getService()
+{
+ AutoMutex lock(mLock);
+ if (mISoundTrigger == 0) {
+ if (mModuleName == NULL) {
+ mModuleName = "primary";
+ }
+ std::string serviceName = "sound_trigger.";
+ serviceName.append(mModuleName);
+ mISoundTrigger = ISoundTriggerHw::getService(serviceName);
+ }
+ return mISoundTrigger;
+}
+
+void SoundTriggerHalHidl::clearService()
+{
+ AutoMutex lock(mLock);
+ mISoundTrigger = 0;
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::getModel(sound_model_handle_t handle)
+{
+ AutoMutex lock(mLock);
+ return mSoundModels.valueFor(handle);
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::removeModel(sound_model_handle_t handle)
+{
+ AutoMutex lock(mLock);
+ sp<SoundModel> model = mSoundModels.valueFor(handle);
+ mSoundModels.removeItem(handle);
+ return model;
+}
+
+uint32_t SoundTriggerHalHidl::nextUniqueId()
+{
+ return (uint32_t) atomic_fetch_add_explicit(&mNextUniqueId,
+ (uint_fast32_t) 1, memory_order_acq_rel);
+}
+
+void SoundTriggerHalHidl::convertUuidToHal(Uuid *halUuid,
+ const sound_trigger_uuid_t *uuid)
+{
+ halUuid->timeLow = uuid->timeLow;
+ halUuid->timeMid = uuid->timeMid;
+ halUuid->versionAndTimeHigh = uuid->timeHiAndVersion;
+ halUuid->variantAndClockSeqHigh = uuid->clockSeq;
+ memcpy(halUuid->node.data(), &uuid->node[0], sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertUuidFromHal(sound_trigger_uuid_t *uuid,
+ const Uuid *halUuid)
+{
+ uuid->timeLow = halUuid->timeLow;
+ uuid->timeMid = halUuid->timeMid;
+ uuid->timeHiAndVersion = halUuid->versionAndTimeHigh;
+ uuid->clockSeq = halUuid->variantAndClockSeqHigh;
+ memcpy(&uuid->node[0], halUuid->node.data(), sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertPropertiesFromHal(
+ struct sound_trigger_properties *properties,
+ const ISoundTriggerHw::Properties *halProperties)
+{
+ strlcpy(properties->implementor,
+ halProperties->implementor.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+ strlcpy(properties->description,
+ halProperties->description.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+ properties->version = halProperties->version;
+ convertUuidFromHal(&properties->uuid, &halProperties->uuid);
+ properties->max_sound_models = halProperties->maxSoundModels;
+ properties->max_key_phrases = halProperties->maxKeyPhrases;
+ properties->max_users = halProperties->maxUsers;
+ properties->recognition_modes = halProperties->recognitionModes;
+ properties->capture_transition = (bool)halProperties->captureTransition;
+ properties->max_buffer_ms = halProperties->maxBufferMs;
+ properties->concurrent_capture = (bool)halProperties->concurrentCapture;
+ properties->trigger_in_event = (bool)halProperties->triggerInEvent;
+ properties->power_consumption_mw = halProperties->powerConsumptionMw;
+}
+
+void SoundTriggerHalHidl::convertTriggerPhraseToHal(
+ ISoundTriggerHw::Phrase *halTriggerPhrase,
+ const struct sound_trigger_phrase *triggerPhrase)
+{
+ halTriggerPhrase->id = triggerPhrase->id;
+ halTriggerPhrase->recognitionModes = triggerPhrase->recognition_mode;
+ halTriggerPhrase->users.setToExternal((uint32_t *)&triggerPhrase->users[0], triggerPhrase->num_users);
+ halTriggerPhrase->locale = triggerPhrase->locale;
+ halTriggerPhrase->text = triggerPhrase->text;
+}
+
+ISoundTriggerHw::SoundModel *SoundTriggerHalHidl::convertSoundModelToHal(
+ const struct sound_trigger_sound_model *soundModel)
+{
+ ISoundTriggerHw::SoundModel *halModel = NULL;
+ if (soundModel->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel =
+ new ISoundTriggerHw::PhraseSoundModel();
+ struct sound_trigger_phrase_sound_model *keyPhraseModel =
+ (struct sound_trigger_phrase_sound_model *)soundModel;
+ ISoundTriggerHw::Phrase *halPhrases =
+ new ISoundTriggerHw::Phrase[keyPhraseModel->num_phrases];
+
+
+ for (unsigned int i = 0; i < keyPhraseModel->num_phrases; i++) {
+ convertTriggerPhraseToHal(&halPhrases[i],
+ &keyPhraseModel->phrases[i]);
+ }
+ halKeyPhraseModel->phrases.setToExternal(halPhrases, keyPhraseModel->num_phrases);
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ halKeyPhraseModel->phrases.resize(keyPhraseModel->num_phrases);
+
+ delete[] halPhrases;
+
+ halModel = (ISoundTriggerHw::SoundModel *)halKeyPhraseModel;
+ } else {
+ halModel = new ISoundTriggerHw::SoundModel();
+ }
+ halModel->type = (SoundModelType)soundModel->type;
+ convertUuidToHal(&halModel->uuid, &soundModel->uuid);
+ convertUuidToHal(&halModel->vendorUuid, &soundModel->vendor_uuid);
+ halModel->data.setToExternal((uint8_t *)soundModel + soundModel->data_offset, soundModel->data_size);
+ halModel->data.resize(soundModel->data_size);
+
+ return halModel;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraToHal(
+ PhraseRecognitionExtra *halExtra,
+ const struct sound_trigger_phrase_recognition_extra *extra)
+{
+ halExtra->id = extra->id;
+ halExtra->recognitionModes = extra->recognition_modes;
+ halExtra->confidenceLevel = extra->confidence_level;
+ ConfidenceLevel *halLevels =
+ new ConfidenceLevel[extra->num_levels];
+ for (unsigned int i = 0; i < extra->num_levels; i++) {
+ halLevels[i].userId = extra->levels[i].user_id;
+ halLevels[i].levelPercent = extra->levels[i].level;
+ }
+ halExtra->levels.setToExternal(halLevels, extra->num_levels);
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ halExtra->levels.resize(extra->num_levels);
+
+ delete[] halLevels;
+}
+
+
+ISoundTriggerHw::RecognitionConfig *SoundTriggerHalHidl::convertRecognitionConfigToHal(
+ const struct sound_trigger_recognition_config *config)
+{
+ ISoundTriggerHw::RecognitionConfig *halConfig =
+ new ISoundTriggerHw::RecognitionConfig();
+
+ halConfig->captureHandle = config->capture_handle;
+ halConfig->captureDevice = (AudioDevice)config->capture_device;
+ halConfig->captureRequested = (uint32_t)config->capture_requested;
+
+ PhraseRecognitionExtra *halExtras =
+ new PhraseRecognitionExtra[config->num_phrases];
+
+ for (unsigned int i = 0; i < config->num_phrases; i++) {
+ convertPhraseRecognitionExtraToHal(&halExtras[i],
+ &config->phrases[i]);
+ }
+ halConfig->phrases.setToExternal(halExtras, config->num_phrases);
+ // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+ halConfig->phrases.resize(config->num_phrases);
+
+ delete[] halExtras;
+
+ halConfig->data.setToExternal((uint8_t *)config + config->data_offset, config->data_size);
+
+ return halConfig;
+}
+
+
+// ISoundTriggerHwCallback
+::android::hardware::Return<void> SoundTriggerHalHidl::recognitionCallback(
+ const ISoundTriggerHwCallback::RecognitionEvent& halEvent,
+ CallbackCookie cookie)
+{
+ sp<SoundModel> model;
+ {
+ AutoMutex lock(mLock);
+ model = mSoundModels.valueFor((SoundModelHandle)cookie);
+ if (model == 0) {
+ return Return<void>();
+ }
+ }
+ struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(&halEvent);
+ if (event == NULL) {
+ return Return<void>();
+ }
+ event->model = model->mHandle;
+ model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+ free(event);
+
+ return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::phraseRecognitionCallback(
+ const ISoundTriggerHwCallback::PhraseRecognitionEvent& halEvent,
+ CallbackCookie cookie)
+{
+ sp<SoundModel> model;
+ {
+ AutoMutex lock(mLock);
+ model = mSoundModels.valueFor((SoundModelHandle)cookie);
+ if (model == 0) {
+ return Return<void>();
+ }
+ }
+
+ struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(
+ (const ISoundTriggerHwCallback::RecognitionEvent *)&halEvent);
+ if (event == NULL) {
+ return Return<void>();
+ }
+
+ event->model = model->mHandle;
+ model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+ free(event);
+
+ return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::soundModelCallback(
+ const ISoundTriggerHwCallback::ModelEvent& halEvent,
+ CallbackCookie cookie)
+{
+ sp<SoundModel> model;
+ {
+ AutoMutex lock(mLock);
+ model = mSoundModels.valueFor((SoundModelHandle)cookie);
+ if (model == 0) {
+ return Return<void>();
+ }
+ }
+
+ struct sound_trigger_model_event *event = convertSoundModelEventFromHal(&halEvent);
+ if (event == NULL) {
+ return Return<void>();
+ }
+
+ event->model = model->mHandle;
+ model->mSoundModelCallback(event, model->mSoundModelCookie);
+
+ free(event);
+
+ return Return<void>();
+}
+
+
+struct sound_trigger_model_event *SoundTriggerHalHidl::convertSoundModelEventFromHal(
+ const ISoundTriggerHwCallback::ModelEvent *halEvent)
+{
+ struct sound_trigger_model_event *event = (struct sound_trigger_model_event *)malloc(
+ sizeof(struct sound_trigger_model_event) +
+ halEvent->data.size());
+ if (event == NULL) {
+ return NULL;
+ }
+
+ event->status = (int)halEvent->status;
+ // event->model to be set by caller
+ event->data_offset = sizeof(struct sound_trigger_model_event);
+ event->data_size = halEvent->data.size();
+ uint8_t *dst = (uint8_t *)event + event->data_offset;
+ uint8_t *src = (uint8_t *)&halEvent->data[0];
+ memcpy(dst, src, halEvent->data.size());
+
+ return event;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraFromHal(
+ struct sound_trigger_phrase_recognition_extra *extra,
+ const PhraseRecognitionExtra *halExtra)
+{
+ extra->id = halExtra->id;
+ extra->recognition_modes = halExtra->recognitionModes;
+ extra->confidence_level = halExtra->confidenceLevel;
+
+ size_t i;
+ for (i = 0; i < halExtra->levels.size() && i < SOUND_TRIGGER_MAX_USERS; i++) {
+ extra->levels[i].user_id = halExtra->levels[i].userId;
+ extra->levels[i].level = halExtra->levels[i].levelPercent;
+ }
+ extra->num_levels = (unsigned int)i;
+}
+
+
+struct sound_trigger_recognition_event *SoundTriggerHalHidl::convertRecognitionEventFromHal(
+ const ISoundTriggerHwCallback::RecognitionEvent *halEvent)
+{
+ struct sound_trigger_recognition_event *event;
+
+ if (halEvent->type == SoundModelType::KEYPHRASE) {
+ struct sound_trigger_phrase_recognition_event *phraseEvent =
+ (struct sound_trigger_phrase_recognition_event *)malloc(
+ sizeof(struct sound_trigger_phrase_recognition_event) +
+ halEvent->data.size());
+ if (phraseEvent == NULL) {
+ return NULL;
+ }
+ const ISoundTriggerHwCallback::PhraseRecognitionEvent *halPhraseEvent =
+ (const ISoundTriggerHwCallback::PhraseRecognitionEvent *)halEvent;
+
+ for (unsigned int i = 0; i < halPhraseEvent->phraseExtras.size(); i++) {
+ convertPhraseRecognitionExtraFromHal(&phraseEvent->phrase_extras[i],
+ &halPhraseEvent->phraseExtras[i]);
+ }
+ phraseEvent->num_phrases = halPhraseEvent->phraseExtras.size();
+ event = (struct sound_trigger_recognition_event *)phraseEvent;
+ event->data_offset = sizeof(sound_trigger_phrase_recognition_event);
+ } else {
+ event = (struct sound_trigger_recognition_event *)malloc(
+ sizeof(struct sound_trigger_recognition_event) + halEvent->data.size());
+ if (event == NULL) {
+ return NULL;
+ }
+ event->data_offset = sizeof(sound_trigger_recognition_event);
+ }
+ event->status = (int)halEvent->status;
+ event->type = (sound_trigger_sound_model_type_t)halEvent->type;
+ // event->model to be set by caller
+ event->capture_available = (bool)halEvent->captureAvailable;
+ event->capture_session = halEvent->captureSession;
+ event->capture_delay_ms = halEvent->captureDelayMs;
+ event->capture_preamble_ms = halEvent->capturePreambleMs;
+ event->trigger_in_data = (bool)halEvent->triggerInData;
+ event->audio_config.sample_rate = halEvent->audioConfig.sampleRateHz;
+ event->audio_config.channel_mask = (audio_channel_mask_t)halEvent->audioConfig.channelMask;
+ event->audio_config.format = (audio_format_t)halEvent->audioConfig.format;
+
+ event->data_size = halEvent->data.size();
+ uint8_t *dst = (uint8_t *)event + event->data_offset;
+ uint8_t *src = (uint8_t *)&halEvent->data[0];
+ memcpy(dst, src, halEvent->data.size());
+
+ return event;
+}
+
+} // namespace android
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
new file mode 100644
index 0000000..60404dc
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalHidl.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include "SoundTriggerHalInterface.h"
+#include <android/hardware/soundtrigger/2.0/types.h>
+#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
+#include <android/hardware/soundtrigger/2.0/ISoundTriggerHwCallback.h>
+#include <android/hardware/soundtrigger/2.0/BnSoundTriggerHwCallback.h>
+
+namespace android {
+
+using android::hardware::audio::common::V2_0::Uuid;
+using android::hardware::soundtrigger::V2_0::ConfidenceLevel;
+using android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra;
+using android::hardware::soundtrigger::V2_0::SoundModelType;
+using android::hardware::soundtrigger::V2_0::SoundModelHandle;
+using android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
+using android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
+
+class SoundTriggerHalHidl : public SoundTriggerHalInterface,
+ public virtual ISoundTriggerHwCallback
+
+{
+public:
+ virtual int getProperties(struct sound_trigger_properties *properties);
+
+ /*
+ * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+ * Only one active recognition per model at a time. The SoundTrigger service will handle
+ * concurrent recognition requests by different users/applications on the same model.
+ * The implementation returns a unique handle used by other functions (unload_sound_model(),
+ * start_recognition(), etc...
+ */
+ virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+ sound_model_callback_t callback,
+ void *cookie,
+ sound_model_handle_t *handle);
+
+ /*
+ * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+ * implementation limitations.
+ */
+ virtual int unloadSoundModel(sound_model_handle_t handle);
+
+ /* Start recognition on a given model. Only one recognition active at a time per model.
+ * Once recognition succeeds of fails, the callback is called.
+ * TODO: group recognition configuration parameters into one struct and add key phrase options.
+ */
+ virtual int startRecognition(sound_model_handle_t handle,
+ const struct sound_trigger_recognition_config *config,
+ recognition_callback_t callback,
+ void *cookie);
+
+ /* Stop recognition on a given model.
+ * The implementation does not have to call the callback when stopped via this method.
+ */
+ virtual int stopRecognition(sound_model_handle_t handle);
+
+ /* Stop recognition on all models.
+ * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+ * If no implementation is provided, stop_recognition will be called for each running model.
+ */
+ virtual int stopAllRecognitions();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ // ISoundTriggerHwCallback
+ virtual ::android::hardware::Return<void> recognitionCallback(
+ const ISoundTriggerHwCallback::RecognitionEvent& event, CallbackCookie cookie);
+ virtual ::android::hardware::Return<void> phraseRecognitionCallback(
+ const ISoundTriggerHwCallback::PhraseRecognitionEvent& event, int32_t cookie);
+ virtual ::android::hardware::Return<void> soundModelCallback(
+ const ISoundTriggerHwCallback::ModelEvent& event, CallbackCookie cookie);
+private:
+ class SoundModel : public RefBase {
+ public:
+ SoundModel(sound_model_handle_t handle, sound_model_callback_t callback,
+ void *cookie, android::hardware::soundtrigger::V2_0::SoundModelHandle halHandle)
+ : mHandle(handle), mHalHandle(halHandle),
+ mSoundModelCallback(callback), mSoundModelCookie(cookie),
+ mRecognitionCallback(NULL), mRecognitionCookie(NULL) {}
+ ~SoundModel() {}
+
+ sound_model_handle_t mHandle;
+ android::hardware::soundtrigger::V2_0::SoundModelHandle mHalHandle;
+ sound_model_callback_t mSoundModelCallback;
+ void * mSoundModelCookie;
+ recognition_callback_t mRecognitionCallback;
+ void * mRecognitionCookie;
+ };
+
+ friend class SoundTriggerHalInterface;
+
+ explicit SoundTriggerHalHidl(const char *moduleName = NULL);
+ virtual ~SoundTriggerHalHidl();
+
+ void convertUuidToHal(Uuid *halUuid,
+ const sound_trigger_uuid_t *uuid);
+ void convertUuidFromHal(sound_trigger_uuid_t *uuid,
+ const Uuid *halUuid);
+
+ void convertPropertiesFromHal(
+ struct sound_trigger_properties *properties,
+ const ISoundTriggerHw::Properties *halProperties);
+
+ void convertTriggerPhraseToHal(
+ ISoundTriggerHw::Phrase *halTriggerPhrase,
+ const struct sound_trigger_phrase *triggerPhrase);
+ ISoundTriggerHw::SoundModel *convertSoundModelToHal(
+ const struct sound_trigger_sound_model *soundModel);
+
+ void convertPhraseRecognitionExtraToHal(
+ PhraseRecognitionExtra *halExtra,
+ const struct sound_trigger_phrase_recognition_extra *extra);
+ ISoundTriggerHw::RecognitionConfig *convertRecognitionConfigToHal(
+ const struct sound_trigger_recognition_config *config);
+
+ struct sound_trigger_model_event *convertSoundModelEventFromHal(
+ const ISoundTriggerHwCallback::ModelEvent *halEvent);
+ void convertPhraseRecognitionExtraFromHal(
+ struct sound_trigger_phrase_recognition_extra *extra,
+ const PhraseRecognitionExtra *halExtra);
+ struct sound_trigger_recognition_event *convertRecognitionEventFromHal(
+ const ISoundTriggerHwCallback::RecognitionEvent *halEvent);
+
+ uint32_t nextUniqueId();
+ sp<ISoundTriggerHw> getService();
+ void clearService();
+ sp<SoundModel> getModel(sound_model_handle_t handle);
+ sp<SoundModel> removeModel(sound_model_handle_t handle);
+
+ static pthread_once_t sOnceControl;
+ static void sOnceInit();
+
+ Mutex mLock;
+ Mutex mHalLock;
+ const char *mModuleName;
+ volatile atomic_uint_fast32_t mNextUniqueId;
+ // Effect chains without a valid thread
+ DefaultKeyedVector< sound_model_handle_t , sp<SoundModel> > mSoundModels;
+ sp<::android::hardware::soundtrigger::V2_0::ISoundTriggerHw> mISoundTrigger;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
diff --git a/services/soundtrigger/SoundTriggerHalInterface.h b/services/soundtrigger/SoundTriggerHalInterface.h
new file mode 100644
index 0000000..c083195
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalInterface.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/sound_trigger.h>
+#include <hardware/sound_trigger.h>
+
+namespace android {
+
+class SoundTriggerHalInterface : public virtual RefBase
+{
+public:
+ /* get a sound trigger HAL instance */
+ static sp<SoundTriggerHalInterface> connectModule(const char *moduleName);
+
+ virtual ~SoundTriggerHalInterface() {}
+
+ virtual int getProperties(struct sound_trigger_properties *properties) = 0;
+
+ /*
+ * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+ * Only one active recognition per model at a time. The SoundTrigger service will handle
+ * concurrent recognition requests by different users/applications on the same model.
+ * The implementation returns a unique handle used by other functions (unload_sound_model(),
+ * start_recognition(), etc...
+ */
+ virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+ sound_model_callback_t callback,
+ void *cookie,
+ sound_model_handle_t *handle) = 0;
+
+ /*
+ * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+ * implementation limitations.
+ */
+ virtual int unloadSoundModel(sound_model_handle_t handle) = 0;
+
+ /* Start recognition on a given model. Only one recognition active at a time per model.
+ * Once recognition succeeds of fails, the callback is called.
+ * TODO: group recognition configuration parameters into one struct and add key phrase options.
+ */
+ virtual int startRecognition(sound_model_handle_t handle,
+ const struct sound_trigger_recognition_config *config,
+ recognition_callback_t callback,
+ void *cookie) = 0;
+
+ /* Stop recognition on a given model.
+ * The implementation does not have to call the callback when stopped via this method.
+ */
+ virtual int stopRecognition(sound_model_handle_t handle) = 0;
+
+ /* Stop recognition on all models.
+ * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+ * If no implementation is provided, stop_recognition will be called for each running model.
+ */
+ virtual int stopAllRecognitions() = 0;
+
+protected:
+ SoundTriggerHalInterface() {}
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
diff --git a/services/soundtrigger/SoundTriggerHalLegacy.cpp b/services/soundtrigger/SoundTriggerHalLegacy.cpp
new file mode 100644
index 0000000..2b78818
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalLegacy.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <utils/Log.h>
+#include "SoundTriggerHalLegacy.h"
+
+namespace android {
+
+/* static */
+sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
+{
+ return new SoundTriggerHalLegacy(moduleName);
+}
+
+SoundTriggerHalLegacy::SoundTriggerHalLegacy(const char *moduleName)
+ : mModuleName(moduleName), mHwDevice(NULL)
+{
+}
+
+void SoundTriggerHalLegacy::onFirstRef()
+{
+ const hw_module_t *mod;
+ int rc;
+
+ if (mModuleName == NULL) {
+ mModuleName = "primary";
+ }
+
+ rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, &mod);
+ if (rc != 0) {
+ ALOGE("couldn't load sound trigger module %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+ return;
+ }
+ rc = sound_trigger_hw_device_open(mod, &mHwDevice);
+ if (rc != 0) {
+ ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+ mHwDevice = NULL;
+ return;
+ }
+ if (mHwDevice->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 ||
+ mHwDevice->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("wrong sound trigger hw device version %04x", mHwDevice->common.version);
+ return;
+ }
+}
+
+SoundTriggerHalLegacy::~SoundTriggerHalLegacy()
+{
+ if (mHwDevice != NULL) {
+ sound_trigger_hw_device_close(mHwDevice);
+ }
+}
+
+int SoundTriggerHalLegacy::getProperties(struct sound_trigger_properties *properties)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ return mHwDevice->get_properties(mHwDevice, properties);
+}
+
+int SoundTriggerHalLegacy::loadSoundModel(struct sound_trigger_sound_model *sound_model,
+ sound_model_callback_t callback,
+ void *cookie,
+ sound_model_handle_t *handle)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ return mHwDevice->load_sound_model(mHwDevice, sound_model, callback, cookie, handle);
+}
+
+int SoundTriggerHalLegacy::unloadSoundModel(sound_model_handle_t handle)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ return mHwDevice->unload_sound_model(mHwDevice, handle);
+}
+
+int SoundTriggerHalLegacy::startRecognition(sound_model_handle_t handle,
+ const struct sound_trigger_recognition_config *config,
+ recognition_callback_t callback,
+ void *cookie)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ return mHwDevice->start_recognition(mHwDevice, handle, config, callback, cookie);
+}
+
+int SoundTriggerHalLegacy::stopRecognition(sound_model_handle_t handle)
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ return mHwDevice->stop_recognition(mHwDevice, handle);
+}
+
+int SoundTriggerHalLegacy::stopAllRecognitions()
+{
+ if (mHwDevice == NULL) {
+ return -ENODEV;
+ }
+ if (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 &&
+ mHwDevice->stop_all_recognitions) {
+ return mHwDevice->stop_all_recognitions(mHwDevice);
+ }
+ return -ENOSYS;
+}
+
+} // namespace android
diff --git a/services/soundtrigger/SoundTriggerHalLegacy.h b/services/soundtrigger/SoundTriggerHalLegacy.h
new file mode 100644
index 0000000..52488de
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalLegacy.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
+
+#include "SoundTriggerHalInterface.h"
+
+namespace android {
+
+class SoundTriggerHalLegacy : public SoundTriggerHalInterface
+
+{
+public:
+ virtual ~SoundTriggerHalLegacy();
+
+ virtual int getProperties(struct sound_trigger_properties *properties);
+
+ /*
+ * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+ * Only one active recognition per model at a time. The SoundTrigger service will handle
+ * concurrent recognition requests by different users/applications on the same model.
+ * The implementation returns a unique handle used by other functions (unload_sound_model(),
+ * start_recognition(), etc...
+ */
+ virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+ sound_model_callback_t callback,
+ void *cookie,
+ sound_model_handle_t *handle);
+
+ /*
+ * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+ * implementation limitations.
+ */
+ virtual int unloadSoundModel(sound_model_handle_t handle);
+
+ /* Start recognition on a given model. Only one recognition active at a time per model.
+ * Once recognition succeeds of fails, the callback is called.
+ * TODO: group recognition configuration parameters into one struct and add key phrase options.
+ */
+ virtual int startRecognition(sound_model_handle_t handle,
+ const struct sound_trigger_recognition_config *config,
+ recognition_callback_t callback,
+ void *cookie);
+
+ /* Stop recognition on a given model.
+ * The implementation does not have to call the callback when stopped via this method.
+ */
+ virtual int stopRecognition(sound_model_handle_t handle);
+
+ /* Stop recognition on all models.
+ * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+ * If no implementation is provided, stop_recognition will be called for each running model.
+ */
+ int stopAllRecognitions();
+
+ // RefBase
+ virtual void onFirstRef();
+
+private:
+
+ friend class SoundTriggerHalInterface;
+
+ explicit SoundTriggerHalLegacy(const char *moduleName = NULL);
+
+ const char *mModuleName;
+ struct sound_trigger_hw_device* mHwDevice;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 6a52b9c..3ba7f62 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -32,17 +32,16 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
-#include <hardware/sound_trigger.h>
+#include <system/sound_trigger.h>
#include <ServiceUtilities.h>
#include "SoundTriggerHwService.h"
-namespace android {
-
#ifdef SOUND_TRIGGER_USE_STUB_MODULE
#define HW_MODULE_PREFIX "stub"
#else
#define HW_MODULE_PREFIX "primary"
#endif
+namespace android {
SoundTriggerHwService::SoundTriggerHwService()
: BnSoundTriggerHwService(),
@@ -54,30 +53,17 @@
void SoundTriggerHwService::onFirstRef()
{
- const hw_module_t *mod;
int rc;
- sound_trigger_hw_device *dev;
- rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod);
- if (rc != 0) {
- ALOGE("couldn't load sound trigger module %s.%s (%s)",
- SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, strerror(-rc));
- return;
- }
- rc = sound_trigger_hw_device_open(mod, &dev);
- if (rc != 0) {
- ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
- SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, strerror(-rc));
- return;
- }
- if (dev->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 ||
- dev->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
- ALOGE("wrong sound trigger hw device version %04x", dev->common.version);
- return;
- }
+ sp<SoundTriggerHalInterface> halInterface =
+ SoundTriggerHalInterface::connectModule(HW_MODULE_PREFIX);
+ if (halInterface == 0) {
+ ALOGW("could not connect to HAL");
+ return;
+ }
sound_trigger_module_descriptor descriptor;
- rc = dev->get_properties(dev, &descriptor.properties);
+ rc = halInterface->getProperties(&descriptor.properties);
if (rc != 0) {
ALOGE("could not read implementation properties");
return;
@@ -88,7 +74,7 @@
descriptor.handle);
sp<ISoundTriggerClient> client;
- sp<Module> module = new Module(this, dev, descriptor, client);
+ sp<Module> module = new Module(this, halInterface, descriptor, client);
mModules.add(descriptor.handle, module);
mCallbackThread = new CallbackThread(this);
}
@@ -98,9 +84,6 @@
if (mCallbackThread != 0) {
mCallbackThread->exit();
}
- for (size_t i = 0; i < mModules.size(); i++) {
- sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice());
- }
}
status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules,
@@ -489,10 +472,10 @@
#define LOG_TAG "SoundTriggerHwService::Module"
SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
- sound_trigger_hw_device* hwDevice,
+ const sp<SoundTriggerHalInterface>& halInterface,
sound_trigger_module_descriptor descriptor,
const sp<ISoundTriggerClient>& client)
- : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor),
+ : mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
{
}
@@ -510,10 +493,12 @@
for (size_t i = 0; i < mModels.size(); i++) {
sp<Model> model = mModels.valueAt(i);
ALOGV("detach() unloading model %d", model->mHandle);
- if (model->mState == Model::STATE_ACTIVE) {
- mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+ if (mHalInterface != 0) {
+ if (model->mState == Model::STATE_ACTIVE) {
+ mHalInterface->stopRecognition(model->mHandle);
+ }
+ mHalInterface->unloadSoundModel(model->mHandle);
}
- mHwDevice->unload_sound_model(mHwDevice, model->mHandle);
}
mModels.clear();
}
@@ -531,10 +516,12 @@
sound_model_handle_t *handle)
{
ALOGV("loadSoundModel() handle");
+ if (mHalInterface == 0) {
+ return NO_INIT;
+ }
if (!captureHotwordAllowed()) {
return PERMISSION_DENIED;
}
-
if (modelMemory == 0 || modelMemory->pointer() == NULL) {
ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
return BAD_VALUE;
@@ -566,7 +553,7 @@
return INVALID_OPERATION;
}
- status_t status = mHwDevice->load_sound_model(mHwDevice, sound_model,
+ status_t status = mHalInterface->loadSoundModel(sound_model,
SoundTriggerHwService::soundModelCallback,
this, handle);
@@ -601,6 +588,9 @@
status_t SoundTriggerHwService::Module::unloadSoundModel_l(sound_model_handle_t handle)
{
+ if (mHalInterface == 0) {
+ return NO_INIT;
+ }
ssize_t index = mModels.indexOfKey(handle);
if (index < 0) {
return BAD_VALUE;
@@ -608,17 +598,20 @@
sp<Model> model = mModels.valueAt(index);
mModels.removeItem(handle);
if (model->mState == Model::STATE_ACTIVE) {
- mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+ mHalInterface->stopRecognition(model->mHandle);
model->mState = Model::STATE_IDLE;
}
AudioSystem::releaseSoundTriggerSession(model->mCaptureSession);
- return mHwDevice->unload_sound_model(mHwDevice, handle);
+ return mHalInterface->unloadSoundModel(handle);
}
status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle,
const sp<IMemory>& dataMemory)
{
ALOGV("startRecognition() model handle %d", handle);
+ if (mHalInterface == 0) {
+ return NO_INIT;
+ }
if (!captureHotwordAllowed()) {
return PERMISSION_DENIED;
}
@@ -657,7 +650,7 @@
//TODO: get capture handle and device from audio policy service
config->capture_handle = model->mCaptureIOHandle;
config->capture_device = model->mCaptureDevice;
- status_t status = mHwDevice->start_recognition(mHwDevice, handle, config,
+ status_t status = mHalInterface->startRecognition(handle, config,
SoundTriggerHwService::recognitionCallback,
this);
@@ -672,6 +665,9 @@
status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle)
{
ALOGV("stopRecognition() model handle %d", handle);
+ if (mHalInterface == 0) {
+ return NO_INIT;
+ }
if (!captureHotwordAllowed()) {
return PERMISSION_DENIED;
}
@@ -685,7 +681,7 @@
if (model->mState != Model::STATE_ACTIVE) {
return INVALID_OPERATION;
}
- mHwDevice->stop_recognition(mHwDevice, handle);
+ mHalInterface->stopRecognition(handle);
model->mState = Model::STATE_IDLE;
return NO_ERROR;
}
@@ -808,18 +804,13 @@
}
const bool supports_stop_all =
- (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 &&
- mHwDevice->stop_all_recognitions);
-
- if (supports_stop_all) {
- mHwDevice->stop_all_recognitions(mHwDevice);
- }
+ (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
for (size_t i = 0; i < mModels.size(); i++) {
sp<Model> model = mModels.valueAt(i);
if (model->mState == Model::STATE_ACTIVE) {
- if (!supports_stop_all) {
- mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+ if (mHalInterface != 0 && !supports_stop_all) {
+ mHalInterface->stopRecognition(model->mHandle);
}
// keep model in ACTIVE state so that event is processed by onCallbackEvent()
if (model->mType == SOUND_MODEL_TYPE_KEYPHRASE) {
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index 13a577a..7f7d0cc 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -26,7 +26,7 @@
#include <soundtrigger/ISoundTrigger.h>
#include <soundtrigger/ISoundTriggerClient.h>
#include <system/sound_trigger.h>
-#include <hardware/sound_trigger.h>
+#include "SoundTriggerHalInterface.h"
namespace android {
@@ -103,7 +103,7 @@
public:
Module(const sp<SoundTriggerHwService>& service,
- sound_trigger_hw_device* hwDevice,
+ const sp<SoundTriggerHalInterface>& halInterface,
sound_trigger_module_descriptor descriptor,
const sp<ISoundTriggerClient>& client);
@@ -123,7 +123,6 @@
virtual status_t dump(int fd, const Vector<String16>& args);
- sound_trigger_hw_device *hwDevice() const { return mHwDevice; }
struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
void setClient(const sp<ISoundTriggerClient>& client) { mClient = client; }
void clearClient() { mClient.clear(); }
@@ -146,7 +145,7 @@
Mutex mLock;
wp<SoundTriggerHwService> mService;
- struct sound_trigger_hw_device* mHwDevice;
+ sp<SoundTriggerHalInterface> mHalInterface;
struct sound_trigger_module_descriptor mDescriptor;
sp<ISoundTriggerClient> mClient;
DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels;
diff --git a/soundtrigger/Android.mk b/soundtrigger/Android.mk
index c794cc1..e29adbf 100644
--- a/soundtrigger/Android.mk
+++ b/soundtrigger/Android.mk
@@ -27,7 +27,6 @@
libutils \
liblog \
libbinder \
- libhardware
#LOCAL_C_INCLUDES += \
system/media/camera/include \