Merge "Use tokens for IOmxNode."
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index 1548e64..f0a0db0 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -25,6 +25,7 @@
libutils \
libui \
libbinder \
+ libhidltransport \
android.hardware.camera.common@1.0 \
android.hardware.camera.provider@2.4 \
android.hardware.camera.device@1.0 \
diff --git a/camera/cameraserver/main_cameraserver.cpp b/camera/cameraserver/main_cameraserver.cpp
index f4be468..3972436 100644
--- a/camera/cameraserver/main_cameraserver.cpp
+++ b/camera/cameraserver/main_cameraserver.cpp
@@ -17,8 +17,8 @@
#define LOG_TAG "cameraserver"
//#define LOG_NDEBUG 0
-// from LOCAL_C_INCLUDES
#include "CameraService.h"
+#include <hidl/HidlTransportSupport.h>
using namespace android;
@@ -26,6 +26,9 @@
{
signal(SIGPIPE, SIG_IGN);
+ // Set 3 threads for HIDL calls
+ hardware::configureRpcThreadpool(3, /*willjoin*/ false);
+
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
diff --git a/drm/libmediadrm/Android.mk b/drm/libmediadrm/Android.mk
index 7176582..a57fafa 100644
--- a/drm/libmediadrm/Android.mk
+++ b/drm/libmediadrm/Android.mk
@@ -7,13 +7,13 @@
include $(CLEAR_VARS)
LOCAL_AIDL_INCLUDES := \
- frameworks/base/media/java
+ frameworks/av/drm/libmediadrm/aidl
LOCAL_SRC_FILES := \
- ../../../base/media/java/android/media/ICas.aidl \
- ../../../base/media/java/android/media/ICasListener.aidl \
- ../../../base/media/java/android/media/IDescrambler.aidl \
- ../../../base/media/java/android/media/IMediaCasService.aidl \
+ aidl/android/media/ICas.aidl \
+ aidl/android/media/ICasListener.aidl \
+ aidl/android/media/IDescrambler.aidl \
+ aidl/android/media/IMediaCasService.aidl \
LOCAL_SRC_FILES += \
CasImpl.cpp \
diff --git a/drm/libmediadrm/aidl/android/media/ICas.aidl b/drm/libmediadrm/aidl/android/media/ICas.aidl
new file mode 100644
index 0000000..6b2ce4a
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/ICas.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.MediaCas;
+
+/** @hide */
+interface ICas {
+ void setPrivateData(in byte[] pvtData);
+ byte[] openSession(int program_number);
+ byte[] openSessionForStream(int program_number, int elementary_PID);
+ void closeSession(in byte[] sessionId);
+ void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData);
+ void processEcm(in byte[] sessionId, in MediaCas.ParcelableCasData ecm);
+ void processEmm(in MediaCas.ParcelableCasData emm);
+ void sendEvent(int event, int arg, in @nullable byte[] eventData);
+ void provision(String provisionString);
+ void refreshEntitlements(int refreshType, in @nullable byte[] refreshData);
+ void release();
+}
\ No newline at end of file
diff --git a/drm/libmediadrm/aidl/android/media/ICasListener.aidl b/drm/libmediadrm/aidl/android/media/ICasListener.aidl
new file mode 100644
index 0000000..01a5abc
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/ICasListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/** @hide */
+interface ICasListener {
+ void onEvent(int event, int arg, in @nullable byte[] data);
+}
\ No newline at end of file
diff --git a/drm/libmediadrm/aidl/android/media/IDescrambler.aidl b/drm/libmediadrm/aidl/android/media/IDescrambler.aidl
new file mode 100644
index 0000000..fdf99eb
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/IDescrambler.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.MediaDescrambler;
+
+/** @hide */
+interface IDescrambler {
+ void setMediaCasSession(in byte[] sessionId);
+ boolean requiresSecureDecoderComponent(String mime);
+ int descramble(in MediaDescrambler.DescrambleInfo descrambleInfo);
+ void release();
+}
\ No newline at end of file
diff --git a/drm/libmediadrm/aidl/android/media/IMediaCasService.aidl b/drm/libmediadrm/aidl/android/media/IMediaCasService.aidl
new file mode 100644
index 0000000..44f6825
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/IMediaCasService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.IDescrambler;
+import android.media.ICas;
+import android.media.ICasListener;
+import android.media.MediaCas;
+
+/** @hide */
+interface IMediaCasService {
+ MediaCas.ParcelableCasPluginDescriptor[] enumeratePlugins();
+ boolean isSystemIdSupported(int CA_system_id);
+ ICas createPlugin(int CA_system_id, ICasListener listener);
+ boolean isDescramblerSupported(int CA_system_id);
+ IDescrambler createDescrambler(int CA_system_id);
+}
+
diff --git a/drm/libmediadrm/aidl/android/media/MediaCas.aidl b/drm/libmediadrm/aidl/android/media/MediaCas.aidl
new file mode 100644
index 0000000..cb8d0c6
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/MediaCas.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/** @hide */
+parcelable MediaCas.ParcelableCasPluginDescriptor cpp_header "media/MediaCasDefs.h";
+
+/** @hide */
+parcelable MediaCas.ParcelableCasData cpp_header "media/MediaCasDefs.h";
\ No newline at end of file
diff --git a/drm/libmediadrm/aidl/android/media/MediaDescrambler.aidl b/drm/libmediadrm/aidl/android/media/MediaDescrambler.aidl
new file mode 100644
index 0000000..e789244
--- /dev/null
+++ b/drm/libmediadrm/aidl/android/media/MediaDescrambler.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/** @hide */
+parcelable MediaDescrambler.DescrambleInfo cpp_header "media/MediaCasDefs.h";
\ No newline at end of file
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/StreamHalHidl.cpp
index 2a7a67f..77ba716 100644
--- a/media/libaudiohal/StreamHalHidl.cpp
+++ b/media/libaudiohal/StreamHalHidl.cpp
@@ -315,6 +315,10 @@
WriteCommand::WRITE, "write", static_cast<const uint8_t*>(buffer), bytes,
[&] (const WriteStatus& writeStatus) {
*written = writeStatus.reply.written;
+ // Diagnostics of the cause of b/35813113.
+ ALOGE_IF(*written > bytes,
+ "hal reports more bytes written than asked for: %lld > %lld",
+ (long long)*written, (long long)bytes);
});
}
@@ -328,8 +332,8 @@
if (data != nullptr) {
size_t availableToWrite = mDataMQ->availableToWrite();
if (dataSize > availableToWrite) {
- ALOGW("truncating write data from %d to %d due to insufficient data queue space",
- (int32_t)dataSize, (int32_t)availableToWrite);
+ ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",
+ (long long)dataSize, (long long)availableToWrite);
dataSize = availableToWrite;
}
if (!mDataMQ->write(data, dataSize)) {
diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp
index 7df3b65..31c85af 100644
--- a/media/libmedia/IDataSource.cpp
+++ b/media/libmedia/IDataSource.cpp
@@ -55,8 +55,16 @@
data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
data.writeInt64(offset);
data.writeInt64(size);
- remote()->transact(READ_AT, data, &reply);
- return reply.readInt64();
+ status_t err = remote()->transact(READ_AT, data, &reply);
+ if (err != OK) {
+ return err;
+ }
+ int64_t value = 0;
+ err = reply.readInt64(&value);
+ if (err != OK) {
+ return err;
+ }
+ return (ssize_t)value;
}
virtual status_t getSize(off64_t* size) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index abea5bc..3b2a8a1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -206,9 +206,11 @@
status_t NuPlayerDriver::getDefaultBufferingSettings(BufferingSettings* buffering) {
ALOGV("getDefaultBufferingSettings(%p)", this);
- Mutex::Autolock autoLock(mLock);
- if (mState == STATE_IDLE) {
- return INVALID_OPERATION;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mState == STATE_IDLE) {
+ return INVALID_OPERATION;
+ }
}
return mPlayer->getDefaultBufferingSettings(buffering);
@@ -216,9 +218,11 @@
status_t NuPlayerDriver::setBufferingSettings(const BufferingSettings& buffering) {
ALOGV("setBufferingSettings(%p)", this);
- Mutex::Autolock autoLock(mLock);
- if (mState == STATE_IDLE) {
- return INVALID_OPERATION;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mState == STATE_IDLE) {
+ return INVALID_OPERATION;
+ }
}
return mPlayer->setBufferingSettings(buffering);
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 296394b..259e134 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -20,6 +20,7 @@
#include <numeric>
+#include <android/media/IDescrambler.h>
#include <binder/MemoryDealer.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -33,7 +34,8 @@
#include "include/SharedMemoryBuffer.h"
namespace android {
-
+using binder::Status;
+using MediaDescrambler::DescrambleInfo;
using BufferInfo = ACodecBufferChannel::BufferInfo;
using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
@@ -92,7 +94,7 @@
const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
AString *errorDetailMsg) {
- if (mCrypto == nullptr) {
+ if (!hasCryptoOrDescrambler()) {
return -ENOSYS;
}
std::shared_ptr<const std::vector<const BufferInfo>> array(
@@ -116,9 +118,47 @@
destination.mType = ICrypto::kDestinationTypeSharedMemory;
destination.mSharedMemory = mDecryptDestination;
}
- ssize_t result = mCrypto->decrypt(key, iv, mode, pattern,
- it->mSharedEncryptedBuffer, it->mClientBuffer->offset(),
- subSamples, numSubSamples, destination, errorDetailMsg);
+
+ ssize_t result = -1;
+ if (mCrypto != NULL) {
+ result = mCrypto->decrypt(key, iv, mode, pattern,
+ it->mSharedEncryptedBuffer, it->mClientBuffer->offset(),
+ subSamples, numSubSamples, destination, errorDetailMsg);
+ } else {
+ DescrambleInfo descrambleInfo;
+ descrambleInfo.dstType = destination.mType ==
+ ICrypto::kDestinationTypeSharedMemory ?
+ DescrambleInfo::kDestinationTypeVmPointer :
+ DescrambleInfo::kDestinationTypeNativeHandle;
+ descrambleInfo.scramblingControl = key != NULL ?
+ (DescramblerPlugin::ScramblingControl)key[0] :
+ DescramblerPlugin::kScrambling_Unscrambled;
+ descrambleInfo.numSubSamples = numSubSamples;
+ descrambleInfo.subSamples = (DescramblerPlugin::SubSample *)subSamples;
+ descrambleInfo.srcMem = it->mSharedEncryptedBuffer;
+ descrambleInfo.srcOffset = 0;
+ descrambleInfo.dstPtr = NULL;
+ descrambleInfo.dstOffset = 0;
+
+ int32_t descrambleResult = -1;
+ Status status = mDescrambler->descramble(descrambleInfo, &descrambleResult);
+
+ if (status.isOk()) {
+ result = descrambleResult;
+ }
+
+ if (result < 0) {
+ ALOGE("descramble failed, exceptionCode=%d, err=%d, result=%zd",
+ status.exceptionCode(), status.transactionError(), result);
+ } else {
+ ALOGV("descramble succeeded, result=%zd", result);
+ }
+
+ if (result > 0 && destination.mType == ICrypto::kDestinationTypeSharedMemory) {
+ memcpy(destination.mSharedMemory->pointer(),
+ (uint8_t*)it->mSharedEncryptedBuffer->pointer(), result);
+ }
+ }
if (result < 0) {
return result;
@@ -212,8 +252,7 @@
}
void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
- bool secure = (mCrypto != nullptr);
- if (secure) {
+ if (hasCryptoOrDescrambler()) {
size_t totalSize = std::accumulate(
array.begin(), array.end(), 0u,
[alignment = MemoryDealer::getAllocationAlignment()]
@@ -232,7 +271,7 @@
std::vector<const BufferInfo> inputBuffers;
for (const BufferAndId &elem : array) {
sp<IMemory> sharedEncryptedBuffer;
- if (secure) {
+ if (hasCryptoOrDescrambler()) {
sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
}
inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 7ce6102..18cfc0e 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -130,7 +130,8 @@
LOCAL_CFLAGS += -DENABLE_STAGEFRIGHT_EXPERIMENTS
endif
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_MODULE:= libstagefright
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 70916dc..a017737 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -72,6 +72,7 @@
Vector<SidxEntry> &sidx,
const Trex *trex,
off64_t firstMoofOffset);
+ virtual status_t init();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -2175,7 +2176,10 @@
*offset += chunk_size;
if (underQTMetaPath(mPath, 3)) {
- parseQTMetaKey(data_offset, chunk_data_size);
+ status_t err = parseQTMetaKey(data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
}
break;
}
@@ -2334,7 +2338,10 @@
case FOURCC('s', 'i', 'd', 'x'):
{
- parseSegmentIndex(data_offset, chunk_data_size);
+ status_t err = parseSegmentIndex(data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
*offset += chunk_size;
return UNKNOWN_ERROR; // stop parsing after sidx
}
@@ -2382,7 +2389,10 @@
// check if we're parsing 'ilst' for meta keys
// if so, treat type as a number (key-id).
if (underQTMetaPath(mPath, 3)) {
- parseQTMetaVal(chunk_type, data_offset, chunk_data_size);
+ status_t err = parseQTMetaVal(chunk_type, data_offset, chunk_data_size);
+ if (err != OK) {
+ return err;
+ }
}
*offset += chunk_size;
@@ -3302,9 +3312,13 @@
}
}
- return new MPEG4Source(this,
+ sp<MPEG4Source> source = new MPEG4Source(this,
track->meta, mDataSource, track->timescale, track->sampleTable,
mSidxEntries, trex, mMoofOffset);
+ if (source->init() != OK) {
+ return NULL;
+ }
+ return source;
}
// static
@@ -3701,6 +3715,7 @@
mTrex(trex),
mFirstMoofOffset(firstMoofOffset),
mCurrentMoofOffset(firstMoofOffset),
+ mNextMoofOffset(-1),
mCurrentTime(0),
mCurrentSampleInfoAllocSize(0),
mCurrentSampleInfoSizes(NULL),
@@ -3765,10 +3780,14 @@
CHECK(format->findInt32(kKeyTrackID, &mTrackId));
+}
+
+status_t MPEG4Source::init() {
if (mFirstMoofOffset != 0) {
off64_t offset = mFirstMoofOffset;
- parseChunk(&offset);
+ return parseChunk(&offset);
}
+ return OK;
}
MPEG4Source::~MPEG4Source() {
@@ -3899,9 +3918,30 @@
}
chunk_size = ntohl(hdr[0]);
chunk_type = ntohl(hdr[1]);
+ if (chunk_size == 1) {
+ // ISO/IEC 14496-12:2012, 8.8.4 Movie Fragment Box, moof is a Box
+ // which is defined in 4.2 Object Structure.
+ // When chunk_size==1, 8 bytes follows as "largesize".
+ if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ if (chunk_size < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
+ return ERROR_MALFORMED;
+ }
+ } else if (chunk_size == 0) {
+ // next box extends to end of file.
+ } else if (chunk_size < 8) {
+ // The smallest valid chunk is 8 bytes long in this case.
+ return ERROR_MALFORMED;
+ }
+
if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
mNextMoofOffset = *offset;
break;
+ } else if (chunk_size == 0) {
+ break;
}
*offset += chunk_size;
}
@@ -4785,17 +4825,25 @@
totalOffset += se->mSize;
}
mCurrentMoofOffset = totalOffset;
+ mNextMoofOffset = -1;
mCurrentSamples.clear();
mCurrentSampleIndex = 0;
- parseChunk(&totalOffset);
+ status_t err = parseChunk(&totalOffset);
+ if (err != OK) {
+ return err;
+ }
mCurrentTime = totalTime * mTimescale / 1000000ll;
} else {
// without sidx boxes, we can only seek to 0
mCurrentMoofOffset = mFirstMoofOffset;
+ mNextMoofOffset = -1;
mCurrentSamples.clear();
mCurrentSampleIndex = 0;
off64_t tmp = mCurrentMoofOffset;
- parseChunk(&tmp);
+ status_t err = parseChunk(&tmp);
+ if (err != OK) {
+ return err;
+ }
mCurrentTime = 0;
}
@@ -4824,7 +4872,10 @@
mCurrentMoofOffset = nextMoof;
mCurrentSamples.clear();
mCurrentSampleIndex = 0;
- parseChunk(&nextMoof);
+ status_t err = parseChunk(&nextMoof);
+ if (err != OK) {
+ return err;
+ }
if (mCurrentSampleIndex >= mCurrentSamples.size()) {
return ERROR_END_OF_STREAM;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6674e2c..8728b6f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2012,6 +2012,7 @@
}
mDescrambler = static_cast<IDescrambler *>(descrambler);
+ mBufferChannel->setDescrambler(mDescrambler);
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -2033,7 +2034,6 @@
CHECK(msg->senderAwaitsResponse(&replyID));
status_t err = OK;
- sp<Surface> surface;
switch (mState) {
case CONFIGURED:
@@ -2718,7 +2718,7 @@
CryptoPlugin::Pattern pattern;
if (msg->findSize("size", &size)) {
- if (mCrypto != NULL) {
+ if (hasCryptoOrDescrambler()) {
ss.mNumBytesOfClearData = size;
ss.mNumBytesOfEncryptedData = 0;
@@ -2730,7 +2730,9 @@
pattern.mSkipBlocks = 0;
}
} else {
- if (mCrypto == NULL) {
+ if (!hasCryptoOrDescrambler()) {
+ ALOGE("[%s] queuing secure buffer without mCrypto or mDescrambler!",
+ mComponentName.c_str());
return -EINVAL;
}
@@ -2779,7 +2781,7 @@
sp<MediaCodecBuffer> buffer = info->mData;
status_t err = OK;
- if (mCrypto != NULL) {
+ if (hasCryptoOrDescrambler()) {
AString *errorDetailMsg;
CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index ec02fb9..e8c46e3 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -610,6 +610,22 @@
sp<AMessage> msg = new AMessage;
msg->setString("mime", mime);
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyCas, &type, &data, &size)) {
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ msg->setBuffer("cas", buffer);
+ memcpy(buffer->data(), data, size);
+ }
+
+ if (!strncasecmp("video/scrambled", mime, 15)
+ || !strncasecmp("audio/scrambled", mime, 15)) {
+
+ *format = msg;
+ return OK;
+ }
+
int64_t durationUs;
if (meta->findInt64(kKeyDuration, &durationUs)) {
msg->setInt64("durationUs", durationUs);
@@ -759,9 +775,6 @@
msg->setInt32("frame-rate", fps);
}
- uint32_t type;
- const void *data;
- size_t size;
if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
diff --git a/media/libstagefright/codecs/aacenc/src/dyn_bits.c b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
index 4d763d0..e75b48f 100644
--- a/media/libstagefright/codecs/aacenc/src/dyn_bits.c
+++ b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
@@ -20,6 +20,10 @@
*******************************************************************************/
+#define LOG_TAG "NoiselessCoder"
+
+#include "log/log.h"
+
#include "aac_rom.h"
#include "dyn_bits.h"
#include "bit_cnt.h"
@@ -296,6 +300,9 @@
case SHORT_WINDOW:
sideInfoTab = sideInfoTabShort;
break;
+ default:
+ ALOGE("invalid blockType: %d", blockType);
+ return;
}
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 1e82727..1e7a4cc 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -17,7 +17,8 @@
libyuv_static \
LOCAL_CFLAGS += -Werror
-LOCAL_SANITIZE := signed-integer-overflow
+LOCAL_SANITIZE := signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_MODULE:= libstagefright_color_conversion
diff --git a/media/libstagefright/filters/Android.mk b/media/libstagefright/filters/Android.mk
index 07b2eb8..d4ecfcc 100644
--- a/media/libstagefright/filters/Android.mk
+++ b/media/libstagefright/filters/Android.mk
@@ -29,4 +29,7 @@
LOCAL_MODULE:= libstagefright_mediafilter
+LOCAL_SANITIZE := cfi
+LOCAL_SANITIZE_DIAG := cfi
+
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 19ada73..827703e 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -5,7 +5,8 @@
ID3.cpp
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_SHARED_LIBRARIES := libmedia
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index ce9bd3c..02468c1 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -126,6 +126,10 @@
// the caller has given up the reference, so that access is also safe.
std::shared_ptr<const std::vector<const BufferInfo>> mInputBuffers;
std::shared_ptr<const std::vector<const BufferInfo>> mOutputBuffers;
+
+ bool hasCryptoOrDescrambler() {
+ return mCrypto != NULL || mDescrambler != NULL;
+ }
};
} // namespace android
diff --git a/media/libstagefright/include/CodecBase.h b/media/libstagefright/include/CodecBase.h
index cfbaea4..845146d 100644
--- a/media/libstagefright/include/CodecBase.h
+++ b/media/libstagefright/include/CodecBase.h
@@ -35,9 +35,10 @@
#include <utils/NativeHandle.h>
#include <system/graphics.h>
+#include <android/media/IDescrambler.h>
namespace android {
-
+using namespace media;
class BufferChannelBase;
class BufferProducerWrapper;
class MediaCodecBuffer;
@@ -259,6 +260,10 @@
mCrypto = crypto;
}
+ inline void setDescrambler(const sp<IDescrambler> &descrambler) {
+ mDescrambler = descrambler;
+ }
+
/**
* Queue an input buffer into the buffer channel.
*
@@ -317,6 +322,7 @@
protected:
std::unique_ptr<CodecBase::BufferCallback> mCallback;
sp<ICrypto> mCrypto;
+ sp<IDescrambler> mDescrambler;
};
} // namespace android
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index ef55620..2a75298f 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -45,6 +45,8 @@
virtual sp<MetaData> getMetaData();
+ virtual status_t setMediaCas(const sp<ICas> &cas) override;
+
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG2TSExtractor"; }
@@ -70,7 +72,10 @@
off64_t mOffset;
+ static bool isScrambledFormat(const sp<MetaData> &format);
+
void init();
+ void addSource(const sp<AnotherPacketSource> &impl);
// Try to feed more data from source to parser.
// |isInit| means this function is called inside init(). This is a signal to
// save SyncEvent so that init() can add SyncPoint after it updates |mSourceImpls|.
diff --git a/media/libstagefright/include/MediaCodec.h b/media/libstagefright/include/MediaCodec.h
index fd3ff26..30454dc 100644
--- a/media/libstagefright/include/MediaCodec.h
+++ b/media/libstagefright/include/MediaCodec.h
@@ -401,6 +401,10 @@
status_t connectToSurface(const sp<Surface> &surface);
status_t disconnectFromSurface();
+ bool hasCryptoOrDescrambler() {
+ return mCrypto != NULL || mDescrambler != NULL;
+ }
+
void postActivityNotificationIfPossible();
void onInputBufferAvailable();
diff --git a/media/libstagefright/include/MetaData.h b/media/libstagefright/include/MetaData.h
index 6ba7b32..214f4ff 100644
--- a/media/libstagefright/include/MetaData.h
+++ b/media/libstagefright/include/MetaData.h
@@ -176,6 +176,7 @@
kKeyCryptoDefaultIVSize = 'cryS', // int32_t
kKeyPssh = 'pssh', // raw data
+ kKeyCas = ' cas',
// Please see MediaFormat.KEY_IS_AUTOSELECT.
kKeyTrackIsAutoselect = 'auto', // bool (int32_t)
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 7dd0863..7de5dbe 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -10,7 +10,8 @@
$(TOP)/frameworks/av/media/libstagefright/include \
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_SHARED_LIBRARIES := libmedia
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 4975d9a..47caf61 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -17,13 +17,14 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ATSParser"
#include <utils/Log.h>
-
#include "ATSParser.h"
-
#include "AnotherPacketSource.h"
+#include "CasManager.h"
#include "ESQueue.h"
#include "include/avc_utils.h"
+#include <android/media/IDescrambler.h>
+#include <binder/MemoryDealer.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -40,6 +41,8 @@
#include <inttypes.h>
namespace android {
+using binder::Status;
+using MediaDescrambler::DescrambleInfo;
// I want the expression "y" evaluated even if verbose logging is off.
#define MY_LOGV(x, y) \
@@ -60,6 +63,8 @@
bool parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br, status_t *err, SyncEvent *event);
void signalDiscontinuity(
@@ -90,14 +95,21 @@
return mParser->mFlags;
}
+ sp<CasManager> casManager() const {
+ return mParser->mCasManager;
+ }
+
uint64_t firstPTS() const {
return mFirstPTS;
}
+ void updateCasSessions();
+
private:
struct StreamInfo {
unsigned mType;
unsigned mPID;
+ int32_t mCASystemId;
};
ATSParser *mParser;
@@ -110,6 +122,8 @@
status_t parseProgramMap(ABitReader *br);
int64_t recoverPTS(uint64_t PTS_33bit);
+ bool findCADescriptor(
+ ABitReader *br, unsigned infoLength, CADescriptor *caDescriptor);
bool switchPIDs(const Vector<StreamInfo> &infos);
DISALLOW_EVIL_CONSTRUCTORS(Program);
@@ -119,18 +133,25 @@
Stream(Program *program,
unsigned elementaryPID,
unsigned streamType,
- unsigned PCR_PID);
+ unsigned PCR_PID,
+ int32_t CA_system_ID);
unsigned type() const { return mStreamType; }
unsigned pid() const { return mElementaryPID; }
void setPID(unsigned pid) { mElementaryPID = pid; }
+ void setCasSession(
+ const sp<IDescrambler> &descrambler,
+ const std::vector<uint8_t> &sessionId);
+
// Parse the payload and set event when PES with a sync frame is detected.
// This method knows when a PES starts; so record mPesStartOffsets in that
// case.
status_t parse(
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br,
SyncEvent *event);
@@ -150,6 +171,11 @@
virtual ~Stream();
private:
+ struct SubSampleInfo {
+ size_t subSampleSize;
+ unsigned transport_scrambling_mode;
+ unsigned random_access_indicator;
+ };
Program *mProgram;
unsigned mElementaryPID;
unsigned mStreamType;
@@ -166,10 +192,26 @@
ElementaryStreamQueue *mQueue;
+ bool mScrambled;
+ sp<IMemory> mMem;
+ sp<MemoryDealer> mDealer;
+ sp<ABuffer> mDescrambledBuffer;
+ List<SubSampleInfo> mSubSamples;
+ sp<IDescrambler> mDescrambler;
+
// Flush accumulated payload if necessary --- i.e. at EOS or at the start of
// another payload. event is set if the flushed payload is PES with a sync
// frame.
status_t flush(SyncEvent *event);
+
+ // Flush accumulated payload for scrambled streams if necessary --- i.e. at
+ // EOS or at the start of another payload. event is set if the flushed
+ // payload is PES with a sync frame.
+ status_t flushScrambled(SyncEvent *event);
+
+ // Check if a PES packet is scrambled at PES level.
+ uint32_t getPesScramblingControl(ABitReader *br, int32_t *pesOffset);
+
// Strip and parse PES headers and pass remaining payload into onPayload
// with parsed metadata. event is set if the PES contains a sync frame.
status_t parsePES(ABitReader *br, SyncEvent *event);
@@ -179,7 +221,13 @@
// and timestamp of the packet.
void onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
- const uint8_t *data, size_t size, SyncEvent *event);
+ unsigned PES_scrambling_control,
+ const uint8_t *data, size_t size,
+ int32_t payloadOffset, SyncEvent *event);
+
+ // Ensure internal buffers can hold specified size, and will re-allocate
+ // as needed.
+ void ensureBufferCapacity(size_t size);
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
@@ -254,6 +302,8 @@
bool ATSParser::Program::parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br, status_t *err, SyncEvent *event) {
*err = OK;
@@ -263,7 +313,11 @@
}
*err = mStreams.editValueAt(index)->parse(
- continuity_counter, payload_unit_start_indicator, br, event);
+ continuity_counter,
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
+ br, event);
return true;
}
@@ -359,6 +413,38 @@
return success;
}
+bool ATSParser::Program::findCADescriptor(
+ ABitReader *br, unsigned infoLength,
+ ATSParser::CADescriptor *caDescriptor) {
+ bool found = false;
+ while (infoLength > 2) {
+ unsigned descriptor_tag = br->getBits(8);
+ ALOGV(" tag = 0x%02x", descriptor_tag);
+
+ unsigned descriptor_length = br->getBits(8);
+ ALOGV(" len = %u", descriptor_length);
+
+ infoLength -= 2;
+ if (descriptor_length > infoLength) {
+ break;
+ }
+ if (descriptor_tag == 9 && descriptor_length >= 4) {
+ found = true;
+ caDescriptor->mSystemID = br->getBits(16);
+ caDescriptor->mPID = br->getBits(16) & 0x1fff;
+ infoLength -= 4;
+ caDescriptor->mPrivateData.assign(
+ br->data(), br->data() + descriptor_length - 4);
+ break;
+ } else {
+ infoLength -= descriptor_length;
+ br->skipBits(descriptor_length * 8);
+ }
+ }
+ br->skipBits(infoLength * 8);
+ return found;
+}
+
status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
ALOGV(" table_id = %u", table_id);
@@ -395,7 +481,13 @@
unsigned program_info_length = br->getBits(12);
ALOGV(" program_info_length = %u", program_info_length);
- br->skipBits(program_info_length * 8); // skip descriptors
+ // descriptors
+ CADescriptor programCA;
+ bool hasProgramCA = findCADescriptor(br, program_info_length, &programCA);
+ if (hasProgramCA && !mParser->mCasManager->addProgram(
+ mProgramNumber, programCA)) {
+ return ERROR_MALFORMED;
+ }
Vector<StreamInfo> infos;
@@ -419,28 +511,17 @@
unsigned ES_info_length = br->getBits(12);
ALOGV(" ES_info_length = %u", ES_info_length);
-#if 0
- br->skipBits(ES_info_length * 8); // skip descriptors
-#else
- unsigned info_bytes_remaining = ES_info_length;
- while (info_bytes_remaining >= 2) {
- MY_LOGV(" tag = 0x%02x", br->getBits(8));
-
- unsigned descLength = br->getBits(8);
- ALOGV(" len = %u", descLength);
-
- if (info_bytes_remaining < descLength) {
- return ERROR_MALFORMED;
- }
- br->skipBits(descLength * 8);
-
- info_bytes_remaining -= descLength + 2;
+ CADescriptor streamCA;
+ bool hasStreamCA = findCADescriptor(br, ES_info_length, &streamCA);
+ if (hasStreamCA && !mParser->mCasManager->addStream(
+ mProgramNumber, elementaryPID, streamCA)) {
+ return ERROR_MALFORMED;
}
-#endif
-
StreamInfo info;
info.mType = streamType;
info.mPID = elementaryPID;
+ info.mCASystemId = hasProgramCA ? programCA.mSystemID :
+ hasStreamCA ? streamCA.mSystemID : -1;
infos.push(info);
infoBytesRemaining -= 5 + ES_info_length;
@@ -490,19 +571,29 @@
}
}
+ bool isAddingScrambledStream = false;
for (size_t i = 0; i < infos.size(); ++i) {
StreamInfo &info = infos.editItemAt(i);
+ if (mParser->mCasManager->isCAPid(info.mPID)) {
+ // skip CA streams (EMM/ECM)
+ continue;
+ }
ssize_t index = mStreams.indexOfKey(info.mPID);
if (index < 0) {
sp<Stream> stream = new Stream(
- this, info.mPID, info.mType, PCR_PID);
+ this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
+ isAddingScrambledStream |= info.mCASystemId >= 0;
mStreams.add(info.mPID, stream);
}
}
+ if (isAddingScrambledStream) {
+ ALOGI("Receiving scrambled streams without descrambler!");
+ return ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED;
+ }
return OK;
}
@@ -586,13 +677,27 @@
return timeUs;
}
+void ATSParser::Program::updateCasSessions() {
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ sp<Stream> &stream = mStreams.editValueAt(i);
+ sp<IDescrambler> descrambler;
+ std::vector<uint8_t> sessionId;
+ if (mParser->mCasManager->getCasSession(
+ mProgramNumber, stream->pid(), &descrambler, &sessionId)) {
+ stream->setCasSession(descrambler, sessionId);
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
+static const size_t kInitialStreamBufferSize = 192 * 1024;
ATSParser::Stream::Stream(
Program *program,
unsigned elementaryPID,
unsigned streamType,
- unsigned PCR_PID)
+ unsigned PCR_PID,
+ int32_t CA_system_ID)
: mProgram(program),
mElementaryPID(elementaryPID),
mStreamType(streamType),
@@ -601,54 +706,71 @@
mPayloadStarted(false),
mEOSReached(false),
mPrevPTS(0),
- mQueue(NULL) {
+ mQueue(NULL),
+ mScrambled(CA_system_ID >= 0) {
+ ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d",
+ elementaryPID, streamType, mScrambled);
+
+ uint32_t flags = (isVideo() && mScrambled) ?
+ ElementaryStreamQueue::kFlag_ScrambledData : 0;
+
+ ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;
+
switch (mStreamType) {
case STREAMTYPE_H264:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::H264,
- (mProgram->parserFlags() & ALIGNED_VIDEO_DATA)
- ? ElementaryStreamQueue::kFlag_AlignedData : 0);
+ mode = ElementaryStreamQueue::H264;
+ flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ?
+ ElementaryStreamQueue::kFlag_AlignedData : 0;
break;
+
case STREAMTYPE_MPEG2_AUDIO_ADTS:
- mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
+ mode = ElementaryStreamQueue::AAC;
break;
+
case STREAMTYPE_MPEG1_AUDIO:
case STREAMTYPE_MPEG2_AUDIO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG_AUDIO);
+ mode = ElementaryStreamQueue::MPEG_AUDIO;
break;
case STREAMTYPE_MPEG1_VIDEO:
case STREAMTYPE_MPEG2_VIDEO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG_VIDEO);
+ mode = ElementaryStreamQueue::MPEG_VIDEO;
break;
case STREAMTYPE_MPEG4_VIDEO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG4_VIDEO);
+ mode = ElementaryStreamQueue::MPEG4_VIDEO;
break;
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::AC3);
+ mode = ElementaryStreamQueue::AC3;
break;
case STREAMTYPE_METADATA:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::METADATA);
+ mode = ElementaryStreamQueue::METADATA;
break;
default:
- break;
+ ALOGE("stream PID 0x%02x has invalid stream type 0x%02x",
+ elementaryPID, streamType);
+ return;
}
- ALOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
+ mQueue = new ElementaryStreamQueue(mode, flags);
if (mQueue != NULL) {
- mBuffer = new ABuffer(192 * 1024);
- mBuffer->setRange(0, 0);
+ ensureBufferCapacity(kInitialStreamBufferSize);
+
+ if (mScrambled && (isAudio() || isVideo())) {
+ // Set initial format to scrambled
+ sp<MetaData> meta = new MetaData();
+ meta->setCString(kKeyMIMEType,
+ isAudio() ? MEDIA_MIMETYPE_AUDIO_SCRAMBLED
+ : MEDIA_MIMETYPE_VIDEO_SCRAMBLED);
+ // for DrmInitData
+ meta->setData(kKeyCas, 0, &CA_system_ID, sizeof(CA_system_ID));
+ mSource = new AnotherPacketSource(meta);
+ }
}
}
@@ -657,10 +779,57 @@
mQueue = NULL;
}
+void ATSParser::Stream::ensureBufferCapacity(size_t neededSize) {
+ if (mBuffer != NULL && mBuffer->capacity() >= neededSize) {
+ return;
+ }
+
+ ALOGV("ensureBufferCapacity: current size %zu, new size %zu, scrambled %d",
+ mBuffer == NULL ? 0 : mBuffer->capacity(), neededSize, mScrambled);
+
+ sp<ABuffer> newBuffer, newScrambledBuffer;
+ sp<IMemory> newMem;
+ sp<MemoryDealer> newDealer;
+ if (mScrambled) {
+ size_t alignment = MemoryDealer::getAllocationAlignment();
+ neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1);
+ // Align to multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+ newDealer = new MemoryDealer(neededSize, "ATSParser");
+ newMem = newDealer->allocate(neededSize);
+ newScrambledBuffer = new ABuffer(newMem->pointer(), newMem->size());
+
+ if (mDescrambledBuffer != NULL) {
+ memcpy(newScrambledBuffer->data(),
+ mDescrambledBuffer->data(), mDescrambledBuffer->size());
+ newScrambledBuffer->setRange(0, mDescrambledBuffer->size());
+ } else {
+ newScrambledBuffer->setRange(0, 0);
+ }
+ mMem = newMem;
+ mDealer = newDealer;
+ mDescrambledBuffer = newScrambledBuffer;
+ } else {
+ // Align to multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+ }
+
+ newBuffer = new ABuffer(neededSize);
+ if (mBuffer != NULL) {
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ } else {
+ newBuffer->setRange(0, 0);
+ }
+ mBuffer = newBuffer;
+}
+
status_t ATSParser::Stream::parse(
unsigned continuity_counter,
- unsigned payload_unit_start_indicator, ABitReader *br,
- SyncEvent *event) {
+ unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
+ ABitReader *br, SyncEvent *event) {
if (mQueue == NULL) {
return OK;
}
@@ -672,6 +841,7 @@
mPayloadStarted = false;
mPesStartOffsets.clear();
mBuffer->setRange(0, 0);
+ mSubSamples.clear();
mExpectedContinuityCounter = -1;
#if 0
@@ -725,21 +895,16 @@
}
size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
- if (mBuffer->capacity() < neededSize) {
- // Increment in multiples of 64K.
- neededSize = (neededSize + 65535) & ~65535;
-
- ALOGI("resizing buffer to %zu bytes", neededSize);
-
- sp<ABuffer> newBuffer = new ABuffer(neededSize);
- memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
- newBuffer->setRange(0, mBuffer->size());
- mBuffer = newBuffer;
- }
+ ensureBufferCapacity(neededSize);
memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
+ if (mScrambled) {
+ mSubSamples.push_back({payloadSizeBits / 8,
+ transport_scrambling_control, random_access_indicator});
+ }
+
return OK;
}
@@ -789,6 +954,7 @@
mPesStartOffsets.clear();
mEOSReached = false;
mBuffer->setRange(0, 0);
+ mSubSamples.clear();
bool clearFormat = false;
if (isAudio()) {
@@ -817,7 +983,15 @@
}
if (mSource != NULL) {
- mSource->queueDiscontinuity(type, extra, true);
+ sp<MetaData> meta = mSource->getFormat();
+ const char* mime;
+ if (clearFormat && meta != NULL && meta->findCString(kKeyMIMEType, &mime)
+ && (!strncasecmp(mime, MEDIA_MIMETYPE_AUDIO_SCRAMBLED, 15)
+ || !strncasecmp(mime, MEDIA_MIMETYPE_VIDEO_SCRAMBLED, 15))){
+ mSource->clear();
+ } else {
+ mSource->queueDiscontinuity(type, extra, true);
+ }
}
}
@@ -830,6 +1004,8 @@
}
status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) {
+ const uint8_t *basePtr = br->data();
+
unsigned packet_startcode_prefix = br->getBits(24);
ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
@@ -859,7 +1035,9 @@
return ERROR_MALFORMED;
}
- MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
+ unsigned PES_scrambling_control = br->getBits(2);
+ ALOGV("PES_scrambling_control = %u", PES_scrambling_control);
+
MY_LOGV("PES_priority = %u", br->getBits(1));
MY_LOGV("data_alignment_indicator = %u", br->getBits(1));
MY_LOGV("copyright = %u", br->getBits(1));
@@ -992,6 +1170,7 @@
br->skipBits(optional_bytes_remaining * 8);
// ES data follows.
+ int32_t pesOffset = br->data() - basePtr;
if (PES_packet_length != 0) {
if (PES_packet_length < PES_header_data_length + 3) {
@@ -1009,21 +1188,26 @@
return ERROR_MALFORMED;
}
+ ALOGV("There's %u bytes of payload, PES_packet_length=%u, offset=%d",
+ dataLength, PES_packet_length, pesOffset);
+
onPayloadData(
- PTS_DTS_flags, PTS, DTS, br->data(), dataLength, event);
+ PTS_DTS_flags, PTS, DTS, PES_scrambling_control,
+ br->data(), dataLength, pesOffset, event);
br->skipBits(dataLength * 8);
} else {
onPayloadData(
- PTS_DTS_flags, PTS, DTS,
- br->data(), br->numBitsLeft() / 8, event);
+ PTS_DTS_flags, PTS, DTS, PES_scrambling_control,
+ br->data(), br->numBitsLeft() / 8, pesOffset, event);
size_t payloadSizeBits = br->numBitsLeft();
if (payloadSizeBits % 8 != 0u) {
return ERROR_MALFORMED;
}
- ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8);
+ ALOGV("There's %zu bytes of payload, offset=%d",
+ payloadSizeBits / 8, pesOffset);
}
} else if (stream_id == 0xbe) { // padding_stream
if (PES_packet_length == 0u) {
@@ -1040,6 +1224,200 @@
return OK;
}
+uint32_t ATSParser::Stream::getPesScramblingControl(
+ ABitReader *br, int32_t *pesOffset) {
+ unsigned packet_startcode_prefix = br->getBits(24);
+
+ ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
+
+ if (packet_startcode_prefix != 1) {
+ ALOGV("unit does not start with startcode.");
+ return 0;
+ }
+
+ if (br->numBitsLeft() < 48) {
+ return 0;
+ }
+
+ unsigned stream_id = br->getBits(8);
+ ALOGV("stream_id = 0x%02x", stream_id);
+
+ br->skipBits(16); // PES_packet_length
+
+ if (stream_id != 0xbc // program_stream_map
+ && stream_id != 0xbe // padding_stream
+ && stream_id != 0xbf // private_stream_2
+ && stream_id != 0xf0 // ECM
+ && stream_id != 0xf1 // EMM
+ && stream_id != 0xff // program_stream_directory
+ && stream_id != 0xf2 // DSMCC
+ && stream_id != 0xf8) { // H.222.1 type E
+ if (br->getBits(2) != 2u) {
+ return 0;
+ }
+
+ unsigned PES_scrambling_control = br->getBits(2);
+ ALOGV("PES_scrambling_control = %u", PES_scrambling_control);
+
+ if (PES_scrambling_control == 0) {
+ return 0;
+ }
+
+ br->skipBits(12); // don't care
+
+ unsigned PES_header_data_length = br->getBits(8);
+ ALOGV("PES_header_data_length = %u", PES_header_data_length);
+
+ if (PES_header_data_length * 8 > br->numBitsLeft()) {
+ return 0;
+ }
+
+ *pesOffset = 9 + PES_header_data_length;
+ ALOGD("found PES_scrambling_control=%d, PES offset=%d",
+ PES_scrambling_control, *pesOffset);
+ return PES_scrambling_control;
+ }
+
+ return 0;
+}
+
+status_t ATSParser::Stream::flushScrambled(SyncEvent *event) {
+ if (mDescrambler == NULL) {
+ ALOGE("received scrambled packets without descrambler!");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mDescrambledBuffer == NULL || mMem == NULL) {
+ ALOGE("received scrambled packets without shared memory!");
+
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t pesOffset = 0;
+ int32_t descrambleSubSamples = 0, descrambleBytes = 0;
+ uint32_t tsScramblingControl = 0, pesScramblingControl = 0;
+
+ // First, go over subsamples to find TS-level scrambling key id, and
+ // calculate how many subsample we need to descramble (assuming we don't
+ // have PES-level scrambling).
+ for (auto it = mSubSamples.begin(); it != mSubSamples.end(); it++) {
+ if (it->transport_scrambling_mode != 0) {
+ // TODO: handle keyId change, use the first non-zero keyId for now.
+ if (tsScramblingControl == 0) {
+ tsScramblingControl = it->transport_scrambling_mode;
+ }
+ }
+ if (tsScramblingControl == 0 || descrambleSubSamples == 0
+ || !mQueue->isScrambled()) {
+ descrambleSubSamples++;
+ descrambleBytes += it->subSampleSize;
+ }
+ }
+ // If not scrambled at TS-level, check PES-level scrambling
+ if (tsScramblingControl == 0) {
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ pesScramblingControl = getPesScramblingControl(&br, &pesOffset);
+ // If not scrambled at PES-level either, or scrambled at PES-level but
+ // requires output to remain scrambled, we don't need to descramble
+ // anything.
+ if (pesScramblingControl == 0 || mQueue->isScrambled()) {
+ descrambleSubSamples = 0;
+ descrambleBytes = 0;
+ }
+ }
+
+ uint32_t sctrl = tsScramblingControl != 0 ?
+ tsScramblingControl : pesScramblingControl;
+
+ // Perform the 1st pass descrambling if needed
+ if (descrambleBytes > 0) {
+ memcpy(mDescrambledBuffer->data(), mBuffer->data(), descrambleBytes);
+ mDescrambledBuffer->setRange(0, descrambleBytes);
+
+ sp<ABuffer> subSamples = new ABuffer(
+ sizeof(DescramblerPlugin::SubSample) * descrambleSubSamples);
+
+ DescrambleInfo info;
+ info.dstType = DescrambleInfo::kDestinationTypeVmPointer;
+ info.scramblingControl = (DescramblerPlugin::ScramblingControl)sctrl;
+ info.numSubSamples = descrambleSubSamples;
+ info.subSamples = (DescramblerPlugin::SubSample *)subSamples->data();
+ info.srcMem = mMem;
+ info.srcOffset = 0;
+ info.dstPtr = NULL; // in-place descrambling into srcMem
+ info.dstOffset = 0;
+
+ int32_t i = 0;
+ for (auto it = mSubSamples.begin();
+ it != mSubSamples.end() && i < descrambleSubSamples; it++, i++) {
+ if (it->transport_scrambling_mode != 0 || pesScramblingControl != 0) {
+ info.subSamples[i].mNumBytesOfClearData = 0;
+ info.subSamples[i].mNumBytesOfEncryptedData = it->subSampleSize;
+ } else {
+ info.subSamples[i].mNumBytesOfClearData = it->subSampleSize;
+ info.subSamples[i].mNumBytesOfEncryptedData = 0;
+ }
+ }
+ // If scrambled at PES-level, PES header should be skipped
+ if (pesScramblingControl != 0) {
+ info.srcOffset = info.dstOffset = pesOffset;
+ info.subSamples[0].mNumBytesOfEncryptedData -= pesOffset;
+ }
+
+ int32_t result;
+ Status status = mDescrambler->descramble(info, &result);
+
+ if (!status.isOk()) {
+ ALOGE("[stream %d] descramble failed, exceptionCode=%d",
+ mElementaryPID, status.exceptionCode());
+ return UNKNOWN_ERROR;
+ }
+
+ ALOGV("[stream %d] descramble succeeded, %d bytes",
+ mElementaryPID, result);
+ memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes);
+ }
+
+ if (mQueue->isScrambled()) {
+ // Queue subSample info for scrambled queue
+ sp<ABuffer> clearSizesBuffer = new ABuffer(mSubSamples.size() * 4);
+ sp<ABuffer> encSizesBuffer = new ABuffer(mSubSamples.size() * 4);
+ int32_t *clearSizePtr = (int32_t*)clearSizesBuffer->data();
+ int32_t *encSizePtr = (int32_t*)encSizesBuffer->data();
+ int32_t isSync = 0;
+ int32_t i = 0;
+ for (auto it = mSubSamples.begin();
+ it != mSubSamples.end(); it++, i++) {
+ if ((it->transport_scrambling_mode == 0
+ && pesScramblingControl == 0)
+ || i < descrambleSubSamples) {
+ clearSizePtr[i] = it->subSampleSize;
+ encSizePtr[i] = 0;
+ } else {
+ clearSizePtr[i] = 0;
+ encSizePtr[i] = it->subSampleSize;
+ }
+ isSync |= it->random_access_indicator;
+ }
+ // Pass the original TS subsample size now. The PES header adjust
+ // will be applied when the scrambled AU is dequeued.
+ mQueue->appendScrambledData(
+ mBuffer->data(), mBuffer->size(), sctrl,
+ isSync, clearSizesBuffer, encSizesBuffer);
+ }
+
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ status_t err = parsePES(&br, event);
+
+ if (err != OK) {
+ ALOGE("[stream %d] failed to parse descrambled PES, err=%d",
+ mElementaryPID, err);
+ }
+
+ return err;
+}
+
+
status_t ATSParser::Stream::flush(SyncEvent *event) {
if (mBuffer == NULL || mBuffer->size() == 0) {
return OK;
@@ -1047,9 +1425,14 @@
ALOGV("flushing stream 0x%04x size = %zu", mElementaryPID, mBuffer->size());
- ABitReader br(mBuffer->data(), mBuffer->size());
-
- status_t err = parsePES(&br, event);
+ status_t err = OK;
+ if (mScrambled) {
+ err = flushScrambled(event);
+ mSubSamples.clear();
+ } else {
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ err = parsePES(&br, event);
+ }
mBuffer->setRange(0, 0);
@@ -1058,7 +1441,9 @@
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
- const uint8_t *data, size_t size, SyncEvent *event) {
+ unsigned PES_scrambling_control,
+ const uint8_t *data, size_t size,
+ int32_t payloadOffset, SyncEvent *event) {
#if 0
ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
mStreamType,
@@ -1074,7 +1459,8 @@
timeUs = mProgram->convertPTSToTimestamp(PTS);
}
- status_t err = mQueue->appendData(data, size, timeUs);
+ status_t err = mQueue->appendData(
+ data, size, timeUs, payloadOffset, PES_scrambling_control);
if (mEOSReached) {
mQueue->signalEOS();
@@ -1096,9 +1482,11 @@
const char *mime;
if (meta->findCString(kKeyMIMEType, &mime)
- && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
- && !IsIDR(accessUnit)) {
- continue;
+ && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ int32_t sync = 0;
+ if (!accessUnit->meta()->findInt32("isSync", &sync) || !sync) {
+ continue;
+ }
}
mSource = new AnotherPacketSource(meta);
mSource->queueAccessUnit(accessUnit);
@@ -1178,6 +1566,18 @@
return NULL;
}
+void ATSParser::Stream::setCasSession(
+ const sp<IDescrambler> &descrambler,
+ const std::vector<uint8_t> &sessionId) {
+ if (mSource != NULL && mDescrambler == NULL && descrambler != NULL) {
+ signalDiscontinuity(DISCONTINUITY_FORMAT_ONLY, NULL);
+ mDescrambler = descrambler;
+ if (mQueue->isScrambled()) {
+ mQueue->setCasSession(sessionId);
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
ATSParser::ATSParser(uint32_t flags)
@@ -1189,6 +1589,7 @@
mNumTSPacketsParsed(0),
mNumPCRs(0) {
mPSISections.add(0 /* PID */, new PSISection);
+ mCasManager = new CasManager();
}
ATSParser::~ATSParser() {
@@ -1205,6 +1606,17 @@
return parseTS(&br, event);
}
+status_t ATSParser::setMediaCas(const sp<ICas> &cas) {
+ status_t err = mCasManager->setMediaCas(cas);
+ if (err != OK) {
+ return err;
+ }
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ mPrograms.editItemAt(i)->updateCasSessions();
+ }
+ return OK;
+}
+
void ATSParser::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
int64_t mediaTimeUs;
@@ -1331,6 +1743,8 @@
ABitReader *br, unsigned PID,
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
SyncEvent *event) {
ssize_t sectionIndex = mPSISections.indexOfKey(PID);
@@ -1402,7 +1816,10 @@
for (size_t i = 0; i < mPrograms.size(); ++i) {
status_t err;
if (mPrograms.editItemAt(i)->parsePID(
- PID, continuity_counter, payload_unit_start_indicator,
+ PID, continuity_counter,
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
br, &err, event)) {
if (err != OK) {
return err;
@@ -1414,13 +1831,19 @@
}
if (!handled) {
+ handled = mCasManager->parsePID(br, PID);
+ }
+
+ if (!handled) {
ALOGV("PID 0x%04x not handled.", PID);
}
return OK;
}
-status_t ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
+status_t ATSParser::parseAdaptationField(
+ ABitReader *br, unsigned PID, unsigned *random_access_indicator) {
+ *random_access_indicator = 0;
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
@@ -1435,7 +1858,16 @@
ALOGV("PID 0x%04x: discontinuity_indicator = 1 (!!!)", PID);
}
- br->skipBits(2);
+ *random_access_indicator = br->getBits(1);
+ if (*random_access_indicator) {
+ ALOGV("PID 0x%04x: random_access_indicator = 1", PID);
+ }
+
+ unsigned elementary_stream_priority_indicator = br->getBits(1);
+ if (elementary_stream_priority_indicator) {
+ ALOGV("PID 0x%04x: elementary_stream_priority_indicator = 1", PID);
+ }
+
unsigned PCR_flag = br->getBits(1);
size_t numBitsRead = 4;
@@ -1501,7 +1933,8 @@
unsigned PID = br->getBits(13);
ALOGV("PID = 0x%04x", PID);
- MY_LOGV("transport_scrambling_control = %u", br->getBits(2));
+ unsigned transport_scrambling_control = br->getBits(2);
+ ALOGV("transport_scrambling_control = %u", transport_scrambling_control);
unsigned adaptation_field_control = br->getBits(2);
ALOGV("adaptation_field_control = %u", adaptation_field_control);
@@ -1513,13 +1946,17 @@
status_t err = OK;
+ unsigned random_access_indicator = 0;
if (adaptation_field_control == 2 || adaptation_field_control == 3) {
- err = parseAdaptationField(br, PID);
+ err = parseAdaptationField(br, PID, &random_access_indicator);
}
if (err == OK) {
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
err = parsePID(br, PID, continuity_counter,
- payload_unit_start_indicator, event);
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
+ event);
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index faae6c9..4a88713 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -26,11 +26,17 @@
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
+#include <vector>
namespace android {
-
+namespace media {
+class ICas;
+class IDescrambler;
+};
+using namespace media;
class ABitReader;
struct ABuffer;
+struct AnotherPacketSource;
struct ATSParser : public RefBase {
enum DiscontinuityType {
@@ -100,6 +106,8 @@
explicit ATSParser(uint32_t flags = 0);
+ status_t setMediaCas(const sp<ICas> &cas);
+
// Feed a TS packet into the parser. uninitialized event with the start
// offset of this TS packet goes in, and if the parser detects PES with
// a sync frame, the event will be initiailzed with the start offset of the
@@ -150,6 +158,14 @@
struct Program;
struct Stream;
struct PSISection;
+ struct CasManager;
+ struct CADescriptor {
+ int32_t mSystemID;
+ unsigned mPID;
+ std::vector<uint8_t> mPrivateData;
+ };
+
+ sp<CasManager> mCasManager;
uint32_t mFlags;
Vector<sp<Program> > mPrograms;
@@ -181,9 +197,13 @@
ABitReader *br, unsigned PID,
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
SyncEvent *event);
- status_t parseAdaptationField(ABitReader *br, unsigned PID);
+ status_t parseAdaptationField(
+ ABitReader *br, unsigned PID, unsigned *random_access_indicator);
+
// see feedTSPacket().
status_t parseTS(ABitReader *br, SyncEvent *event);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 92c386c..5140e66 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -5,6 +5,7 @@
LOCAL_SRC_FILES:= \
AnotherPacketSource.cpp \
ATSParser.cpp \
+ CasManager.cpp \
ESQueue.cpp \
MPEG2PSExtractor.cpp \
MPEG2TSExtractor.cpp \
@@ -14,7 +15,8 @@
$(TOP)/frameworks/native/include/media/openmax
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_SHARED_LIBRARIES := libmedia
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 548f44e..433b1fc 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -204,25 +204,53 @@
}
MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
+ sp<MetaData> bufmeta = mediaBuffer->meta_data();
- mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ bufmeta->setInt64(kKeyTime, timeUs);
int32_t isSync;
if (buffer->meta()->findInt32("isSync", &isSync)) {
- mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+ bufmeta->setInt32(kKeyIsSyncFrame, isSync);
}
sp<ABuffer> sei;
if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
- mediaBuffer->meta_data()->setData(kKeySEI, 0, sei->data(), sei->size());
+ bufmeta->setData(kKeySEI, 0, sei->data(), sei->size());
}
sp<ABuffer> mpegUserData;
if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
- mediaBuffer->meta_data()->setData(
+ bufmeta->setData(
kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
}
+ int32_t cryptoMode;
+ if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) {
+ int32_t cryptoKey;
+ sp<ABuffer> clearBytesBuffer, encBytesBuffer;
+
+ CHECK(buffer->meta()->findInt32("cryptoKey", &cryptoKey));
+ CHECK(buffer->meta()->findBuffer("clearBytes", &clearBytesBuffer)
+ && clearBytesBuffer != NULL);
+ CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer)
+ && encBytesBuffer != NULL);
+
+ bufmeta->setInt32(kKeyCryptoMode, cryptoMode);
+
+ uint8_t array[16] = {0};
+ bufmeta->setData(kKeyCryptoIV, 0, array, 16);
+
+ array[0] = (uint8_t) (cryptoKey & 0xff);
+ bufmeta->setData(kKeyCryptoKey, 0, array, 16);
+
+ bufmeta->setData(kKeyPlainSizes, 0,
+ clearBytesBuffer->data(), clearBytesBuffer->size());
+
+ bufmeta->setData(kKeyEncryptedSizes, 0,
+ encBytesBuffer->data(), encBytesBuffer->size());
+ }
+
+
*out = mediaBuffer;
return OK;
}
diff --git a/media/libstagefright/mpeg2ts/CasManager.cpp b/media/libstagefright/mpeg2ts/CasManager.cpp
new file mode 100644
index 0000000..4e34a30
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/CasManager.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CasManager"
+#include "CasManager.h"
+
+#include <android/media/ICas.h>
+#include <android/media/IDescrambler.h>
+#include <android/media/IMediaCasService.h>
+#include <binder/IServiceManager.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <utils/Log.h>
+
+namespace android {
+using binder::Status;
+
+struct ATSParser::CasManager::ProgramCasManager : public RefBase {
+ ProgramCasManager(unsigned programNumber, const CADescriptor &descriptor);
+ ProgramCasManager(unsigned programNumber);
+
+ bool addStream(unsigned elementaryPID, const CADescriptor &descriptor);
+
+ status_t setMediaCas(const sp<ICas> &cas, PidToSessionMap &sessionMap);
+
+ bool getCasSession(unsigned elementaryPID,
+ sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const;
+
+ void closeAllSessions(const sp<ICas>& cas);
+
+private:
+ struct CasSession {
+ CasSession() {}
+ CasSession(const CADescriptor &descriptor) :
+ mCADescriptor(descriptor) {}
+
+ CADescriptor mCADescriptor;
+ std::vector<uint8_t> mSessionId;
+ sp<IDescrambler> mDescrambler;
+ };
+ status_t initSession(
+ const sp<ICas>& cas, PidToSessionMap &sessionMap,
+ CasSession *session, unsigned programNumber, unsigned elementaryPID);
+ void closeSession(const sp<ICas>& cas, const CasSession &casSession);
+
+ unsigned mProgramNumber;
+ bool mHasProgramCas;
+ CasSession mProgramCas;
+ KeyedVector<unsigned, CasSession> mStreamPidToCasMap;
+};
+
+ATSParser::CasManager::ProgramCasManager::ProgramCasManager(
+ unsigned programNumber, const CADescriptor &descriptor) :
+ mProgramNumber(programNumber),
+ mHasProgramCas(true),
+ mProgramCas(descriptor) {}
+
+ATSParser::CasManager::ProgramCasManager::ProgramCasManager(
+ unsigned programNumber) :
+ mProgramNumber(programNumber),
+ mHasProgramCas(false) {}
+
+bool ATSParser::CasManager::ProgramCasManager::addStream(
+ unsigned elementaryPID, const CADescriptor &descriptor) {
+ ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID);
+ if (index >= 0) {
+ return false;
+ }
+ ALOGV("addStream: program=%d, elementaryPID=%d, CA_system_ID=0x%x",
+ mProgramNumber, elementaryPID, descriptor.mSystemID);
+ mStreamPidToCasMap.add(elementaryPID, CasSession(descriptor));
+ return true;
+}
+
+status_t ATSParser::CasManager::ProgramCasManager::setMediaCas(
+ const sp<ICas> &cas, PidToSessionMap &sessionMap) {
+ if (mHasProgramCas) {
+ return initSession(cas, sessionMap, &mProgramCas, mProgramNumber, 0);
+ }
+ for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) {
+ unsigned elementaryPID = mStreamPidToCasMap.keyAt(index);
+ status_t err;
+ if ((err = initSession(cas, sessionMap,
+ &mStreamPidToCasMap.editValueAt(index),
+ mProgramNumber, elementaryPID)) != OK) {
+ return err;
+ }
+ }
+ return OK;
+}
+
+bool ATSParser::CasManager::ProgramCasManager::getCasSession(
+ unsigned elementaryPID, sp<IDescrambler> *descrambler,
+ std::vector<uint8_t> *sessionId) const {
+ if (mHasProgramCas) {
+ *descrambler = mProgramCas.mDescrambler;
+ *sessionId = mProgramCas.mSessionId;
+ return true;
+ }
+ ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID);
+ if (index < 0) {
+ return false;
+ }
+
+ *descrambler = mStreamPidToCasMap[index].mDescrambler;
+ *sessionId = mStreamPidToCasMap[index].mSessionId;
+ return true;
+}
+
+status_t ATSParser::CasManager::ProgramCasManager::initSession(
+ const sp<ICas>& cas, PidToSessionMap &sessionMap,
+ CasSession *session, unsigned programNumber, unsigned elementaryPID) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> casServiceBinder = sm->getService(String16("media.cas"));
+ sp<IMediaCasService> casService =
+ interface_cast<IMediaCasService>(casServiceBinder);
+
+ if (casService == NULL) {
+ ALOGE("Cannot obtain IMediaCasService");
+ return NO_INIT;
+ }
+
+ sp<IDescrambler> descrambler;
+ std::vector<uint8_t> sessionId;
+ const CADescriptor &descriptor = session->mCADescriptor;
+
+ Status status;
+ if (elementaryPID == 0) {
+ status = cas->openSession(programNumber, &sessionId);
+ } else {
+ status = cas->openSessionForStream(
+ programNumber, elementaryPID, &sessionId);
+ }
+ if (!status.isOk()) {
+ ALOGE("Failed to open session: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ cas->setSessionPrivateData(sessionId, descriptor.mPrivateData);
+ if (!status.isOk()) {
+ ALOGE("Failed to set private data: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ status = casService->createDescrambler(descriptor.mSystemID, &descrambler);
+ if (!status.isOk() || descrambler == NULL) {
+ ALOGE("Failed to create descrambler: : exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ status = descrambler->setMediaCasSession(sessionId);
+ if (!status.isOk()) {
+ ALOGE("Failed to init descrambler: : exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ session->mSessionId = sessionId;
+ session->mDescrambler = descrambler;
+ sessionMap.add(descriptor.mPID, sessionId);
+
+ return OK;
+
+l_fail:
+ if (!sessionId.empty()) {
+ cas->closeSession(sessionId);
+ }
+ if (descrambler != NULL) {
+ descrambler->release();
+ }
+ return NO_INIT;
+}
+
+void ATSParser::CasManager::ProgramCasManager::closeSession(
+ const sp<ICas>& cas, const CasSession &casSession) {
+ if (casSession.mDescrambler != NULL) {
+ casSession.mDescrambler->release();
+ }
+ if (!casSession.mSessionId.empty()) {
+ cas->closeSession(casSession.mSessionId);
+ }
+}
+
+void ATSParser::CasManager::ProgramCasManager::closeAllSessions(
+ const sp<ICas>& cas) {
+ if (mHasProgramCas) {
+ closeSession(cas, mProgramCas);
+ }
+ for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) {
+ closeSession(cas, mStreamPidToCasMap.editValueAt(index));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::CasManager::CasManager() : mSystemId(-1) {}
+
+ATSParser::CasManager::~CasManager() {
+ // Explictly close the sessions opened by us, since the CAS object is owned
+ // by the app and may not go away after the parser is destroyed, and the app
+ // may not have information about the sessions.
+ if (mICas != NULL) {
+ for (size_t index = 0; index < mProgramCasMap.size(); index++) {
+ mProgramCasMap.editValueAt(index)->closeAllSessions(mICas);
+ }
+ }
+}
+
+bool ATSParser::CasManager::setSystemId(int32_t CA_system_ID) {
+ if (mSystemId == -1) {
+ // Verify the CA_system_ID is within range on the first program
+ if (CA_system_ID < 0 || CA_system_ID > 0xffff) {
+ ALOGE("Invalid CA_system_id: %d", CA_system_ID);
+ return false;
+ }
+ mSystemId = CA_system_ID;
+ } else if (mSystemId != CA_system_ID) {
+ // All sessions need to be under the same CA system
+ ALOGE("Multiple CA systems not allowed: %d vs %d",
+ mSystemId, CA_system_ID);
+ return false;
+ }
+ return true;
+}
+
+status_t ATSParser::CasManager::setMediaCas(const sp<ICas> &cas) {
+ if (cas == NULL) {
+ ALOGE("setMediaCas: received NULL object");
+ return BAD_VALUE;
+ }
+ if (mICas != NULL) {
+ ALOGW("setMediaCas: already set");
+ return ALREADY_EXISTS;
+ }
+ for (size_t index = 0; index < mProgramCasMap.size(); index++) {
+ status_t err;
+ if ((err = mProgramCasMap.editValueAt(
+ index)->setMediaCas(cas, mCAPidToSessionIdMap)) != OK) {
+ return err;
+ }
+ }
+ mICas = cas;
+ return OK;
+}
+
+bool ATSParser::CasManager::addProgram(
+ unsigned programNumber, const CADescriptor &descriptor) {
+ if (!setSystemId(descriptor.mSystemID)) {
+ return false;
+ }
+
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ if (index < 0) {
+ ALOGV("addProgram: programNumber=%d, CA_system_ID=0x%x",
+ programNumber, descriptor.mSystemID);
+ mProgramCasMap.add(programNumber,
+ new ProgramCasManager(programNumber, descriptor));
+ mCAPidSet.insert(descriptor.mPID);
+ }
+ return true;
+}
+
+bool ATSParser::CasManager::addStream(
+ unsigned programNumber, unsigned elementaryPID,
+ const CADescriptor &descriptor) {
+ if (!setSystemId(descriptor.mSystemID)) {
+ return false;
+ }
+
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ sp<ProgramCasManager> programCasManager;
+ if (index < 0) {
+ ALOGV("addProgram (no CADescriptor): programNumber=%d", programNumber);
+ programCasManager = new ProgramCasManager(programNumber);
+ mProgramCasMap.add(programNumber, programCasManager);
+ } else {
+ programCasManager = mProgramCasMap.editValueAt(index);
+ }
+ if (programCasManager->addStream(elementaryPID, descriptor)) {
+ mCAPidSet.insert(descriptor.mPID);
+ }
+ return true;
+}
+
+bool ATSParser::CasManager::getCasSession(
+ unsigned programNumber, unsigned elementaryPID,
+ sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const {
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ if (index < 0) {
+ return false;
+ }
+ return mProgramCasMap[index]->getCasSession(
+ elementaryPID, descrambler, sessionId);
+}
+
+bool ATSParser::CasManager::isCAPid(unsigned pid) {
+ return mCAPidSet.find(pid) != mCAPidSet.end();
+}
+
+bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) {
+ ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid);
+ if (index < 0) {
+ return false;
+ }
+ MediaCas::ParcelableCasData ecm(br->data(), br->numBitsLeft() / 8);
+ Status status = mICas->processEcm(mCAPidToSessionIdMap[index], ecm);
+ if (!status.isOk()) {
+ ALOGE("Failed to process ECM: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ }
+ return true; // handled
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/CasManager.h b/media/libstagefright/mpeg2ts/CasManager.h
new file mode 100644
index 0000000..a7a3de9
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/CasManager.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+
+#include "ATSParser.h"
+#include <utils/KeyedVector.h>
+#include <set>
+
+namespace android {
+namespace media {
+class ICas;
+class IDescrambler;
+}
+
+struct ATSParser::CasManager : public RefBase {
+ CasManager();
+ virtual ~CasManager();
+
+ status_t setMediaCas(const sp<ICas> &cas);
+
+ bool addProgram(
+ unsigned programNumber, const CADescriptor &descriptor);
+
+ bool addStream(
+ unsigned programNumber, unsigned elementaryPID,
+ const CADescriptor &descriptor);
+
+ bool getCasSession(
+ unsigned programNumber, unsigned elementaryPID,
+ sp<IDescrambler> *descrambler,
+ std::vector<uint8_t> *sessionId) const;
+
+ bool isCAPid(unsigned pid);
+
+ bool parsePID(ABitReader *br, unsigned pid);
+
+private:
+ typedef KeyedVector<unsigned, std::vector<uint8_t> > PidToSessionMap;
+ struct ProgramCasManager;
+
+ bool setSystemId(int32_t CA_system_ID);
+
+ int32_t mSystemId;
+ sp<ICas> mICas;
+ KeyedVector<unsigned, sp<ProgramCasManager> > mProgramCasMap;
+ PidToSessionMap mCAPidToSessionIdMap;
+ std::set<uint32_t> mCAPidSet;
+};
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 96ca405..b933002 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -28,6 +28,8 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
#include "include/avc_utils.h"
@@ -53,6 +55,11 @@
mRangeInfos.clear();
+ if (mScrambledBuffer != NULL) {
+ mScrambledBuffer->setRange(0, 0);
+ }
+ mScrambledRangeInfos.clear();
+
if (clearFormat) {
mFormat.clear();
}
@@ -246,7 +253,8 @@
}
status_t ElementaryStreamQueue::appendData(
- const void *data, size_t size, int64_t timeUs) {
+ const void *data, size_t size, int64_t timeUs,
+ int32_t payloadOffset, uint32_t pesScramblingControl) {
if (mEOSReached) {
ALOGE("appending data after EOS");
@@ -276,7 +284,7 @@
return ERROR_MALFORMED;
}
- if (startOffset > 0) {
+ if (mFormat == NULL && startOffset > 0) {
ALOGI("found something resembling an H.264/MPEG syncword "
"at offset %zd",
startOffset);
@@ -451,6 +459,8 @@
RangeInfo info;
info.mLength = size;
info.mTimestampUs = timeUs;
+ info.mPesOffset = payloadOffset;
+ info.mPesScramblingControl = pesScramblingControl;
mRangeInfos.push_back(info);
#if 0
@@ -463,8 +473,129 @@
return OK;
}
+void ElementaryStreamQueue::appendScrambledData(
+ const void *data, size_t size,
+ int32_t keyId, bool isSync,
+ sp<ABuffer> clearSizes, sp<ABuffer> encSizes) {
+ if (!isScrambled()) {
+ return;
+ }
+
+ size_t neededSize = (mScrambledBuffer == NULL ? 0 : mScrambledBuffer->size()) + size;
+ if (mScrambledBuffer == NULL || neededSize > mScrambledBuffer->capacity()) {
+ neededSize = (neededSize + 65535) & ~65535;
+
+ ALOGI("resizing scrambled buffer to size %zu", neededSize);
+
+ sp<ABuffer> buffer = new ABuffer(neededSize);
+ if (mScrambledBuffer != NULL) {
+ memcpy(buffer->data(), mScrambledBuffer->data(), mScrambledBuffer->size());
+ buffer->setRange(0, mScrambledBuffer->size());
+ } else {
+ buffer->setRange(0, 0);
+ }
+
+ mScrambledBuffer = buffer;
+ }
+ memcpy(mScrambledBuffer->data() + mScrambledBuffer->size(), data, size);
+ mScrambledBuffer->setRange(0, mScrambledBuffer->size() + size);
+
+ ScrambledRangeInfo scrambledInfo;
+ scrambledInfo.mLength = size;
+ scrambledInfo.mKeyId = keyId;
+ scrambledInfo.mIsSync = isSync;
+ scrambledInfo.mClearSizes = clearSizes;
+ scrambledInfo.mEncSizes = encSizes;
+
+ ALOGV("[stream %d] appending scrambled range: size=%zu", mMode, size);
+
+ mScrambledRangeInfos.push_back(scrambledInfo);
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() {
+ size_t nextScan = mBuffer->size();
+ mBuffer->setRange(0, 0);
+ int32_t pesOffset = 0, pesScramblingControl = 0;
+ int64_t timeUs = fetchTimestamp(nextScan, &pesOffset, &pesScramblingControl);
+ if (timeUs < 0ll) {
+ ALOGE("Negative timeUs");
+ return NULL;
+ }
+
+ // return scrambled unit
+ int32_t keyId = pesScramblingControl, isSync = 0, scrambledLength = 0;
+ sp<ABuffer> clearSizes, encSizes;
+ while (mScrambledRangeInfos.size() > mRangeInfos.size()) {
+ auto it = mScrambledRangeInfos.begin();
+ ALOGV("[stream %d] fetching scrambled range: size=%zu", mMode, it->mLength);
+
+ if (scrambledLength > 0) {
+ // This shouldn't happen since we always dequeue the entire PES.
+ ALOGW("Discarding srambled length %d", scrambledLength);
+ }
+ scrambledLength = it->mLength;
+
+ // TODO: handle key id change, use first non-zero keyId for now
+ if (keyId == 0) {
+ keyId = it->mKeyId;
+ }
+ clearSizes = it->mClearSizes;
+ encSizes = it->mEncSizes;
+ isSync = it->mIsSync;
+ mScrambledRangeInfos.erase(it);
+ }
+ if (scrambledLength == 0) {
+ ALOGE("[stream %d] empty scrambled unit!", mMode);
+ return NULL;
+ }
+
+ // skip the PES header, and copy the rest into scrambled access unit
+ sp<ABuffer> scrambledAccessUnit = ABuffer::CreateAsCopy(
+ mScrambledBuffer->data() + pesOffset,
+ scrambledLength - pesOffset);
+
+ // fix up first sample size after skipping the PES header
+ if (pesOffset > 0) {
+ int32_t &firstClearSize = *(int32_t*)clearSizes->data();
+ int32_t &firstEncSize = *(int32_t*)encSizes->data();
+ // Cut away the PES header
+ if (firstClearSize >= pesOffset) {
+ // This is for TS-level scrambling, we descrambled the first
+ // (or it was clear to begin with)
+ firstClearSize -= pesOffset;
+ } else if (firstEncSize >= pesOffset) {
+ // This can only be PES-level scrambling
+ firstEncSize -= pesOffset;
+ }
+ }
+
+ scrambledAccessUnit->meta()->setInt64("timeUs", timeUs);
+ if (isSync) {
+ scrambledAccessUnit->meta()->setInt32("isSync", 1);
+ }
+
+ // fill in CryptoInfo fields for AnotherPacketSource::read()
+ // MediaCas doesn't use cryptoMode, but set to non-zero value here.
+ scrambledAccessUnit->meta()->setInt32(
+ "cryptoMode", CryptoPlugin::kMode_AES_CBC);
+ scrambledAccessUnit->meta()->setInt32("cryptoKey", keyId);
+ scrambledAccessUnit->meta()->setBuffer("clearBytes", clearSizes);
+ scrambledAccessUnit->meta()->setBuffer("encBytes", encSizes);
+
+ memmove(mScrambledBuffer->data(),
+ mScrambledBuffer->data() + scrambledLength,
+ mScrambledBuffer->size() - scrambledLength);
+
+ mScrambledBuffer->setRange(0, mScrambledBuffer->size() - scrambledLength);
+
+ ALOGV("[stream %d] dequeued scrambled AU: timeUs=%lld, size=%zu",
+ mMode, (long long)timeUs, scrambledAccessUnit->size());
+
+ return scrambledAccessUnit;
+}
+
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
- if ((mFlags & kFlag_AlignedData) && mMode == H264) {
+ if ((mFlags & kFlag_AlignedData) && mMode == H264 && !isScrambled()) {
if (mRangeInfos.empty()) {
return NULL;
}
@@ -751,7 +882,8 @@
return accessUnit;
}
-int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) {
+int64_t ElementaryStreamQueue::fetchTimestamp(
+ size_t size, int32_t *pesOffset, int32_t *pesScramblingControl) {
int64_t timeUs = -1;
bool first = true;
@@ -764,6 +896,12 @@
if (first) {
timeUs = info->mTimestampUs;
+ if (pesOffset != NULL) {
+ *pesOffset = info->mPesOffset;
+ }
+ if (pesScramblingControl != NULL) {
+ *pesScramblingControl = info->mPesScramblingControl;
+ }
first = false;
}
@@ -787,6 +925,25 @@
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
+ if (isScrambled()) {
+ if (mBuffer == NULL || mBuffer->size() == 0) {
+ return NULL;
+ }
+ if (mFormat == NULL) {
+ mFormat = MakeAVCCodecSpecificData(mBuffer);
+ if (mFormat == NULL) {
+ ALOGI("Creating dummy AVC format for scrambled content");
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ mFormat->setInt32(kKeyWidth, 1280);
+ mFormat->setInt32(kKeyHeight, 720);
+ }
+ // for DrmInitData
+ mFormat->setData(kKeyCas, 0, mCasSessionId.data(), mCasSessionId.size());
+ }
+ return dequeueScrambledAccessUnit();
+ }
+
const uint8_t *data = mBuffer->data();
size_t size = mBuffer->size();
@@ -1045,6 +1202,23 @@
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() {
+ if (isScrambled()) {
+ if (mBuffer == NULL || mBuffer->size() == 0) {
+ return NULL;
+ }
+ if (mFormat == NULL) {
+ ALOGI("Creating dummy MPEG format for scrambled content");
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+ mFormat->setInt32(kKeyWidth, 1280);
+ mFormat->setInt32(kKeyHeight, 720);
+
+ // for DrmInitData
+ mFormat->setData(kKeyCas, 0, mCasSessionId.data(), mCasSessionId.size());
+ }
+ return dequeueScrambledAccessUnit();
+ }
+
const uint8_t *data = mBuffer->data();
size_t size = mBuffer->size();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 56f0706..6941e3f 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -22,6 +22,7 @@
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/RefBase.h>
+#include <vector>
namespace android {
@@ -30,6 +31,7 @@
struct ElementaryStreamQueue {
enum Mode {
+ INVALID = 0,
H264,
AAC,
AC3,
@@ -43,10 +45,19 @@
enum Flags {
// Data appended to the queue is always at access unit boundaries.
kFlag_AlignedData = 1,
+ kFlag_ScrambledData = 2,
};
explicit ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
- status_t appendData(const void *data, size_t size, int64_t timeUs);
+ status_t appendData(const void *data, size_t size,
+ int64_t timeUs, int32_t payloadOffset = 0,
+ uint32_t pesScramblingControl = 0);
+
+ void appendScrambledData(
+ const void *data, size_t size,
+ int32_t keyId, bool isSync,
+ sp<ABuffer> clearSizes, sp<ABuffer> encSizes);
+
void signalEOS();
void clear(bool clearFormat);
@@ -54,10 +65,29 @@
sp<MetaData> getFormat();
+ bool isScrambled() {
+ return (mFlags & kFlag_ScrambledData) != 0;
+ }
+
+ void setCasSession(const std::vector<uint8_t> &sessionId) {
+ mCasSessionId = sessionId;
+ }
+
private:
struct RangeInfo {
int64_t mTimestampUs;
size_t mLength;
+ int32_t mPesOffset;
+ uint32_t mPesScramblingControl;
+ };
+
+ struct ScrambledRangeInfo {
+ //int64_t mTimestampUs;
+ size_t mLength;
+ int32_t mKeyId;
+ int32_t mIsSync;
+ sp<ABuffer> mClearSizes;
+ sp<ABuffer> mEncSizes;
};
Mode mMode;
@@ -67,6 +97,10 @@
sp<ABuffer> mBuffer;
List<RangeInfo> mRangeInfos;
+ sp<ABuffer> mScrambledBuffer;
+ List<ScrambledRangeInfo> mScrambledRangeInfos;
+ std::vector<uint8_t> mCasSessionId;
+
sp<MetaData> mFormat;
sp<ABuffer> dequeueAccessUnitH264();
@@ -80,7 +114,11 @@
// consume a logical (compressed) access unit of size "size",
// returns its timestamp in us (or -1 if no time information).
- int64_t fetchTimestamp(size_t size);
+ int64_t fetchTimestamp(size_t size,
+ int32_t *pesOffset = NULL,
+ int32_t *pesScramblingControl = NULL);
+
+ sp<ABuffer> dequeueScrambledAccessUnit();
DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
};
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index bde33dc..c3f1274 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -148,12 +148,46 @@
return meta;
}
+//static
+bool MPEG2TSExtractor::isScrambledFormat(const sp<MetaData> &format) {
+ const char *mime;
+ return format->findCString(kKeyMIMEType, &mime)
+ && (!strcasecmp(MEDIA_MIMETYPE_VIDEO_SCRAMBLED, mime)
+ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_SCRAMBLED, mime));
+}
+
+status_t MPEG2TSExtractor::setMediaCas(const sp<ICas> &cas) {
+ ALOGD("setMediaCas: %p", cas.get());
+
+ status_t err = mParser->setMediaCas(cas);
+ if (err == OK) {
+ ALOGI("All tracks now have descramblers");
+ init();
+ }
+ return err;
+}
+
+void MPEG2TSExtractor::addSource(const sp<AnotherPacketSource> &impl) {
+ bool found = false;
+ for (size_t i = 0; i < mSourceImpls.size(); i++) {
+ if (mSourceImpls[i] == impl) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ mSourceImpls.push(impl);
+ }
+}
+
void MPEG2TSExtractor::init() {
bool haveAudio = false;
bool haveVideo = false;
int64_t startTime = ALooper::GetNowUs();
- while (feedMore(true /* isInit */) == OK) {
+ status_t err;
+ while ((err = feedMore(true /* isInit */)) == OK
+ || err == ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED) {
if (haveAudio && haveVideo) {
addSyncPoint_l(mLastSyncEvent);
mLastSyncEvent.reset();
@@ -165,10 +199,15 @@
ATSParser::VIDEO).get();
if (impl != NULL) {
- haveVideo = true;
- mSourceImpls.push(impl);
- mSyncPoints.push();
- mSeekSyncPoints = &mSyncPoints.editTop();
+ sp<MetaData> format = impl->getFormat();
+ if (format != NULL) {
+ haveVideo = true;
+ addSource(impl);
+ if (!isScrambledFormat(format)) {
+ mSyncPoints.push();
+ mSeekSyncPoints = &mSyncPoints.editTop();
+ }
+ }
}
}
@@ -178,11 +217,16 @@
ATSParser::AUDIO).get();
if (impl != NULL) {
- haveAudio = true;
- mSourceImpls.push(impl);
- mSyncPoints.push();
- if (!haveVideo) {
- mSeekSyncPoints = &mSyncPoints.editTop();
+ sp<MetaData> format = impl->getFormat();
+ if (format != NULL) {
+ haveAudio = true;
+ addSource(impl);
+ if (!isScrambledFormat(format)) {
+ mSyncPoints.push();
+ if (!haveVideo) {
+ mSeekSyncPoints = &mSyncPoints.editTop();
+ }
+ }
}
}
}
@@ -190,6 +234,16 @@
addSyncPoint_l(mLastSyncEvent);
mLastSyncEvent.reset();
+ // ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED is returned when the mpeg2ts
+ // is scrambled but we don't have a MediaCas object set. The extraction
+ // will only continue when setMediaCas() is called successfully.
+ if (err == ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED) {
+ ALOGI("stopped parsing scrambled content, "
+ "haveAudio=%d, haveVideo=%d, elaspedTime=%" PRId64,
+ haveAudio, haveVideo, ALooper::GetNowUs() - startTime);
+ return;
+ }
+
// Wait only for 2 seconds to detect audio/video streams.
if (ALooper::GetNowUs() - startTime > 2000000ll) {
break;
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 0b0facf..70ae46b 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -5,7 +5,8 @@
TextDescriptions.cpp \
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
-LOCAL_SANITIZE := signed-integer-overflow
+LOCAL_SANITIZE := signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/include/media/stagefright/timedtext \
diff --git a/media/libstagefright/webm/Android.mk b/media/libstagefright/webm/Android.mk
index 096fd07..0d55de9 100644
--- a/media/libstagefright/webm/Android.mk
+++ b/media/libstagefright/webm/Android.mk
@@ -4,7 +4,8 @@
LOCAL_CPPFLAGS += -D__STDINT_LIMITS
LOCAL_CFLAGS += -Werror -Wall
-LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
+LOCAL_SANITIZE_DIAG := cfi
LOCAL_SRC_FILES:= EbmlUtil.cpp \
WebmElement.cpp \
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index 82a2627..0bf7854 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -277,19 +277,20 @@
}
void MtpDevice::print() {
- if (mDeviceInfo) {
- mDeviceInfo->print();
+ if (!mDeviceInfo)
+ return;
- if (mDeviceInfo->mDeviceProperties) {
- ALOGI("***** DEVICE PROPERTIES *****\n");
- int count = mDeviceInfo->mDeviceProperties->size();
- for (int i = 0; i < count; i++) {
- MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
- MtpProperty* property = getDevicePropDesc(propCode);
- if (property) {
- property->print();
- delete property;
- }
+ mDeviceInfo->print();
+
+ if (mDeviceInfo->mDeviceProperties) {
+ ALOGI("***** DEVICE PROPERTIES *****\n");
+ int count = mDeviceInfo->mDeviceProperties->size();
+ for (int i = 0; i < count; i++) {
+ MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+ MtpProperty* property = getDevicePropDesc(propCode);
+ if (property) {
+ property->print();
+ delete property;
}
}
}
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 1262746..b4029c7 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1605,7 +1605,8 @@
// never process effects when:
// - on an OFFLOAD thread
// - no more tracks are on the session and the effect tail has been rendered
- bool doProcess = (thread->type() != ThreadBase::OFFLOAD);
+ bool doProcess = (thread->type() != ThreadBase::OFFLOAD)
+ && (thread->type() != ThreadBase::MMAP);
if (!isGlobalSession) {
bool tracksOnSession = (trackCnt() != 0);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index e710f0a..79e7ff0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -507,6 +507,14 @@
return res;
if (!isStreamInfoValid) {
+ // Streaming sharing is only supported for IMPLEMENTATION_DEFINED
+ // formats.
+ if (isShared && streamInfo.format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ String8 msg = String8::format("Camera %s: Stream sharing is only supported for "
+ "IMPLEMENTATION_DEFINED format", mCameraIdStr.string());
+ ALOGW("%s: %s", __FUNCTION__, msg.string());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+ }
isStreamInfoValid = true;
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index c40809f..f6ad7d7 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -54,9 +54,6 @@
mListener = listener;
mServiceProxy = proxy;
- // See if there's a passthrough HAL, but let's not complain if there's not
- addProvider(kLegacyProviderName, /*expected*/ false);
-
// Registering will trigger notifications for all already-known providers
bool success = mServiceProxy->registerForNotifications(
/* instance name, empty means no filter */ "",
@@ -67,6 +64,9 @@
return INVALID_OPERATION;
}
+ // See if there's a passthrough HAL, but let's not complain if there's not
+ addProvider(kLegacyProviderName, /*expected*/ false);
+
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 068a2b3..f20556d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -3956,7 +3956,8 @@
}
}
- res = outputStream->getBuffer(&outputBuffers->editItemAt(i));
+ res = outputStream->getBuffer(&outputBuffers->editItemAt(i),
+ captureRequest->mOutputSurfaces[i]);
if (res != OK) {
// Can't get output buffer from gralloc queue - this could be due to
// abandoned queue or other consumer misbehavior, so not a fatal
@@ -3968,13 +3969,6 @@
}
halRequest->num_output_buffers++;
- res = outputStream->notifyRequestedSurfaces(halRequest->frame_number,
- captureRequest->mOutputSurfaces[i]);
- if (res != OK) {
- ALOGE("RequestThread: Cannot register output surfaces: %s (%d)",
- strerror(-res), res);
- return INVALID_OPERATION;
- }
}
totalNumBuffers += halRequest->num_output_buffers;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 1a730d6..9c951b7 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -36,7 +36,8 @@
}
-status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *,
+ const std::vector<size_t>&) {
ATRACE_CALL();
ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
@@ -83,14 +84,6 @@
return OK;
}
-status_t Camera3DummyStream::notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids) {
- (void) frame_number;
- (void) surface_ids;
- // Do nothing
- return OK;
-}
-
status_t Camera3DummyStream::configureQueueLocked() {
// Do nothing
return OK;
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index b6ec99c..35a6a18 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -56,9 +56,6 @@
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
-
/**
* Return if this output stream is for video encoding.
*/
@@ -102,7 +99,8 @@
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index f971116..51dc20a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -148,73 +148,17 @@
disconnectLocked();
}
-status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) {
+status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>&) {
ATRACE_CALL();
- status_t res;
-
- if ((res = getBufferPreconditionCheckLocked()) != OK) {
- return res;
- }
ANativeWindowBuffer* anb;
int fenceFd = -1;
- bool gotBufferFromManager = false;
- if (mUseBufferManager) {
- sp<GraphicBuffer> gb;
- res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd);
- if (res == OK) {
- // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
- // successful return.
- anb = gb.get();
- res = mConsumer->attachBuffer(anb);
- if (res != OK) {
- ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
- gotBufferFromManager = true;
- ALOGV("Stream %d: Attached new buffer", getId());
- } else if (res == ALREADY_EXISTS) {
- // Have sufficient free buffers already attached, can just
- // dequeue from buffer queue
- ALOGV("Stream %d: Reusing attached buffer", getId());
- gotBufferFromManager = false;
- } else if (res != OK) {
- ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
- }
- if (!gotBufferFromManager) {
- /**
- * Release the lock briefly to avoid deadlock for below scenario:
- * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
- * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
- * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
- * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
- * StreamingProcessor lock.
- * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
- * and try to lock bufferQueue lock.
- * Then there is circular locking dependency.
- */
- sp<ANativeWindow> currentConsumer = mConsumer;
- mLock.unlock();
-
- res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
- mLock.lock();
- if (res != OK) {
- ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
-
- // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
- // let prepareNextBuffer handle the error.)
- if (res == NO_INIT && mState == STATE_CONFIGURED) {
- mState = STATE_ABANDONED;
- }
-
- return res;
- }
+ status_t res;
+ res = getBufferLockedCommon(&anb, &fenceFd);
+ if (res != OK) {
+ return res;
}
/**
@@ -227,6 +171,11 @@
return OK;
}
+status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence) {
+ return consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+}
+
status_t Camera3OutputStream::returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp) {
@@ -269,6 +218,7 @@
sp<ANativeWindow> currentConsumer = mConsumer;
mLock.unlock();
+ ANativeWindowBuffer *anwBuffer = container_of(buffer.buffer, ANativeWindowBuffer, handle);
/**
* Return buffer back to ANativeWindow
*/
@@ -276,13 +226,14 @@
// Cancel buffer
ALOGW("A frame is dropped for stream %d", mId);
res = currentConsumer->cancelBuffer(currentConsumer.get(),
- container_of(buffer.buffer, ANativeWindowBuffer, handle),
+ anwBuffer,
anwReleaseFence);
if (res != OK) {
ALOGE("%s: Stream %d: Error cancelling buffer to native window:"
" %s (%d)", __FUNCTION__, mId, strerror(-res), res);
}
+ notifyBufferReleased(anwBuffer);
if (mUseBufferManager) {
// Return this buffer back to buffer manager.
mBufferReleasedListener->onBufferReleased();
@@ -308,9 +259,7 @@
return res;
}
- res = currentConsumer->queueBuffer(currentConsumer.get(),
- container_of(buffer.buffer, ANativeWindowBuffer, handle),
- anwReleaseFence);
+ res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence);
if (res != OK) {
ALOGE("%s: Stream %d: Error queueing buffer to native window: "
"%s (%d)", __FUNCTION__, mId, strerror(-res), res);
@@ -527,6 +476,76 @@
return OK;
}
+status_t Camera3OutputStream::getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd) {
+ ATRACE_CALL();
+ status_t res;
+
+ if ((res = getBufferPreconditionCheckLocked()) != OK) {
+ return res;
+ }
+
+ bool gotBufferFromManager = false;
+
+ if (mUseBufferManager) {
+ sp<GraphicBuffer> gb;
+ res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, fenceFd);
+ if (res == OK) {
+ // Attach this buffer to the bufferQueue: the buffer will be in dequeue state after a
+ // successful return.
+ *anb = gb.get();
+ res = mConsumer->attachBuffer(*anb);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't attach the output buffer to this surface: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ gotBufferFromManager = true;
+ ALOGV("Stream %d: Attached new buffer", getId());
+ } else if (res == ALREADY_EXISTS) {
+ // Have sufficient free buffers already attached, can just
+ // dequeue from buffer queue
+ ALOGV("Stream %d: Reusing attached buffer", getId());
+ gotBufferFromManager = false;
+ } else if (res != OK) {
+ ALOGE("%s: Stream %d: Can't get next output buffer from buffer manager: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ }
+ if (!gotBufferFromManager) {
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ sp<ANativeWindow> currentConsumer = mConsumer;
+ mLock.unlock();
+
+ res = currentConsumer->dequeueBuffer(currentConsumer.get(), anb, fenceFd);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+
+ // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+ // let prepareNextBuffer handle the error.)
+ if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ mState = STATE_ABANDONED;
+ }
+
+ return res;
+ }
+ }
+
+ return res;
+}
+
status_t Camera3OutputStream::disconnectLocked() {
status_t res;
@@ -702,8 +721,7 @@
return OK;
}
-status_t Camera3OutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
- const std::vector<size_t>& /*surface_ids*/) {
+status_t Camera3OutputStream::notifyBufferReleased(ANativeWindowBuffer* /*anwBuffer*/) {
return OK;
}
@@ -717,6 +735,7 @@
}
status_t Camera3OutputStream::setConsumers(const std::vector<sp<Surface>>& consumers) {
+ Mutex::Autolock l(mLock);
if (consumers.size() != 1) {
ALOGE("%s: it's illegal to set %zu consumer surfaces!",
__FUNCTION__, consumers.size());
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 080c721..24e4e05 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -158,8 +158,11 @@
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd);
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
+ /**
+ * Notify that the buffer is being released to the buffer queue instead of
+ * being queued to the consumer.
+ */
+ virtual status_t notifyBufferReleased(ANativeWindowBuffer *anwBuffer);
/**
* Set the graphic buffer manager to get/return the stream buffers.
@@ -198,6 +201,9 @@
static const nsecs_t kDequeueBufferTimeout = 1000000000; // 1 sec
+ status_t getBufferLockedCommon(ANativeWindowBuffer** anb, int* fenceFd);
+
+
private:
int mTransform;
@@ -243,11 +249,16 @@
/**
* Internal Camera3Stream interface
*/
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids);
+
virtual status_t returnBufferLocked(
const camera3_stream_buffer &buffer,
nsecs_t timestamp);
+ virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence);
+
virtual status_t configureQueueLocked();
virtual status_t getEndpointUsage(uint32_t *usage) const;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 11868e7..8107dd0 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -59,20 +59,6 @@
*
*/
virtual status_t detachBuffer(sp<GraphicBuffer>* buffer, int* fenceFd) = 0;
-
- /**
- * Notify which surfaces are requested for a particular frame number.
- *
- * Mulitple surfaces could share the same output stream, but a request may
- * be only for a subset of surfaces. In this case, the
- * Camera3OutputStreamInterface object needs to manage the output surfaces on
- * a per request basis.
- *
- * If there is only one surface for this output stream, calling this
- * function is a no-op.
- */
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids) = 0;
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 0d6a96c..2ae5660 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -44,7 +44,7 @@
uint32_t usage;
getEndpointUsage(&usage);
- res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, mConsumer);
+ res = mStreamSplitter->connect(mSurfaces, usage, camera3_stream::max_buffers, &mConsumer);
if (res != OK) {
ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
__FUNCTION__, strerror(-res), res);
@@ -54,13 +54,13 @@
return res;
}
-status_t Camera3SharedOutputStream::notifyRequestedSurfaces(uint32_t /*frame_number*/,
- const std::vector<size_t>& surface_ids) {
+status_t Camera3SharedOutputStream::notifyBufferReleased(ANativeWindowBuffer *anwBuffer) {
Mutex::Autolock l(mLock);
status_t res = OK;
+ const sp<GraphicBuffer> buffer(static_cast<GraphicBuffer*>(anwBuffer));
if (mStreamSplitter != nullptr) {
- res = mStreamSplitter->notifyRequestedSurfaces(surface_ids);
+ res = mStreamSplitter->notifyBufferReleased(buffer);
}
return res;
@@ -72,6 +72,7 @@
}
status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
+ Mutex::Autolock l(mLock);
if (surfaces.size() == 0) {
ALOGE("%s: it's illegal to set zero consumer surfaces!", __FUNCTION__);
return INVALID_OPERATION;
@@ -88,7 +89,7 @@
// Only call addOutput if the splitter has been connected.
if (mStreamSplitter != nullptr) {
- ret = mStreamSplitter->addOutput(surface, camera3_stream::max_buffers);
+ ret = mStreamSplitter->addOutput(surface);
if (ret != OK) {
ALOGE("%s: addOutput failed with error code %d", __FUNCTION__, ret);
return ret;
@@ -99,6 +100,64 @@
return ret;
}
+status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids) {
+ ANativeWindowBuffer* anb;
+ int fenceFd = -1;
+
+ status_t res;
+ res = getBufferLockedCommon(&anb, &fenceFd);
+ if (res != OK) {
+ return res;
+ }
+
+ // Attach the buffer to the splitter output queues. This could block if
+ // the output queue doesn't have any empty slot. So unlock during the course
+ // of attachBufferToOutputs.
+ sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+ mLock.unlock();
+ res = splitter->attachBufferToOutputs(anb, surface_ids);
+ mLock.lock();
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+ // let prepareNextBuffer handle the error.)
+ if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ mState = STATE_ABANDONED;
+ }
+
+ return res;
+ }
+
+ /**
+ * FenceFD now owned by HAL except in case of error,
+ * in which case we reassign it to acquire_fence
+ */
+ handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd,
+ /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true);
+
+ return OK;
+}
+
+status_t Camera3SharedOutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence) {
+ status_t res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+
+ // After queuing buffer to the internal consumer queue, check whether the buffer is
+ // successfully queued to the output queues.
+ if (res == OK) {
+ res = mStreamSplitter->getOnFrameAvailableResult();
+ if (res != OK) {
+ ALOGE("%s: getOnFrameAvailable returns %d", __FUNCTION__, res);
+ }
+ } else {
+ ALOGE("%s: queueBufer failed %d", __FUNCTION__, res);
+ }
+
+ return res;
+}
+
status_t Camera3SharedOutputStream::configureQueueLocked() {
status_t res;
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index cc96076..7be0940 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -40,8 +40,7 @@
virtual ~Camera3SharedOutputStream();
- virtual status_t notifyRequestedSurfaces(uint32_t frame_number,
- const std::vector<size_t>& surface_ids);
+ virtual status_t notifyBufferReleased(ANativeWindowBuffer *buffer);
virtual bool isConsumerConfigurationDeferred(size_t surface_id) const;
@@ -62,6 +61,15 @@
*/
status_t connectStreamSplitterLocked();
+ /**
+ * Internal Camera3Stream interface
+ */
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids);
+
+ virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
+ ANativeWindowBuffer* buffer, int anwReleaseFence);
+
virtual status_t configureQueueLocked();
virtual status_t disconnectLocked();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index c3b7565..53a3168 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -443,7 +443,8 @@
return OK;
}
-status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {
+status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res = OK;
@@ -470,7 +471,7 @@
}
}
- res = getBufferLocked(buffer);
+ res = getBufferLocked(buffer, surface_ids);
if (res == OK) {
fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true);
if (buffer->buffer) {
@@ -745,7 +746,8 @@
return res;
}
-status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *) {
+status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
+ const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 471b393..56cb827 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -277,12 +277,18 @@
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to acquire
+ * buffers for those surfaces.
+ *
* This method may only be called once finishConfiguration has been called.
* For bidirectional streams, this method applies to the output-side
* buffers.
*
*/
- status_t getBuffer(camera3_stream_buffer *buffer);
+ status_t getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
/**
* Return a buffer to the stream after use by the HAL.
@@ -412,7 +418,8 @@
// cast to camera3_stream*, implementations must increment the
// refcount of the stream manually in getBufferLocked, and decrement it in
// returnBufferLocked.
- virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
+ virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>());
virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
nsecs_t timestamp);
virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index ceea08a..f7b092f 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -200,12 +200,19 @@
* Fill in the camera3_stream_buffer with the next valid buffer for this
* stream, to hand over to the HAL.
*
+ * Multiple surfaces could share the same HAL stream, but a request may
+ * be only for a subset of surfaces. In this case, the
+ * Camera3StreamInterface object needs the surface ID information to acquire
+ * buffers for those surfaces. For the case of single surface for a HAL
+ * stream, surface_ids parameter has no effect.
+ *
* This method may only be called once finishConfiguration has been called.
* For bidirectional streams, this method applies to the output-side
* buffers.
*
*/
- virtual status_t getBuffer(camera3_stream_buffer *buffer) = 0;
+ virtual status_t getBuffer(camera3_stream_buffer *buffer,
+ const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
/**
* Return a buffer to the stream after use by the HAL.
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index c9f43aa..deb6735 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -37,10 +37,10 @@
namespace android {
status_t Camera3StreamSplitter::connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t hal_max_buffers,
- sp<Surface>& consumer) {
- if (consumer != nullptr) {
- ALOGE("%s: output Surface is not NULL", __FUNCTION__);
+ uint32_t consumerUsage, size_t halMaxBuffers, sp<Surface>* consumer) {
+ ATRACE_CALL();
+ if (consumer == nullptr) {
+ SP_LOGE("%s: consumer pointer is NULL", __FUNCTION__);
return BAD_VALUE;
}
@@ -48,129 +48,147 @@
status_t res = OK;
if (mOutputs.size() > 0 || mConsumer != nullptr) {
- ALOGE("%s: StreamSplitter already connected", __FUNCTION__);
+ SP_LOGE("%s: already connected", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (mBuffers.size() > 0) {
+ SP_LOGE("%s: still has %zu pending buffers", __FUNCTION__, mBuffers.size());
return BAD_VALUE;
}
+ mMaxHalBuffers = halMaxBuffers;
+ mConsumerName = getUniqueConsumerName();
// Add output surfaces. This has to be before creating internal buffer queue
// in order to get max consumer side buffers.
for (size_t i = 0; i < surfaces.size(); i++) {
if (surfaces[i] == nullptr) {
- ALOGE("%s: Fatal: surface is NULL", __FUNCTION__);
+ SP_LOGE("%s: Fatal: surface is NULL", __FUNCTION__);
return BAD_VALUE;
}
- res = addOutputLocked(surfaces[i], hal_max_buffers, OutputType::NonDeferred);
+ res = addOutputLocked(surfaces[i]);
if (res != OK) {
- ALOGE("%s: Failed to add output surface: %s(%d)",
+ SP_LOGE("%s: Failed to add output surface: %s(%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
}
- // Create buffer queue for input
+ // Create BufferQueue for input
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ // Allocate 1 extra buffer to handle the case where all buffers are detached
+ // from input, and attached to the outputs. In this case, the input queue's
+ // dequeueBuffer can still allocate 1 extra buffer before being blocked by
+ // the output's attachBuffer().
mBufferItemConsumer = new BufferItemConsumer(mConsumer, consumerUsage,
- mMaxConsumerBuffers);
+ mMaxConsumerBuffers+1);
if (mBufferItemConsumer == nullptr) {
return NO_MEMORY;
}
- mConsumer->setConsumerName(getUniqueConsumerName());
+ mConsumer->setConsumerName(mConsumerName);
- mSurface = new Surface(mProducer);
- if (mSurface == nullptr) {
+ *consumer = new Surface(mProducer);
+ if (*consumer == nullptr) {
return NO_MEMORY;
}
- consumer = mSurface;
res = mConsumer->consumerConnect(this, /* controlledByApp */ false);
+ SP_LOGV("%s: connected", __FUNCTION__);
return res;
}
+status_t Camera3StreamSplitter::getOnFrameAvailableResult() {
+ ATRACE_CALL();
+ return mOnFrameAvailableRes.load();
+}
+
void Camera3StreamSplitter::disconnect() {
+ ATRACE_CALL();
Mutex::Autolock lock(mMutex);
+ for (auto& notifier : mNotifiers) {
+ sp<IGraphicBufferProducer> producer = notifier.first;
+ sp<OutputListener> listener = notifier.second;
+ IInterface::asBinder(producer)->unlinkToDeath(listener);
+ }
+ mNotifiers.clear();
+
for (auto& output : mOutputs) {
output->disconnect(NATIVE_WINDOW_API_CAMERA);
}
mOutputs.clear();
+ mOutputSlots.clear();
- if (mConsumer != nullptr) {
- mConsumer->consumerDisconnect();
- mConsumer.clear();
- }
+ mConsumer->consumerDisconnect();
if (mBuffers.size() > 0) {
- ALOGI("%zu buffers still being tracked", mBuffers.size());
+ SP_LOGW("%zu buffers still being tracked", mBuffers.size());
+ mBuffers.clear();
}
+
+ mMaxHalBuffers = 0;
+ mMaxConsumerBuffers = 0;
+ SP_LOGV("%s: Disconnected", __FUNCTION__);
}
+
Camera3StreamSplitter::~Camera3StreamSplitter() {
disconnect();
}
-status_t Camera3StreamSplitter::addOutput(
- const sp<Surface>& outputQueue, size_t hal_max_buffers) {
+status_t Camera3StreamSplitter::addOutput(const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- return addOutputLocked(outputQueue, hal_max_buffers, OutputType::Deferred);
+ status_t res = addOutputLocked(outputQueue);
+
+ if (res != OK) {
+ SP_LOGE("%s: addOutputLocked failed %d", __FUNCTION__, res);
+ return res;
+ }
+
+ res = mConsumer->setMaxAcquiredBufferCount(mMaxConsumerBuffers+1);
+
+ return res;
}
-status_t Camera3StreamSplitter::addOutputLocked(
- const sp<Surface>& outputQueue, size_t hal_max_buffers,
- OutputType outputType) {
+status_t Camera3StreamSplitter::addOutputLocked(const sp<Surface>& outputQueue) {
+ ATRACE_CALL();
if (outputQueue == nullptr) {
- ALOGE("addOutput: outputQueue must not be NULL");
- return BAD_VALUE;
- }
- if (hal_max_buffers < 1) {
- ALOGE("%s: Camera HAL requested max_buffer count: %zu, requires at least 1",
- __FUNCTION__, hal_max_buffers);
+ SP_LOGE("addOutput: outputQueue must not be NULL");
return BAD_VALUE;
}
sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
// Connect to the buffer producer
- IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
sp<OutputListener> listener(new OutputListener(this, gbp));
IInterface::asBinder(gbp)->linkToDeath(listener);
- status_t status = gbp->connect(listener, NATIVE_WINDOW_API_CAMERA,
- /* producerControlledByApp */ true, &queueBufferOutput);
- if (status != NO_ERROR) {
- ALOGE("addOutput: failed to connect (%d)", status);
- return status;
+ status_t res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
+ if (res != NO_ERROR) {
+ SP_LOGE("addOutput: failed to connect (%d)", res);
+ return res;
}
// Query consumer side buffer count, and update overall buffer count
int maxConsumerBuffers = 0;
- status = static_cast<ANativeWindow*>(outputQueue.get())->query(
+ res = static_cast<ANativeWindow*>(outputQueue.get())->query(
outputQueue.get(),
NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
- if (status != OK) {
- ALOGE("%s: Unable to query consumer undequeued buffer count"
+ if (res != OK) {
+ SP_LOGE("%s: Unable to query consumer undequeued buffer count"
" for surface", __FUNCTION__);
- return status;
+ return res;
}
- if (maxConsumerBuffers > mMaxConsumerBuffers) {
- if (outputType == OutputType::Deferred) {
- ALOGE("%s: Fatal: Deferred surface has higher consumer buffer count"
- " %d than what's already configured %d", __FUNCTION__,
- maxConsumerBuffers, mMaxConsumerBuffers);
- return BAD_VALUE;
- }
- mMaxConsumerBuffers = maxConsumerBuffers;
- }
-
- ALOGV("%s: Consumer wants %d buffers, HAL wants %zu", __FUNCTION__,
- maxConsumerBuffers, hal_max_buffers);
- size_t totalBufferCount = maxConsumerBuffers + hal_max_buffers;
- status = native_window_set_buffer_count(outputQueue.get(),
+ SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
+ maxConsumerBuffers, mMaxHalBuffers);
+ size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+ res = native_window_set_buffer_count(outputQueue.get(),
totalBufferCount);
- if (status != OK) {
- ALOGE("%s: Unable to set buffer count for surface %p",
+ if (res != OK) {
+ SP_LOGE("%s: Unable to set buffer count for surface %p",
__FUNCTION__, outputQueue.get());
- return status;
+ return res;
}
// Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
@@ -183,157 +201,239 @@
outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
}
- status = gbp->allowAllocation(false);
- if (status != OK) {
- ALOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
- return status;
+ res = gbp->allowAllocation(false);
+ if (res != OK) {
+ SP_LOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
+ return res;
}
// Add new entry into mOutputs
mOutputs.push_back(gbp);
+ mNotifiers[gbp] = listener;
+ mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
+
+ mMaxConsumerBuffers += maxConsumerBuffers;
return NO_ERROR;
}
+status_t Camera3StreamSplitter::outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+ const BufferItem& bufferItem) {
+ ATRACE_CALL();
+ status_t res;
+ IGraphicBufferProducer::QueueBufferInput queueInput(
+ bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
+ bufferItem.mDataSpace, bufferItem.mCrop,
+ static_cast<int32_t>(bufferItem.mScalingMode),
+ bufferItem.mTransform, bufferItem.mFence);
+
+ IGraphicBufferProducer::QueueBufferOutput queueOutput;
+
+ uint64_t bufferId = bufferItem.mGraphicBuffer->getId();
+ const BufferTracker& tracker = *(mBuffers[bufferId]);
+ int slot = getSlotForOutputLocked(output, tracker.getBuffer());
+
+ // In case the output BufferQueue has its own lock, if we hold splitter lock while calling
+ // queueBuffer (which will try to acquire the output lock), the output could be holding its
+ // own lock calling releaseBuffer (which will try to acquire the splitter lock), running into
+ // circular lock situation.
+ mMutex.unlock();
+ res = output->queueBuffer(slot, queueInput, &queueOutput);
+ mMutex.lock();
+
+ SP_LOGV("%s: Queuing buffer to buffer queue %p slot %d returns %d",
+ __FUNCTION__, output.get(), slot, res);
+ if (res != OK) {
+ if (res != NO_INIT && res != DEAD_OBJECT) {
+ SP_LOGE("Queuing buffer to output failed (%d)", res);
+ }
+ // If we just discovered that this output has been abandoned, note
+ // that, increment the release count so that we still release this
+ // buffer eventually, and move on to the next output
+ onAbandonedLocked();
+ decrementBufRefCountLocked(bufferItem.mGraphicBuffer->getId(), output);
+ return res;
+ }
+
+ // If the queued buffer replaces a pending buffer in the async
+ // queue, no onBufferReleased is called by the buffer queue.
+ // Proactively trigger the callback to avoid buffer loss.
+ if (queueOutput.bufferReplaced) {
+ onBufferReleasedByOutputLocked(output);
+ }
+
+ return res;
+}
+
String8 Camera3StreamSplitter::getUniqueConsumerName() {
static volatile int32_t counter = 0;
return String8::format("Camera3StreamSplitter-%d", android_atomic_inc(&counter));
}
-status_t Camera3StreamSplitter::notifyRequestedSurfaces(
- const std::vector<size_t>& surfaces) {
+status_t Camera3StreamSplitter::notifyBufferReleased(const sp<GraphicBuffer>& buffer) {
ATRACE_CALL();
+ status_t res = OK;
+
Mutex::Autolock lock(mMutex);
- mRequestedSurfaces.push_back(surfaces);
- return OK;
-}
+ uint64_t bufferId = buffer->getId();
+ std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[bufferId]);
+ mBuffers.erase(bufferId);
-
-void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /* item */) {
- ATRACE_CALL();
- Mutex::Autolock lock(mMutex);
-
- // The current policy is that if any one consumer is consuming buffers too
- // slowly, the splitter will stall the rest of the outputs by not acquiring
- // any more buffers from the input. This will cause back pressure on the
- // input queue, slowing down its producer.
-
- // If there are too many outstanding buffers, we block until a buffer is
- // released back to the input in onBufferReleased
- while (mOutstandingBuffers >= mMaxConsumerBuffers) {
- mReleaseCondition.wait(mMutex);
-
- // If the splitter is abandoned while we are waiting, the release
- // condition variable will be broadcast, and we should just return
- // without attempting to do anything more (since the input queue will
- // also be abandoned).
- if (mIsAbandoned) {
- return;
+ for (const auto surface : tracker_ptr->requestedSurfaces()) {
+ sp<IGraphicBufferProducer>& gbp = mOutputs[surface];
+ OutputSlots& outputSlots = *(mOutputSlots[gbp]);
+ int slot = getSlotForOutputLocked(gbp, buffer);
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ gbp->detachBuffer(slot);
+ outputSlots[slot].clear();
}
}
- // If the splitter is abandoned without reaching mMaxConsumerBuffers, just
- // return without attempting to do anything more.
- if (mIsAbandoned) {
- return;
+
+ return res;
+}
+
+status_t Camera3StreamSplitter::attachBufferToOutputs(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids) {
+ ATRACE_CALL();
+ status_t res = OK;
+
+ Mutex::Autolock lock(mMutex);
+
+ sp<GraphicBuffer> gb(static_cast<GraphicBuffer*>(anb));
+ uint64_t bufferId = gb->getId();
+
+ // Initialize buffer tracker for this input buffer
+ auto tracker = std::make_unique<BufferTracker>(gb, surface_ids);
+
+ for (auto& surface_id : surface_ids) {
+ sp<IGraphicBufferProducer>& gbp = mOutputs[surface_id];
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ //Temporarly Unlock the mutex when trying to attachBuffer to the output
+ //queue, because attachBuffer could block in case of a slow consumer. If
+ //we block while holding the lock, onFrameAvailable and onBufferReleased
+ //will block as well because they need to acquire the same lock.
+ mMutex.unlock();
+ res = gbp->attachBuffer(&slot, gb);
+ mMutex.lock();
+ if (res != OK) {
+ SP_LOGE("%s: Cannot acquireBuffer from GraphicBufferProducer %p: %s (%d)",
+ __FUNCTION__, gbp.get(), strerror(-res), res);
+ return res;
+ }
+ auto& outputSlots = *mOutputSlots[gbp];
+ if (outputSlots[slot] != nullptr) {
+ // If the buffer is attached to a slot which already contains a buffer,
+ // the previous buffer will be removed from the output queue. Decrement
+ // the reference count accordingly.
+ decrementBufRefCountLocked(outputSlots[slot]->getId(), gbp);
+ }
+ SP_LOGV("%s: Attached buffer %p to slot %d on output %p.",__FUNCTION__, gb.get(),
+ slot, gbp.get());
+ outputSlots[slot] = gb;
}
- ++mOutstandingBuffers;
+ mBuffers[bufferId] = std::move(tracker);
+
+ return res;
+}
+
+void Camera3StreamSplitter::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
// Acquire and detach the buffer from the input
BufferItem bufferItem;
- status_t status = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "acquiring buffer from input failed (%d)", status);
+ status_t res = mConsumer->acquireBuffer(&bufferItem, /* presentWhen */ 0);
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: Acquiring buffer from input failed (%d)", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ return;
+ }
+ if (mBuffers.find(bufferItem.mGraphicBuffer->getId()) == mBuffers.end()) {
+ SP_LOGE("%s: Acquired buffer doesn't exist in attached buffer map",
+ __FUNCTION__);
+ mOnFrameAvailableRes.store(INVALID_OPERATION);
+ return;
+ }
- ALOGV("acquired buffer %#" PRIx64 " from input",
- bufferItem.mGraphicBuffer->getId());
+ SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
+ bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
- status = mConsumer->detachBuffer(bufferItem.mSlot);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "detaching buffer from input failed (%d)", status);
-
- IGraphicBufferProducer::QueueBufferInput queueInput(
- bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
- bufferItem.mDataSpace, bufferItem.mCrop,
- static_cast<int32_t>(bufferItem.mScalingMode),
- bufferItem.mTransform, bufferItem.mFence);
+ res = mConsumer->detachBuffer(bufferItem.mSlot);
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: detaching buffer from input failed (%d)", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ return;
+ }
// Attach and queue the buffer to each of the outputs
- std::vector<std::vector<size_t> >::iterator surfaces = mRequestedSurfaces.begin();
- if (surfaces != mRequestedSurfaces.end()) {
+ BufferTracker& tracker = *(mBuffers[bufferItem.mGraphicBuffer->getId()]);
- LOG_ALWAYS_FATAL_IF(surfaces->size() == 0,
- "requested surface ids shouldn't be empty");
+ SP_LOGV("%s: BufferTracker for buffer %" PRId64 ", number of requests %zu",
+ __FUNCTION__, bufferItem.mGraphicBuffer->getId(), tracker.requestedSurfaces().size());
+ for (const auto id : tracker.requestedSurfaces()) {
- // Initialize our reference count for this buffer
- mBuffers[bufferItem.mGraphicBuffer->getId()] =
- std::unique_ptr<BufferTracker>(
- new BufferTracker(bufferItem.mGraphicBuffer, surfaces->size()));
+ LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
+ "requested surface id exceeding max registered ids");
- for (auto id : *surfaces) {
-
- LOG_ALWAYS_FATAL_IF(id >= mOutputs.size(),
- "requested surface id exceeding max registered ids");
-
- int slot = BufferItem::INVALID_BUFFER_SLOT;
- status = mOutputs[id]->attachBuffer(&slot, bufferItem.mGraphicBuffer);
- if (status == NO_INIT) {
- // If we just discovered that this output has been abandoned, note
- // that, decrement the reference count so that we still release this
- // buffer eventually, and move on to the next output
- onAbandonedLocked();
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else if (status == WOULD_BLOCK) {
- // If the output is async, attachBuffer may return WOULD_BLOCK
- // indicating number of dequeued buffers has reached limit. In
- // this case, simply decrement the reference count, and move on
- // to the next output.
- // TODO: Do we need to report BUFFER_ERROR for this result?
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else if (status == TIMED_OUT) {
- // If attachBuffer times out due to the value set by
- // setDequeueTimeout, simply decrement the reference count, and
- // move on to the next output.
- // TODO: Do we need to report BUFFER_ERROR for this result?
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "attaching buffer to output failed (%d)", status);
- }
-
- IGraphicBufferProducer::QueueBufferOutput queueOutput;
- status = mOutputs[id]->queueBuffer(slot, queueInput, &queueOutput);
- if (status == NO_INIT) {
- // If we just discovered that this output has been abandoned, note
- // that, increment the release count so that we still release this
- // buffer eventually, and move on to the next output
- onAbandonedLocked();
- mBuffers[bufferItem.mGraphicBuffer->getId()]->
- decrementReferenceCountLocked();
- continue;
- } else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "queueing buffer to output failed (%d)", status);
- }
-
- // If the queued buffer replaces a pending buffer in the async
- // queue, no onBufferReleased is called by the buffer queue.
- // Proactively trigger the callback to avoid buffer loss.
- if (queueOutput.bufferReplaced) {
- onBufferReleasedByOutputLocked(mOutputs[id]);
- }
-
- ALOGV("queued buffer %#" PRIx64 " to output %p",
- bufferItem.mGraphicBuffer->getId(), mOutputs[id].get());
+ res = outputBufferLocked(mOutputs[id], bufferItem);
+ if (res != OK) {
+ SP_LOGE("%s: outputBufferLocked failed %d", __FUNCTION__, res);
+ mOnFrameAvailableRes.store(res);
+ // If we fail to send buffer to certain output, keep sending to
+ // other outputs.
+ continue;
}
+ }
- mRequestedSurfaces.erase(surfaces);
+ mOnFrameAvailableRes.store(res);
+}
+
+void Camera3StreamSplitter::decrementBufRefCountLocked(uint64_t id,
+ const sp<IGraphicBufferProducer>& from) {
+ ATRACE_CALL();
+ size_t referenceCount = mBuffers[id]->decrementReferenceCountLocked();
+
+ removeSlotForOutputLocked(from, mBuffers[id]->getBuffer());
+ if (referenceCount > 0) {
+ return;
+ }
+
+ // We no longer need to track the buffer now that it is being returned to the
+ // input. Note that this should happen before we unlock the mutex and call
+ // releaseBuffer, to avoid the case where the same bufferId is acquired in
+ // attachBufferToOutputs resulting in a new BufferTracker with same bufferId
+ // overwrites the current one.
+ std::unique_ptr<BufferTracker> tracker_ptr = std::move(mBuffers[id]);
+ mBuffers.erase(id);
+
+ // Attach and release the buffer back to the input
+ int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
+ status_t res = mConsumer->attachBuffer(&consumerSlot, tracker_ptr->getBuffer());
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: attaching buffer to input failed (%d)", __FUNCTION__, res);
+ return;
+ }
+
+ // Temporarily unlock mutex to avoid circular lock:
+ // 1. This function holds splitter lock, calls releaseBuffer which triggers
+ // onBufferReleased in Camera3OutputStream. onBufferReleased waits on the
+ // OutputStream lock
+ // 2. Camera3SharedOutputStream::getBufferLocked calls
+ // attachBufferToOutputs, which holds the stream lock, and waits for the
+ // splitter lock.
+ sp<IGraphicBufferConsumer> consumer(mConsumer);
+ mMutex.unlock();
+ if (consumer != nullptr) {
+ res = consumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker_ptr->getMergedFence());
+ } else {
+ SP_LOGE("%s: consumer has become null!", __FUNCTION__);
+ }
+ mMutex.lock();
+ // If the producer of this queue is disconnected, -22 error will occur
+ if (res != NO_ERROR) {
+ SP_LOGE("%s: releaseBuffer returns %d", __FUNCTION__, res);
}
}
@@ -347,73 +447,79 @@
void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
const sp<IGraphicBufferProducer>& from) {
-
+ ATRACE_CALL();
sp<GraphicBuffer> buffer;
sp<Fence> fence;
- status_t status = from->detachNextBuffer(&buffer, &fence);
- if (status == NO_INIT) {
+ status_t res = from->detachNextBuffer(&buffer, &fence);
+ if (res == NO_INIT) {
// If we just discovered that this output has been abandoned, note that,
// but we can't do anything else, since buffer is invalid
onAbandonedLocked();
return;
+ } else if (res == NO_MEMORY) {
+ SP_LOGV("%s: No free buffers", __FUNCTION__);
+ return;
} else {
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "detaching buffer from output failed (%d)", status);
+ LOG_ALWAYS_FATAL_IF(res != NO_ERROR,
+ "detaching buffer from output failed (%d)", res);
}
- ALOGV("detached buffer %#" PRIx64 " from output %p",
- buffer->getId(), from.get());
-
BufferTracker& tracker = *(mBuffers[buffer->getId()]);
-
// Merge the release fence of the incoming buffer so that the fence we send
// back to the input includes all of the outputs' fences
- tracker.mergeFence(fence);
+ if (fence != nullptr && fence->isValid()) {
+ tracker.mergeFence(fence);
+ }
+ SP_LOGV("detached buffer %" PRId64 " %p from output %p",
+ buffer->getId(), buffer.get(), from.get());
// Check to see if this is the last outstanding reference to this buffer
- size_t referenceCount = tracker.decrementReferenceCountLocked();
- ALOGV("buffer %#" PRIx64 " reference count %zu", buffer->getId(),
- referenceCount);
- if (referenceCount > 0) {
- return;
- }
-
- // If we've been abandoned, we can't return the buffer to the input, so just
- // stop tracking it and move on
- if (mIsAbandoned) {
- mBuffers.erase(buffer->getId());
- return;
- }
-
- // Attach and release the buffer back to the input
- int consumerSlot = BufferItem::INVALID_BUFFER_SLOT;
- status = mConsumer->attachBuffer(&consumerSlot, tracker.getBuffer());
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "attaching buffer to input failed (%d)", status);
-
- status = mConsumer->releaseBuffer(consumerSlot, /* frameNumber */ 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker.getMergedFence());
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "releasing buffer to input failed (%d)", status);
-
- ALOGV("released buffer %#" PRIx64 " to input", buffer->getId());
-
- // We no longer need to track the buffer once it has been returned to the
- // input
- mBuffers.erase(buffer->getId());
-
- // Notify any waiting onFrameAvailable calls
- --mOutstandingBuffers;
- mReleaseCondition.signal();
+ decrementBufRefCountLocked(buffer->getId(), from);
}
void Camera3StreamSplitter::onAbandonedLocked() {
- ALOGE("one of my outputs has abandoned me");
- if (!mIsAbandoned && mConsumer != nullptr) {
- mConsumer->consumerDisconnect();
+ // If this is called from binderDied callback, it means the app process
+ // holding the binder has died. CameraService will be notified of the binder
+ // death, and camera device will be closed, which in turn calls
+ // disconnect().
+ //
+ // If this is called from onBufferReleasedByOutput or onFrameAvailable, one
+ // consumer being abanoned shouldn't impact the other consumer. So we won't
+ // stop the buffer flow.
+ //
+ // In both cases, we don't need to do anything here.
+ SP_LOGV("One of my outputs has abandoned me");
+}
+
+int Camera3StreamSplitter::getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+ const sp<GraphicBuffer>& gb) {
+ auto& outputSlots = *mOutputSlots[gbp];
+
+ for (size_t i = 0; i < outputSlots.size(); i++) {
+ if (outputSlots[i] == gb) {
+ return (int)i;
+ }
}
- mIsAbandoned = true;
- mReleaseCondition.broadcast();
+
+ SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
+ gbp.get());
+ return BufferItem::INVALID_BUFFER_SLOT;
+}
+
+status_t Camera3StreamSplitter::removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+ const sp<GraphicBuffer>& gb) {
+ auto& outputSlots = *mOutputSlots[gbp];
+
+ for (size_t i = 0; i < outputSlots.size(); i++) {
+ if (outputSlots[i] == gb) {
+ outputSlots[i].clear();
+ return NO_ERROR;
+ }
+ }
+
+ SP_LOGE("%s: Cannot find slot for gb %p on output %p", __FUNCTION__, gb.get(),
+ gbp.get());
+ return BAD_VALUE;
}
Camera3StreamSplitter::OutputListener::OutputListener(
@@ -422,6 +528,7 @@
: mSplitter(splitter), mOutput(output) {}
void Camera3StreamSplitter::OutputListener::onBufferReleased() {
+ ATRACE_CALL();
sp<Camera3StreamSplitter> splitter = mSplitter.promote();
sp<IGraphicBufferProducer> output = mOutput.promote();
if (splitter != nullptr && output != nullptr) {
@@ -438,9 +545,9 @@
}
Camera3StreamSplitter::BufferTracker::BufferTracker(
- const sp<GraphicBuffer>& buffer, size_t referenceCount)
- : mBuffer(buffer), mMergedFence(Fence::NO_FENCE),
- mReferenceCount(referenceCount) {}
+ const sp<GraphicBuffer>& buffer, const std::vector<size_t>& requestedSurfaces)
+ : mBuffer(buffer), mMergedFence(Fence::NO_FENCE), mRequestedSurfaces(requestedSurfaces),
+ mReferenceCount(requestedSurfaces.size()) {}
void Camera3StreamSplitter::BufferTracker::mergeFence(const sp<Fence>& with) {
mMergedFence = Fence::merge(String8("Camera3StreamSplitter"), mMergedFence, with);
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 92371ff..cc623e0 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -26,6 +26,11 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
+#define SP_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+#define SP_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
+
namespace android {
class GraphicBuffer;
@@ -47,8 +52,8 @@
// Connect to the stream splitter by creating buffer queue and connecting it
// with output surfaces.
status_t connect(const std::vector<sp<Surface> >& surfaces,
- uint32_t consumerUsage, size_t hal_max_buffers,
- sp<Surface>& consumer);
+ uint32_t consumerUsage, size_t halMaxBuffers,
+ sp<Surface>* consumer);
// addOutput adds an output BufferQueue to the splitter. The splitter
// connects to outputQueue as a CPU producer, and any buffers queued
@@ -61,13 +66,22 @@
// outputQueue has not been added to the splitter. BAD_VALUE is returned if
// outputQueue is NULL. See IGraphicBufferProducer::connect for explanations
// of other error codes.
- status_t addOutput(const sp<Surface>& outputQueue, size_t hal_max_buffers);
+ status_t addOutput(const sp<Surface>& outputQueue);
- // Request surfaces for a particular frame number. The requested surfaces
- // are stored in a FIFO queue. And when the buffer becomes available from the
- // input queue, the registered surfaces are used to decide which output is
- // the buffer sent to.
- status_t notifyRequestedSurfaces(const std::vector<size_t>& surfaces);
+ // Notification that the graphic buffer has been released to the input
+ // BufferQueue. The buffer should be reused by the camera device instead of
+ // queuing to the outputs.
+ status_t notifyBufferReleased(const sp<GraphicBuffer>& buffer);
+
+ // Attach a buffer to the specified outputs. This call reserves a buffer
+ // slot in the output queue.
+ status_t attachBufferToOutputs(ANativeWindowBuffer* anb,
+ const std::vector<size_t>& surface_ids);
+
+ // Get return value of onFrameAvailable to work around problem that
+ // onFrameAvailable is void. This function should be called by the producer
+ // right after calling queueBuffer().
+ status_t getOnFrameAvailableResult();
// Disconnect the buffer queue from output surfaces.
void disconnect();
@@ -115,6 +129,10 @@
// acquire. This must be called with mMutex locked.
void onAbandonedLocked();
+ // Decrement the buffer's reference count. Once the reference count becomes
+ // 0, return the buffer back to the input BufferQueue.
+ void decrementBufRefCountLocked(uint64_t id, const sp<IGraphicBufferProducer>& from);
+
// This is a thin wrapper class that lets us determine which BufferQueue
// the IProducerListener::onBufferReleased callback is associated with. We
// create one of these per output BufferQueue, and then pass the producer
@@ -139,7 +157,8 @@
class BufferTracker {
public:
- BufferTracker(const sp<GraphicBuffer>& buffer, size_t referenceCount);
+ BufferTracker(const sp<GraphicBuffer>& buffer,
+ const std::vector<size_t>& requestedSurfaces);
~BufferTracker() = default;
const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
@@ -151,6 +170,8 @@
// Only called while mMutex is held
size_t decrementReferenceCountLocked();
+ const std::vector<size_t> requestedSurfaces() const { return mRequestedSurfaces; }
+
private:
// Disallow copying
@@ -159,39 +180,43 @@
sp<GraphicBuffer> mBuffer; // One instance that holds this native handle
sp<Fence> mMergedFence;
+
+ // Request surfaces for a particular buffer. And when the buffer becomes
+ // available from the input queue, the registered surfaces are used to decide
+ // which output is the buffer sent to.
+ std::vector<size_t> mRequestedSurfaces;
size_t mReferenceCount;
};
- // A deferred output is an output being added to the splitter after
- // connect() call, whereas a non deferred output is added within connect()
- // call.
- enum class OutputType { NonDeferred, Deferred };
-
// Must be accessed through RefBase
virtual ~Camera3StreamSplitter();
- status_t addOutputLocked(const sp<Surface>& outputQueue,
- size_t hal_max_buffers, OutputType outputType);
+ status_t addOutputLocked(const sp<Surface>& outputQueue);
+
+ // Send a buffer to particular output, and increment the reference count
+ // of the buffer. If this output is abandoned, the buffer's reference count
+ // won't be incremented.
+ status_t outputBufferLocked(const sp<IGraphicBufferProducer>& output,
+ const BufferItem& bufferItem);
// Get unique name for the buffer queue consumer
- static String8 getUniqueConsumerName();
+ String8 getUniqueConsumerName();
- // Max consumer side buffers for deferred surface. This will be used as a
- // lower bound for overall consumer side max buffers.
- static const int MAX_BUFFERS_DEFERRED_OUTPUT = 2;
- int mMaxConsumerBuffers = MAX_BUFFERS_DEFERRED_OUTPUT;
+ // Helper function to get the BufferQueue slot where a particular buffer is attached to.
+ int getSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+ const sp<GraphicBuffer>& gb);
+ // Helper function to remove the buffer from the BufferQueue slot
+ status_t removeSlotForOutputLocked(const sp<IGraphicBufferProducer>& gbp,
+ const sp<GraphicBuffer>& gb);
+
+
+ // Sum of max consumer buffers for all outputs
+ size_t mMaxConsumerBuffers = 0;
+ size_t mMaxHalBuffers = 0;
static const nsecs_t kDequeueBufferTimeout = s2ns(1); // 1 sec
- // mIsAbandoned is set to true when an output dies. Once the Camera3StreamSplitter
- // has been abandoned, it will continue to detach buffers from other
- // outputs, but it will disconnect from the input and not attempt to
- // communicate with it further.
- bool mIsAbandoned = false;
-
Mutex mMutex;
- Condition mReleaseCondition;
- int mOutstandingBuffers = 0;
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
@@ -199,14 +224,28 @@
sp<Surface> mSurface;
std::vector<sp<IGraphicBufferProducer> > mOutputs;
- // Tracking which outputs should the buffer be attached and queued
- // to for each input buffer.
- std::vector<std::vector<size_t> > mRequestedSurfaces;
-
// Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking
// objects (which are mostly for counting how many outputs have released the
// buffer, but also contain merged release fences).
std::unordered_map<uint64_t, std::unique_ptr<BufferTracker> > mBuffers;
+
+ struct GBPHash {
+ std::size_t operator()(const sp<IGraphicBufferProducer>& producer) const {
+ return std::hash<IGraphicBufferProducer *>{}(producer.get());
+ }
+ };
+
+ std::unordered_map<sp<IGraphicBufferProducer>, sp<OutputListener>,
+ GBPHash> mNotifiers;
+
+ typedef std::vector<sp<GraphicBuffer>> OutputSlots;
+ std::unordered_map<sp<IGraphicBufferProducer>, std::unique_ptr<OutputSlots>,
+ GBPHash> mOutputSlots;
+
+ // Latest onFrameAvailable return value
+ std::atomic<status_t> mOnFrameAvailableRes{0};
+
+ String8 mConsumerName;
};
} // namespace android
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index a428c75..8814cf2 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -20,7 +20,7 @@
# service executable
include $(CLEAR_VARS)
-LOCAL_REQUIRED_MODULES_arm := mediacodec-seccomp.policy
+LOCAL_REQUIRED_MODULES_arm := mediacodec.policy
LOCAL_SRC_FILES := main_codecservice.cpp
LOCAL_SHARED_LIBRARIES := \
libmedia \
@@ -46,4 +46,20 @@
LOCAL_INIT_RC := mediacodec.rc
include $(BUILD_EXECUTABLE)
+# service seccomp policy
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
+include $(CLEAR_VARS)
+LOCAL_MODULE := mediacodec.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
+# mediacodec runs in 32-bit combatibility mode. For 64 bit architectures,
+# use the 32 bit policy
+ifdef TARGET_2ND_ARCH
+ LOCAL_SRC_FILES := seccomp_policy/mediacodec-$(TARGET_2ND_ARCH).policy
+else
+ LOCAL_SRC_FILES := seccomp_policy/mediacodec-$(TARGET_ARCH).policy
+endif
+include $(BUILD_PREBUILT)
+endif
+
include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
index 688c651..ef305b4 100644
--- a/services/mediacodec/main_codecservice.cpp
+++ b/services/mediacodec/main_codecservice.cpp
@@ -38,13 +38,16 @@
using namespace android;
// Must match location in Android.mk.
-static const char kSeccompPolicyPath[] = "/system/etc/seccomp_policy/mediacodec-seccomp.policy";
+static const char kSystemSeccompPolicyPath[] =
+ "/system/etc/seccomp_policy/mediacodec.policy";
+static const char kVendorSeccompPolicyPath[] =
+ "/vendor/etc/seccomp_policy/mediacodec.policy";
int main(int argc __unused, char** argv)
{
LOG(INFO) << "mediacodecservice starting";
signal(SIGPIPE, SIG_IGN);
- SetUpMinijail(kSeccompPolicyPath, std::string());
+ SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath);
strcpy(argv[0], "media.codec");
diff --git a/services/mediacodec/minijail/Android.mk b/services/mediacodec/minijail/Android.mk
deleted file mode 100644
index de05bc3..0000000
--- a/services/mediacodec/minijail/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := mediacodec-seccomp.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-
-# mediacodec runs in 32-bit combatibility mode. For 64 bit architectures,
-# use the 32 bit policy
-ifdef TARGET_2ND_ARCH
- LOCAL_SRC_FILES := $(LOCAL_PATH)/seccomp_policy/mediacodec-seccomp-$(TARGET_2ND_ARCH).policy
-else
- LOCAL_SRC_FILES := $(LOCAL_PATH)/seccomp_policy/mediacodec-seccomp-$(TARGET_ARCH).policy
-endif
-
-# allow device specific additions to the syscall whitelist
-LOCAL_SRC_FILES += $(wildcard $(foreach dir, $(BOARD_SECCOMP_POLICY), \
- $(dir)/mediacodec-seccomp.policy))
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_SRC_FILES)
- @mkdir -p $(dir $@)
- $(hide) cat > $@ $^
-
-endif
diff --git a/services/mediacodec/minijail/seccomp_policy/mediacodec-seccomp-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
similarity index 100%
rename from services/mediacodec/minijail/seccomp_policy/mediacodec-seccomp-arm.policy
rename to services/mediacodec/seccomp_policy/mediacodec-arm.policy
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index 169c770..1ebb7ff 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -11,10 +11,9 @@
# service executable
include $(CLEAR_VARS)
# seccomp filters are defined for the following architectures:
-LOCAL_REQUIRED_MODULES_arm := mediaextractor-seccomp.policy
-LOCAL_REQUIRED_MODULES_arm64 := mediaextractor-seccomp.policy
-LOCAL_REQUIRED_MODULES_x86 := mediaextractor-seccomp.policy
-# TODO add seccomp filter for x86_64.
+LOCAL_REQUIRED_MODULES_arm := mediaextractor.policy
+LOCAL_REQUIRED_MODULES_arm64 := mediaextractor.policy
+LOCAL_REQUIRED_MODULES_x86 := mediaextractor.policy
LOCAL_SRC_FILES := main_extractorservice.cpp
LOCAL_SHARED_LIBRARIES := libmedia libmediaextractorservice libbinder libutils \
liblog libbase libicuuc libavservices_minijail
@@ -24,4 +23,12 @@
LOCAL_C_INCLUDES := frameworks/av/media/libmedia
include $(BUILD_EXECUTABLE)
-include $(call all-makefiles-under, $(LOCAL_PATH))
+# service seccomp filter
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86))
+include $(CLEAR_VARS)
+LOCAL_MODULE := mediaextractor.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
+LOCAL_SRC_FILES := seccomp_policy/mediaextractor-$(TARGET_ARCH).policy
+include $(BUILD_PREBUILT)
+endif
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index 752b7e4..9bc69c4 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -24,6 +24,8 @@
#include <string>
+#include <android-base/logging.h>
+
// from LOCAL_C_INCLUDES
#include "IcuUtils.h"
#include "MediaExtractorService.h"
@@ -32,8 +34,10 @@
using namespace android;
-// Must match location in Android.mk.
-static const char kSeccompPolicyPath[] = "/system/etc/seccomp_policy/mediaextractor-seccomp.policy";
+static const char kSystemSeccompPolicyPath[] =
+ "/system/etc/seccomp_policy/mediaextractor.policy";
+static const char kVendorSeccompPolicyPath[] =
+ "/vendor/etc/seccomp_policy/mediaextractor.policy";
int main(int argc __unused, char** argv)
{
@@ -43,7 +47,7 @@
20 /* upper limit as percentage of physical RAM */);
signal(SIGPIPE, SIG_IGN);
- SetUpMinijail(kSeccompPolicyPath, std::string());
+ SetUpMinijail(kSystemSeccompPolicyPath, kVendorSeccompPolicyPath);
InitializeIcuOrDie();
diff --git a/services/mediaextractor/minijail/Android.mk b/services/mediaextractor/minijail/Android.mk
deleted file mode 100644
index 6b01e77..0000000
--- a/services/mediaextractor/minijail/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# TODO add filter for x86_64
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm arm64 x86))
-include $(CLEAR_VARS)
-LOCAL_MODULE := mediaextractor-seccomp.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-LOCAL_SRC_FILES := $(LOCAL_PATH)/seccomp_policy/mediaextractor-seccomp-$(TARGET_ARCH).policy
-
-# allow device specific additions to the syscall whitelist
-LOCAL_SRC_FILES += $(wildcard $(foreach dir, $(BOARD_SECCOMP_POLICY), \
- $(dir)/mediaextractor-seccomp.policy))
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_SRC_FILES)
- @mkdir -p $(dir $@)
- $(hide) cat > $@ $^
-
-endif
diff --git a/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
similarity index 100%
rename from services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm.policy
rename to services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
diff --git a/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
similarity index 100%
rename from services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy
rename to services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
diff --git a/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
similarity index 100%
rename from services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-x86.policy
rename to services/mediaextractor/seccomp_policy/mediaextractor-x86.policy