Merge "audio policy manager: fix BT SCO stream mute" into rvc-qpr-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index c1ef3d8..be6185e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,6 +31,8 @@
"libmpeg2extractor",
"liboggextractor",
"libwavextractor",
+ // JNI
+ "libmediaparser-jni"
],
},
},
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index aecb70a..0b0f584 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -29,6 +29,7 @@
#include <binder/IMemory.h>
#include <camera/CameraBase.h>
+#include <camera/CameraUtils.h>
// needed to instantiate
#include <camera/Camera.h>
@@ -124,9 +125,7 @@
{
Mutex::Autolock _l(gLock);
if (gCameraService.get() == 0) {
- char value[PROPERTY_VALUE_MAX];
- property_get("config.disable_cameraservice", value, "0");
- if (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0) {
+ if (CameraUtils::isCameraServiceDisabled()) {
return gCameraService;
}
diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp
index 67fc116..f9b1b37 100644
--- a/camera/CameraUtils.cpp
+++ b/camera/CameraUtils.cpp
@@ -23,6 +23,7 @@
#include <system/window.h>
#include <system/graphics.h>
+#include <cutils/properties.h>
#include <utils/Log.h>
namespace android {
@@ -122,4 +123,10 @@
return OK;
}
+bool CameraUtils::isCameraServiceDisabled() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("config.disable_cameraservice", value, "0");
+ return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0);
+}
+
} /* namespace android */
diff --git a/camera/include/camera/CameraUtils.h b/camera/include/camera/CameraUtils.h
index f596f80..a397ccd 100644
--- a/camera/include/camera/CameraUtils.h
+++ b/camera/include/camera/CameraUtils.h
@@ -47,6 +47,11 @@
*/
static bool isNativeHandleMetadata(const sp<IMemory>& imageData);
+ /**
+ * Check if camera service is disabled on this device
+ */
+ static bool isCameraServiceDisabled();
+
private:
CameraUtils();
};
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index b58ebe2..2cba665 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -24,6 +24,7 @@
#include <utils/Vector.h>
#include <cutils/properties.h>
#include <stdlib.h>
+#include <camera/CameraUtils.h>
#include <camera/VendorTagDescriptor.h>
using namespace android::acam;
@@ -70,12 +71,6 @@
mCameraService.clear();
}
-static bool isCameraServiceDisabled() {
- char value[PROPERTY_VALUE_MAX];
- property_get("config.disable_cameraservice", value, "0");
- return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0);
-}
-
sp<hardware::ICameraService> CameraManagerGlobal::getCameraService() {
Mutex::Autolock _l(mLock);
return getCameraServiceLocked();
@@ -83,7 +78,7 @@
sp<hardware::ICameraService> CameraManagerGlobal::getCameraServiceLocked() {
if (mCameraService.get() == nullptr) {
- if (isCameraServiceDisabled()) {
+ if (CameraUtils::isCameraServiceDisabled()) {
return mCameraService;
}
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 0626c8d..1654b11 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -618,25 +618,26 @@
}
void CCodecBufferChannel::feedInputBufferIfAvailableInternal() {
- if (mInputMetEos || mPipelineWatcher.lock()->pipelineFull()) {
+ if (mInputMetEos) {
return;
}
{
Mutexed<Output>::Locked output(mOutput);
if (!output->buffers ||
output->buffers->hasPending() ||
- output->buffers->numClientBuffers() >= output->numSlots) {
+ output->buffers->numActiveSlots() >= output->numSlots) {
return;
}
}
- size_t numInputSlots = mInput.lock()->numSlots;
- for (size_t i = 0; i < numInputSlots; ++i) {
+ size_t numActiveSlots = 0;
+ while (!mPipelineWatcher.lock()->pipelineFull()) {
sp<MediaCodecBuffer> inBuffer;
size_t index;
{
Mutexed<Input>::Locked input(mInput);
- if (input->buffers->numClientBuffers() >= input->numSlots) {
- return;
+ numActiveSlots = input->buffers->numActiveSlots();
+ if (numActiveSlots >= input->numSlots) {
+ break;
}
if (!input->buffers->requestNewBuffer(&index, &inBuffer)) {
ALOGV("[%s] no new buffer available", mName);
@@ -646,6 +647,7 @@
ALOGV("[%s] new input index = %zu [%p]", mName, index, inBuffer.get());
mCallback->onInputBufferAvailable(index, inBuffer);
}
+ ALOGV("[%s] # active slots after feedInputBufferIfAvailable = %zu", mName, numActiveSlots);
}
status_t CCodecBufferChannel::renderOutputBuffer(
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index c49a16c..bddaa9f 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -493,11 +493,12 @@
mBuffers.clear();
}
-size_t FlexBuffersImpl::numClientBuffers() const {
+size_t FlexBuffersImpl::numActiveSlots() const {
return std::count_if(
mBuffers.begin(), mBuffers.end(),
[](const Entry &entry) {
- return (entry.clientBuffer != nullptr);
+ return (entry.clientBuffer != nullptr
+ || !entry.compBuffer.expired());
});
}
@@ -643,11 +644,11 @@
}
}
-size_t BuffersArrayImpl::numClientBuffers() const {
+size_t BuffersArrayImpl::numActiveSlots() const {
return std::count_if(
mBuffers.begin(), mBuffers.end(),
[](const Entry &entry) {
- return entry.ownedByClient;
+ return entry.ownedByClient || !entry.compBuffer.expired();
});
}
@@ -697,8 +698,8 @@
mImpl.flush();
}
-size_t InputBuffersArray::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t InputBuffersArray::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
sp<Codec2Buffer> InputBuffersArray::createNewBuffer() {
@@ -735,8 +736,8 @@
return nullptr;
}
-size_t SlotInputBuffers::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t SlotInputBuffers::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
sp<Codec2Buffer> SlotInputBuffers::createNewBuffer() {
@@ -787,8 +788,8 @@
return std::move(array);
}
-size_t LinearInputBuffers::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t LinearInputBuffers::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
// static
@@ -964,8 +965,8 @@
return std::move(array);
}
-size_t GraphicMetadataInputBuffers::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t GraphicMetadataInputBuffers::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
sp<Codec2Buffer> GraphicMetadataInputBuffers::createNewBuffer() {
@@ -1029,8 +1030,8 @@
return std::move(array);
}
-size_t GraphicInputBuffers::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t GraphicInputBuffers::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
sp<Codec2Buffer> GraphicInputBuffers::createNewBuffer() {
@@ -1119,8 +1120,8 @@
mImpl.getArray(array);
}
-size_t OutputBuffersArray::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t OutputBuffersArray::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
void OutputBuffersArray::realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
@@ -1230,8 +1231,8 @@
return array;
}
-size_t FlexOutputBuffers::numClientBuffers() const {
- return mImpl.numClientBuffers();
+size_t FlexOutputBuffers::numActiveSlots() const {
+ return mImpl.numActiveSlots();
}
// LinearOutputBuffers
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 0d4fa81..4772ab5 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -72,7 +72,7 @@
/**
* Return number of buffers the client owns.
*/
- virtual size_t numClientBuffers() const = 0;
+ virtual size_t numActiveSlots() const = 0;
/**
* Examine image data from the buffer and update the format if necessary.
@@ -584,7 +584,7 @@
* Return the number of buffers that are sent to the client but not released
* yet.
*/
- size_t numClientBuffers() const;
+ size_t numActiveSlots() const;
/**
* Return the number of buffers that are sent to the component but not
@@ -705,7 +705,7 @@
* Return the number of buffers that are sent to the client but not released
* yet.
*/
- size_t numClientBuffers() const;
+ size_t numActiveSlots() const;
/**
* Return the size of the array.
@@ -765,7 +765,7 @@
void flush() override;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
protected:
sp<Codec2Buffer> createNewBuffer() override;
@@ -796,7 +796,7 @@
std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
protected:
sp<Codec2Buffer> createNewBuffer() final;
@@ -826,7 +826,7 @@
std::unique_ptr<InputBuffers> toArrayMode(size_t size) override;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
protected:
sp<Codec2Buffer> createNewBuffer() override;
@@ -894,7 +894,7 @@
std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
protected:
sp<Codec2Buffer> createNewBuffer() override;
@@ -924,7 +924,7 @@
std::unique_ptr<InputBuffers> toArrayMode(
size_t size) final;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
protected:
sp<Codec2Buffer> createNewBuffer() override;
@@ -965,7 +965,7 @@
array->clear();
}
- size_t numClientBuffers() const final {
+ size_t numActiveSlots() const final {
return 0u;
}
@@ -1019,7 +1019,7 @@
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
/**
* Reallocate the array, filled with buffers with the same size as given
@@ -1073,7 +1073,7 @@
std::unique_ptr<OutputBuffersArray> toArrayMode(size_t size) override;
- size_t numClientBuffers() const final;
+ size_t numActiveSlots() const final;
/**
* Return an appropriate Codec2Buffer object for the type of buffers.
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index d19447a..901b29d 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -95,9 +95,9 @@
AMediaFormat *mMeta;
uint16_t mWaveFormat;
const bool mOutputFloat;
- int32_t mSampleRate;
- int32_t mNumChannels;
- int32_t mBitsPerSample;
+ uint32_t mSampleRate;
+ uint32_t mNumChannels;
+ uint32_t mBitsPerSample;
off64_t mOffset;
size_t mSize;
bool mStarted;
@@ -379,9 +379,9 @@
mOffset(offset),
mSize(size),
mStarted(false) {
- CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate));
- CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels));
- CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample));
+ CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) &mSampleRate));
+ CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, (int32_t*) &mNumChannels));
+ CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, (int32_t*) &mBitsPerSample));
}
WAVSource::~WAVSource() {
@@ -472,7 +472,7 @@
}
const size_t maxBytesAvailable =
- (mCurrentPos - mOffset >= (off64_t)mSize)
+ (mCurrentPos < mOffset || mCurrentPos - mOffset >= (off64_t)mSize)
? 0 : mSize - (mCurrentPos - mOffset);
if (maxBytesToRead > maxBytesAvailable) {
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 050ad65..ee78a2d 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -853,6 +853,11 @@
{ .duration = 0 , .waveFreq = { 0 }, 0, 0}},
.repeatCnt = ToneGenerator::TONEGEN_INF,
.repeatSegment = 0 }, // TONE_INDIA_RINGTONE
+ { .segments = { { .duration = 1000, .waveFreq = { 440, 480, 0 }, 0, 0 },
+ { .duration = 2000, .waveFreq = { 0 }, 0, 0 },
+ { .duration = 0 , .waveFreq = { 0 }, 0, 0}},
+ .repeatCnt = ToneGenerator::TONEGEN_INF,
+ .repeatSegment = 0 }, // TONE_TW_RINGTONE
};
// Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type
@@ -937,6 +942,16 @@
TONE_SUP_ERROR, // TONE_SUP_ERROR
TONE_INDIA_CALL_WAITING, // TONE_SUP_CALL_WAITING
TONE_INDIA_RINGTONE // TONE_SUP_RINGTONE
+ },
+ { // TAIWAN
+ TONE_SUP_DIAL, // TONE_SUP_DIAL
+ TONE_SUP_BUSY, // TONE_SUP_BUSY
+ TONE_SUP_CONGESTION, // TONE_SUP_CONGESTION
+ TONE_SUP_RADIO_ACK, // TONE_SUP_RADIO_ACK
+ TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL
+ TONE_SUP_ERROR, // TONE_SUP_ERROR
+ TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING
+ TONE_TW_RINGTONE // TONE_SUP_RINGTONE
}
};
@@ -1010,6 +1025,8 @@
mRegion = IRELAND;
} else if (strstr(value, "in") != NULL) {
mRegion = INDIA;
+ } else if (strstr(value, "tw") != NULL) {
+ mRegion = TAIWAN;
} else {
mRegion = CEPT;
}
diff --git a/media/libaudioclient/include/media/ToneGenerator.h b/media/libaudioclient/include/media/ToneGenerator.h
index 5b0689a..04357a8 100644
--- a/media/libaudioclient/include/media/ToneGenerator.h
+++ b/media/libaudioclient/include/media/ToneGenerator.h
@@ -218,6 +218,7 @@
TONE_INDIA_CONGESTION, // Congestion tone: 400 Hz, 250ms ON, 250ms OFF...
TONE_INDIA_CALL_WAITING, // Call waiting tone: 400 Hz, tone repeated in a 0.2s on, 0.1s off, 0.2s on, 7.5s off pattern.
TONE_INDIA_RINGTONE, // Ring tone: 400 Hz tone modulated with 25Hz, 0.4 on 0.2 off 0.4 on 2..0 off
+ TONE_TW_RINGTONE, // Ring Tone: 440 Hz + 480 Hz repeated with pattern 1s on, 3s off.
NUM_ALTERNATE_TONES
};
@@ -230,6 +231,7 @@
HONGKONG,
IRELAND,
INDIA,
+ TAIWAN,
CEPT,
NUM_REGIONS
};
diff --git a/media/libmedia/aidl/android/media/IResourceManagerService.aidl b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
index 1b2d522..621bd84 100644
--- a/media/libmedia/aidl/android/media/IResourceManagerService.aidl
+++ b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
@@ -102,4 +102,11 @@
* @param clientId clientId within the pid that will be removed.
*/
void markClientForPendingRemoval(int pid, long clientId);
+
+ /**
+ * Reclaim resources from clients pending removal, if any.
+ *
+ * @param pid pid from which resources will be reclaimed.
+ */
+ void reclaimResourcesFromClientsPendingRemoval(int pid);
}
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index 03068c7..a63b8b4 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -53,6 +53,7 @@
visibility: [
"//cts/tests/tests/nativemedia/mediametrics",
"//frameworks/av:__subpackages__",
+ "//frameworks/base/apex/media/framework",
"//frameworks/base/core/jni",
"//frameworks/base/media/jni",
],
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 9b1974b..1cc255d 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -28,6 +28,7 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryBase.h>
+#include <camera/CameraUtils.h>
#include <codec2/hidl/client.h>
#include <cutils/atomic.h>
#include <cutils/properties.h> // for property_get
@@ -423,30 +424,35 @@
sp<IServiceManager> sm = defaultServiceManager();
- // WORKAROUND: We don't know if camera exists here and getService might block for 5 seconds.
- // Use checkService for camera if we don't know it exists.
- static std::atomic<bool> sCameraChecked(false); // once true never becomes false.
- static std::atomic<bool> sCameraVerified(false); // once true never becomes false.
- sp<IBinder> binder = (sCameraVerified || !sCameraChecked)
- ? sm->getService(String16("media.camera")) : sm->checkService(String16("media.camera"));
- // If the device does not have a camera, do not create a death listener for it.
- if (binder != NULL) {
- sCameraVerified = true;
- mDeathNotifiers.emplace_back(
- binder, [l = wp<IMediaRecorderClient>(listener)](){
- sp<IMediaRecorderClient> listener = l.promote();
- if (listener) {
- ALOGV("media.camera service died. "
- "Sending death notification.");
- listener->notify(
- MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
- MediaPlayerService::CAMERA_PROCESS_DEATH);
- } else {
- ALOGW("media.camera service died without a death handler.");
- }
- });
+ static const bool sCameraDisabled = CameraUtils::isCameraServiceDisabled();
+
+ if (!sCameraDisabled) {
+ // WORKAROUND: We don't know if camera exists here and getService might block for 5 seconds.
+ // Use checkService for camera if we don't know it exists.
+ static std::atomic<bool> sCameraChecked(false); // once true never becomes false.
+ static std::atomic<bool> sCameraVerified(false); // once true never becomes false.
+
+ sp<IBinder> binder = (sCameraVerified || !sCameraChecked)
+ ? sm->getService(String16("media.camera")) : sm->checkService(String16("media.camera"));
+ // If the device does not have a camera, do not create a death listener for it.
+ if (binder != NULL) {
+ sCameraVerified = true;
+ mDeathNotifiers.emplace_back(
+ binder, [l = wp<IMediaRecorderClient>(listener)](){
+ sp<IMediaRecorderClient> listener = l.promote();
+ if (listener) {
+ ALOGV("media.camera service died. "
+ "Sending death notification.");
+ listener->notify(
+ MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
+ MediaPlayerService::CAMERA_PROCESS_DEATH);
+ } else {
+ ALOGW("media.camera service died without a death handler.");
+ }
+ });
+ }
+ sCameraChecked = true;
}
- sCameraChecked = true;
{
using ::android::hidl::base::V1_0::IBase;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 31c800e..3e191fe 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -241,6 +241,9 @@
}
AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
+
+ // Kill clients pending removal.
+ mService->reclaimResourcesFromClientsPendingRemoval(mPid);
}
//static
@@ -614,7 +617,10 @@
return new PersistentSurface(bufferProducer, bufferSource);
}
-MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
+MediaCodec::MediaCodec(
+ const sp<ALooper> &looper, pid_t pid, uid_t uid,
+ std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase,
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo)
: mState(UNINITIALIZED),
mReleasedByResourceManager(false),
mLooper(looper),
@@ -639,7 +645,9 @@
mNumLowLatencyDisables(0),
mIsLowLatencyModeOn(false),
mIndexOfFirstFrameWhenLowLatencyOn(-1),
- mInputBufferCounter(0) {
+ mInputBufferCounter(0),
+ mGetCodecBase(getCodecBase),
+ mGetCodecInfo(getCodecInfo) {
if (uid == kNoUid) {
mUid = AIBinder_getCallingUid();
} else {
@@ -647,6 +655,33 @@
}
mResourceManagerProxy = new ResourceManagerServiceProxy(pid, mUid,
::ndk::SharedRefBase::make<ResourceManagerClient>(this));
+ if (!mGetCodecBase) {
+ mGetCodecBase = [](const AString &name, const char *owner) {
+ return GetCodecBase(name, owner);
+ };
+ }
+ if (!mGetCodecInfo) {
+ mGetCodecInfo = [](const AString &name, sp<MediaCodecInfo> *info) -> status_t {
+ *info = nullptr;
+ const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
+ if (!mcl) {
+ return NO_INIT; // if called from Java should raise IOException
+ }
+ AString tmp = name;
+ if (tmp.endsWith(".secure")) {
+ tmp.erase(tmp.size() - 7, 7);
+ }
+ for (const AString &codecName : { name, tmp }) {
+ ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
+ if (codecIdx < 0) {
+ continue;
+ }
+ *info = mcl->getCodecInfo(codecIdx);
+ return OK;
+ }
+ return NAME_NOT_FOUND;
+ };
+ }
initMediametrics();
}
@@ -1008,6 +1043,12 @@
return err;
}
+void MediaCodec::PostReplyWithError(const sp<AMessage> &msg, int32_t err) {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ PostReplyWithError(replyID, err);
+}
+
void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
int32_t finalErr = err;
if (mReleasedByResourceManager) {
@@ -1084,40 +1125,30 @@
bool secureCodec = false;
const char *owner = "";
if (!name.startsWith("android.filter.")) {
- AString tmp = name;
- if (tmp.endsWith(".secure")) {
- secureCodec = true;
- tmp.erase(tmp.size() - 7, 7);
- }
- const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
- if (mcl == NULL) {
+ status_t err = mGetCodecInfo(name, &mCodecInfo);
+ if (err != OK) {
mCodec = NULL; // remove the codec.
- return NO_INIT; // if called from Java should raise IOException
- }
- for (const AString &codecName : { name, tmp }) {
- ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
- if (codecIdx < 0) {
- continue;
- }
- mCodecInfo = mcl->getCodecInfo(codecIdx);
- Vector<AString> mediaTypes;
- mCodecInfo->getSupportedMediaTypes(&mediaTypes);
- for (size_t i = 0; i < mediaTypes.size(); i++) {
- if (mediaTypes[i].startsWith("video/")) {
- mIsVideo = true;
- break;
- }
- }
- break;
+ return err;
}
if (mCodecInfo == nullptr) {
+ ALOGE("Getting codec info with name '%s' failed", name.c_str());
return NAME_NOT_FOUND;
}
+ secureCodec = name.endsWith(".secure");
+ Vector<AString> mediaTypes;
+ mCodecInfo->getSupportedMediaTypes(&mediaTypes);
+ for (size_t i = 0; i < mediaTypes.size(); ++i) {
+ if (mediaTypes[i].startsWith("video/")) {
+ mIsVideo = true;
+ break;
+ }
+ }
owner = mCodecInfo->getOwnerName();
}
- mCodec = GetCodecBase(name, owner);
+ mCodec = mGetCodecBase(name, owner);
if (mCodec == NULL) {
+ ALOGE("Getting codec base with name '%s' (owner='%s') failed", name.c_str(), owner);
return NAME_NOT_FOUND;
}
@@ -1511,7 +1542,6 @@
mStickyError = OK;
// reset state not reset by setState(UNINITIALIZED)
- mReplyID = 0;
mDequeueInputReplyID = 0;
mDequeueOutputReplyID = 0;
mDequeueInputTimeoutGeneration = 0;
@@ -2106,6 +2136,8 @@
}
bool sendErrorResponse = true;
+ std::string origin{"kWhatError:"};
+ origin += stateString(mState);
switch (mState) {
case INITIALIZING:
@@ -2157,14 +2189,14 @@
// be a shutdown complete notification after
// all.
- // note that we're directly going from
+ // note that we may be directly going from
// STOPPING->UNINITIALIZED, instead of the
// usual STOPPING->INITIALIZED state.
setState(UNINITIALIZED);
if (mState == RELEASING) {
mComponentName.clear();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(origin + ":dead");
sendErrorResponse = false;
}
break;
@@ -2190,7 +2222,7 @@
case FLUSHED:
case STARTED:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2220,7 +2252,7 @@
default:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2247,7 +2279,15 @@
}
if (sendErrorResponse) {
- PostReplyWithError(mReplyID, err);
+ // TRICKY: replicate PostReplyWithError logic for
+ // err code override
+ int32_t finalErr = err;
+ if (mReleasedByResourceManager) {
+ // override the err code if MediaCodec has been
+ // released by ResourceManager.
+ finalErr = DEAD_OBJECT;
+ }
+ postPendingRepliesAndDeferredMessages(origin, finalErr);
}
break;
}
@@ -2295,7 +2335,7 @@
MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatComponentAllocated");
break;
}
@@ -2334,7 +2374,7 @@
mFlags |= kFlagUsesSoftwareRenderer;
}
setState(CONFIGURED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatComponentConfigured");
// augment our media metrics info, now that we know more things
// such as what the codec extracted from any CSD passed in.
@@ -2379,6 +2419,12 @@
case kWhatInputSurfaceCreated:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceCreated message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateCreateInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage;
@@ -2397,12 +2443,18 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatInputSurfaceCreated", response);
break;
}
case kWhatInputSurfaceAccepted:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceAccepted message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateSetInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage();
@@ -2413,19 +2465,25 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatInputSurfaceAccepted", response);
break;
}
case kWhatSignaledInputEOS:
{
+ if (!isExecuting()) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatSignaledInputEOS message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to signalEndOfInputStream()
sp<AMessage> response = new AMessage;
status_t err;
if (msg->findInt32("err", &err)) {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatSignaledInputEOS", response);
break;
}
@@ -2444,7 +2502,7 @@
MediaResource::GraphicMemoryResource(getGraphicBufferSize()));
}
setState(STARTED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatStartCompleted");
break;
}
@@ -2580,7 +2638,13 @@
break;
}
setState(INITIALIZED);
- (new AMessage)->postReply(mReplyID);
+ if (mReplyID) {
+ postPendingRepliesAndDeferredMessages("kWhatStopCompleted");
+ } else {
+ ALOGW("kWhatStopCompleted: presumably an error occurred earlier, "
+ "but the operation completed anyway. (last reply origin=%s)",
+ mLastReplyOrigin.c_str());
+ }
break;
}
@@ -2604,7 +2668,7 @@
mReleaseSurface.reset();
if (mReplyID != nullptr) {
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatReleaseCompleted");
}
if (mAsyncReleaseCompleteNotification != nullptr) {
flushMediametrics();
@@ -2629,7 +2693,7 @@
mCodec->signalResume();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages("kWhatFlushCompleted");
break;
}
@@ -2641,14 +2705,18 @@
case kWhatInit:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != UNINITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(INITIALIZING);
@@ -2710,14 +2778,18 @@
case kWhatConfigure:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != INITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
@@ -2855,15 +2927,19 @@
case kWhatCreateInputSurface:
case kWhatSetInputSurface:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
// Must be configured, but can't have been started yet.
if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
if (msg->what() == kWhatCreateInputSurface) {
mCodec->initiateCreateInputSurface();
@@ -2878,9 +2954,6 @@
}
case kWhatStart:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState == FLUSHED) {
setState(STARTED);
if (mHavePendingInputBuffers) {
@@ -2888,13 +2961,20 @@
mHavePendingInputBuffers = false;
}
mCodec->signalResume();
- PostReplyWithError(replyID, OK);
+ PostReplyWithError(msg, OK);
break;
} else if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(STARTING);
@@ -2902,15 +2982,42 @@
break;
}
- case kWhatStop:
+ case kWhatStop: {
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ [[fallthrough]];
+ }
case kWhatRelease:
{
State targetState =
(msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED;
+ if ((mState == RELEASING && targetState == UNINITIALIZED)
+ || (mState == STOPPING && targetState == INITIALIZED)) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ sp<AMessage> asyncNotify;
+ (void)msg->findMessage("async", &asyncNotify);
+ // post asyncNotify if going out of scope.
+ struct AsyncNotifyPost {
+ AsyncNotifyPost(const sp<AMessage> &asyncNotify) : mAsyncNotify(asyncNotify) {}
+ ~AsyncNotifyPost() {
+ if (mAsyncNotify) {
+ mAsyncNotify->post();
+ }
+ }
+ void clear() { mAsyncNotify.clear(); }
+ private:
+ sp<AMessage> mAsyncNotify;
+ } asyncNotifyPost{asyncNotify};
+
// already stopped/released
if (mState == UNINITIALIZED && mReleasedByResourceManager) {
sp<AMessage> response = new AMessage;
@@ -2974,12 +3081,15 @@
// after this, and we'll no longer be able to reply.
if (mState == FLUSHING || mState == STOPPING
|| mState == CONFIGURING || mState == STARTING) {
- (new AMessage)->postReply(mReplyID);
+ // mReply is always set if in these states.
+ postPendingRepliesAndDeferredMessages(
+ std::string("kWhatRelease:") + stateString(mState));
}
if (mFlags & kFlagSawMediaServerDie) {
// It's dead, Jim. Don't expect initiateShutdown to yield
// any useful results now...
+ // Any pending reply would have been handled at kWhatError.
setState(UNINITIALIZED);
if (targetState == UNINITIALIZED) {
mComponentName.clear();
@@ -2993,12 +3103,12 @@
// reply now with an error to unblock the client, client can
// release after the failure (instead of ANR).
if (msg->what() == kWhatStop && (mFlags & kFlagStickyError)) {
+ // Any pending reply would have been handled at kWhatError.
PostReplyWithError(replyID, getStickyError());
break;
}
- sp<AMessage> asyncNotify;
- if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
+ if (asyncNotify != nullptr) {
if (mSurface != NULL) {
if (!mReleaseSurface) {
mReleaseSurface.reset(new ReleaseSurface);
@@ -3018,6 +3128,12 @@
}
}
+ if (mReplyID) {
+ // State transition replies are handled above, so this reply
+ // would not be related to state transition. As we are
+ // shutting down the component, just fail the operation.
+ postPendingRepliesAndDeferredMessages("kWhatRelease:reply", UNKNOWN_ERROR);
+ }
mReplyID = replyID;
setState(msg->what() == kWhatStop ? STOPPING : RELEASING);
@@ -3032,8 +3148,8 @@
if (asyncNotify != nullptr) {
mResourceManagerProxy->markClientForPendingRemoval();
- (new AMessage)->postReply(mReplyID);
- mReplyID = 0;
+ postPendingRepliesAndDeferredMessages("kWhatRelease:async");
+ asyncNotifyPost.clear();
mAsyncReleaseCompleteNotification = asyncNotify;
}
@@ -3204,17 +3320,21 @@
case kWhatSignalEndOfInputStream:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting() || !mHaveInputSurface) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
mCodec->signalEndOfInputStream();
break;
@@ -3256,17 +3376,21 @@
case kWhatFlush:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting()) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
// TODO: skip flushing if already FLUSHED
setState(FLUSHING);
@@ -4211,6 +4335,33 @@
return OK;
}
+void MediaCodec::postPendingRepliesAndDeferredMessages(
+ std::string origin, status_t err /* = OK */) {
+ sp<AMessage> response{new AMessage};
+ if (err != OK) {
+ response->setInt32("err", err);
+ }
+ postPendingRepliesAndDeferredMessages(origin, response);
+}
+
+void MediaCodec::postPendingRepliesAndDeferredMessages(
+ std::string origin, const sp<AMessage> &response) {
+ LOG_ALWAYS_FATAL_IF(
+ !mReplyID,
+ "postPendingRepliesAndDeferredMessages: mReplyID == null, from %s following %s",
+ origin.c_str(),
+ mLastReplyOrigin.c_str());
+ mLastReplyOrigin = origin;
+ response->postReply(mReplyID);
+ mReplyID.clear();
+ ALOGV_IF(!mDeferredMessages.empty(),
+ "posting %zu deferred messages", mDeferredMessages.size());
+ for (sp<AMessage> msg : mDeferredMessages) {
+ msg->post();
+ }
+ mDeferredMessages.clear();
+}
+
std::string MediaCodec::stateString(State state) {
const char *rval = NULL;
char rawbuffer[16]; // room for "%d"
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 8b36ea5..5e537dd 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -17,6 +17,9 @@
"exclude-filter": "android.media.cts.AudioRecordTest"
}
]
+ },
+ {
+ "name": "mediacodecTest"
}
],
"postsubmit": [
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index c64cf46..a5c7f5e 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -219,11 +219,6 @@
if (info->error_protection)
{
- if (bitsAvailable(&pVars->inputStream, 16))
- {
- return SIDE_INFO_ERROR;
- }
-
/*
* Get crc content
*/
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
index 1a3fca5..d644207 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
@@ -73,7 +73,6 @@
#include "pvmp3_get_side_info.h"
#include "pvmp3_crc.h"
-#include "pvmp3_getbits.h"
/*----------------------------------------------------------------------------
@@ -126,22 +125,12 @@
{
if (stereo == 1)
{
- if (!bitsAvailable(inputStream, 14))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 14, crc, info->error_protection);
si->main_data_begin = (tmp << 18) >> 23; /* 9 */
si->private_bits = (tmp << 27) >> 27; /* 5 */
}
else
{
- if (!bitsAvailable(inputStream, 12))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 12, crc, info->error_protection);
si->main_data_begin = (tmp << 20) >> 23; /* 9 */
si->private_bits = (tmp << 29) >> 29; /* 3 */
@@ -150,11 +139,6 @@
for (ch = 0; ch < stereo; ch++)
{
- if (!bitsAvailable(inputStream, 4))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 4, crc, info->error_protection);
si->ch[ch].scfsi[0] = (tmp << 28) >> 31; /* 1 */
si->ch[ch].scfsi[1] = (tmp << 29) >> 31; /* 1 */
@@ -166,11 +150,6 @@
{
for (ch = 0; ch < stereo; ch++)
{
- if (!bitsAvailable(inputStream, 34))
- {
- return SIDE_INFO_ERROR;
- }
-
si->ch[ch].gran[gr].part2_3_length = getbits_crc(inputStream, 12, crc, info->error_protection);
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
@@ -181,11 +160,6 @@
if (si->ch[ch].gran[gr].window_switching_flag)
{
- if (!bitsAvailable(inputStream, 22))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[gr].block_type = (tmp << 10) >> 30; /* 2 */;
@@ -218,11 +192,6 @@
}
else
{
- if (!bitsAvailable(inputStream, 22))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[gr].table_select[0] = (tmp << 10) >> 27; /* 5 */;
@@ -235,11 +204,6 @@
si->ch[ch].gran[gr].block_type = 0;
}
- if (!bitsAvailable(inputStream, 3))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 3, crc, info->error_protection);
si->ch[ch].gran[gr].preflag = (tmp << 29) >> 31; /* 1 */
si->ch[ch].gran[gr].scalefac_scale = (tmp << 30) >> 31; /* 1 */
@@ -249,21 +213,11 @@
}
else /* Layer 3 LSF */
{
- if (!bitsAvailable(inputStream, 8 + stereo))
- {
- return SIDE_INFO_ERROR;
- }
-
si->main_data_begin = getbits_crc(inputStream, 8, crc, info->error_protection);
si->private_bits = getbits_crc(inputStream, stereo, crc, info->error_protection);
for (ch = 0; ch < stereo; ch++)
{
- if (!bitsAvailable(inputStream, 39))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 21, crc, info->error_protection);
si->ch[ch].gran[0].part2_3_length = (tmp << 11) >> 20; /* 12 */
si->ch[ch].gran[0].big_values = (tmp << 23) >> 23; /* 9 */
@@ -276,11 +230,6 @@
if (si->ch[ch].gran[0].window_switching_flag)
{
- if (!bitsAvailable(inputStream, 22))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[0].block_type = (tmp << 10) >> 30; /* 2 */;
@@ -313,11 +262,6 @@
}
else
{
- if (!bitsAvailable(inputStream, 22))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[0].table_select[0] = (tmp << 10) >> 27; /* 5 */;
@@ -330,11 +274,6 @@
si->ch[ch].gran[0].block_type = 0;
}
- if (!bitsAvailable(inputStream, 2))
- {
- return SIDE_INFO_ERROR;
- }
-
tmp = getbits_crc(inputStream, 2, crc, info->error_protection);
si->ch[ch].gran[0].scalefac_scale = tmp >> 1; /* 1 */
si->ch[ch].gran[0].count1table_select = tmp & 1; /* 1 */
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.cpp
index 4d252ef..8ff7953 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.cpp
@@ -113,11 +113,10 @@
uint32 offset;
uint32 bitIndex;
- uint32 bytesToFetch;
- uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1 = 0;
- uint8 Elem2 = 0;
- uint8 Elem3 = 0;
+ uint8 Elem; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1;
+ uint8 Elem2;
+ uint8 Elem3;
uint32 returnValue = 0;
if (!neededBits)
@@ -127,25 +126,10 @@
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
- bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
-
- switch (bytesToFetch)
- {
- case 4:
- Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE));
- [[fallthrough]];
- case 3:
- Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
- [[fallthrough]];
- case 2:
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- [[fallthrough]];
- case 1:
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- }
+ Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
+ Elem3 = *(ptBitStream->pBuffer + module(offset + 3, BUFSIZE));
returnValue = (((uint32)(Elem)) << 24) |
@@ -153,6 +137,9 @@
(((uint32)(Elem2)) << 8) |
((uint32)(Elem3));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
/* This line is faster than to mask off the high bits. */
returnValue <<= bitIndex;
@@ -174,32 +161,22 @@
uint32 offset;
uint32 bitIndex;
- uint32 bytesToFetch;
- uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1 = 0;
+ uint8 Elem; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1;
uint16 returnValue;
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
- bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
-
- if (bytesToFetch > 1)
- {
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- }
- else if (bytesToFetch > 0)
- {
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- }
+ Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
returnValue = (((uint16)(Elem)) << 8) |
((uint16)(Elem1));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
ptBitStream->usedBits += neededBits;
/* This line is faster than to mask off the high bits. */
returnValue = (returnValue << (bitIndex));
@@ -220,40 +197,25 @@
uint32 offset;
uint32 bitIndex;
- uint32 bytesToFetch;
- uint8 Elem = 0; /* Needs to be same type as pInput->pBuffer */
- uint8 Elem1 = 0;
- uint8 Elem2 = 0;
+ uint8 Elem; /* Needs to be same type as pInput->pBuffer */
+ uint8 Elem1;
+ uint8 Elem2;
uint32 returnValue;
offset = (ptBitStream->usedBits) >> INBUF_ARRAY_INDEX_SHIFT;
- /* Remove extra high bits by shifting up */
- bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
-
- bytesToFetch = (bitIndex + neededBits + 7 ) >> 3 ;
-
- if (bytesToFetch > 2)
- {
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
- }
- else if (bytesToFetch > 1)
- {
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
- }
- else if (bytesToFetch > 0)
- {
- Elem = *(ptBitStream->pBuffer + module(offset, BUFSIZE));
- }
+ Elem = *(ptBitStream->pBuffer + module(offset , BUFSIZE));
+ Elem1 = *(ptBitStream->pBuffer + module(offset + 1, BUFSIZE));
+ Elem2 = *(ptBitStream->pBuffer + module(offset + 2, BUFSIZE));
returnValue = (((uint32)(Elem)) << 16) |
(((uint32)(Elem1)) << 8) |
((uint32)(Elem2));
+ /* Remove extra high bits by shifting up */
+ bitIndex = module(ptBitStream->usedBits, INBUF_BIT_WIDTH);
+
ptBitStream->usedBits += neededBits;
/* This line is faster than to mask off the high bits. */
returnValue = 0xFFFFFF & (returnValue << (bitIndex));
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.h b/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.h
index b04fe6d..b058b00 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.h
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_getbits.h
@@ -104,11 +104,6 @@
; Function Prototype declaration
----------------------------------------------------------------------------*/
-static inline bool bitsAvailable(tmp3Bits *inputStream, uint32 neededBits)
-{
- return (inputStream->inputBufferCurrentLength << 3) >= (neededBits + inputStream->usedBits);
-}
-
/*----------------------------------------------------------------------------
; END
----------------------------------------------------------------------------*/
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 8057312..7614ba5 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -366,6 +366,8 @@
AString mOwnerName;
sp<MediaCodecInfo> mCodecInfo;
sp<AReplyToken> mReplyID;
+ std::string mLastReplyOrigin;
+ std::vector<sp<AMessage>> mDeferredMessages;
uint32_t mFlags;
status_t mStickyError;
sp<Surface> mSurface;
@@ -428,13 +430,17 @@
std::shared_ptr<BufferChannelBase> mBufferChannel;
- MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
+ MediaCodec(
+ const sp<ALooper> &looper, pid_t pid, uid_t uid,
+ std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase = nullptr,
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo = nullptr);
static sp<CodecBase> GetCodecBase(const AString &name, const char *owner = nullptr);
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
+ void PostReplyWithError(const sp<AMessage> &msg, int32_t err);
void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
status_t init(const AString &name);
@@ -486,6 +492,9 @@
bool hasPendingBuffer(int portIndex);
bool hasPendingBuffer();
+ void postPendingRepliesAndDeferredMessages(std::string origin, status_t err = OK);
+ void postPendingRepliesAndDeferredMessages(std::string origin, const sp<AMessage> &response);
+
/* called to get the last codec error when the sticky flag is set.
* if no such codec error is found, returns UNKNOWN_ERROR.
*/
@@ -571,6 +580,10 @@
Histogram mLatencyHist;
+ std::function<sp<CodecBase>(const AString &, const char *)> mGetCodecBase;
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> mGetCodecInfo;
+ friend class MediaTestHelper;
+
DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
};
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
index f53b23e..bf85d7e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecListWriter.h
@@ -19,7 +19,6 @@
#define MEDIA_CODEC_LIST_WRITER_H_
#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/MediaCodecListWriter.h>
#include <media/MediaCodecInfo.h>
#include <utils/Errors.h>
@@ -65,6 +64,7 @@
std::vector<sp<MediaCodecInfo>> mCodecInfos;
friend struct MediaCodecList;
+ friend class MediaTestHelper;
};
/**
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 4bb21fa..ea5d2de 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -1433,7 +1433,13 @@
if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) {
uint8_t *nalData = mBuffer->data() + pos.nalOffset;
size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize);
- // Note: the data can shrink due to unescaping
+ // Note: the data can shrink due to unescaping, but it can never grow
+ if (newSize > pos.nalSize) {
+ // don't log unless verbose, since this can get called a lot if
+ // the caller is trying to resynchronize
+ ALOGV("expected sample size < %u, got %zu", pos.nalSize, newSize);
+ return NULL;
+ }
memcpy(accessUnit->data() + dstOffset + 4,
nalData,
newSize);
diff --git a/media/libstagefright/tests/mediacodec/Android.bp b/media/libstagefright/tests/mediacodec/Android.bp
new file mode 100644
index 0000000..0bd0639
--- /dev/null
+++ b/media/libstagefright/tests/mediacodec/Android.bp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+cc_test {
+ name: "mediacodecTest",
+ gtest: true,
+
+ srcs: [
+ "MediaCodecTest.cpp",
+ "MediaTestHelper.cpp",
+ ],
+
+ header_libs: [
+ "libmediadrm_headers",
+ ],
+
+ shared_libs: [
+ "libgui",
+ "libmedia",
+ "libmedia_codeclist",
+ "libmediametrics",
+ "libmediandk",
+ "libstagefright",
+ "libstagefright_codecbase",
+ "libstagefright_foundation",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libgmock",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
new file mode 100644
index 0000000..d00a50f
--- /dev/null
+++ b/media/libstagefright/tests/mediacodec/MediaCodecTest.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <future>
+#include <thread>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <gui/Surface.h>
+#include <mediadrm/ICrypto.h>
+#include <media/stagefright/CodecBase.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecListWriter.h>
+#include <media/MediaCodecInfo.h>
+
+#include "MediaTestHelper.h"
+
+namespace android {
+
+class MockBufferChannel : public BufferChannelBase {
+public:
+ ~MockBufferChannel() override = default;
+
+ MOCK_METHOD(void, setCrypto, (const sp<ICrypto> &crypto), (override));
+ MOCK_METHOD(void, setDescrambler, (const sp<IDescrambler> &descrambler), (override));
+ MOCK_METHOD(status_t, queueInputBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
+ MOCK_METHOD(status_t, queueSecureInputBuffer,
+ (const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ AString *errorDetailMsg),
+ (override));
+ MOCK_METHOD(status_t, attachBuffer,
+ (const std::shared_ptr<C2Buffer> &c2Buffer, const sp<MediaCodecBuffer> &buffer),
+ (override));
+ MOCK_METHOD(status_t, attachEncryptedBuffer,
+ (const sp<hardware::HidlMemory> &memory,
+ bool secure,
+ const uint8_t *key,
+ const uint8_t *iv,
+ CryptoPlugin::Mode mode,
+ CryptoPlugin::Pattern pattern,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const sp<MediaCodecBuffer> &buffer),
+ (override));
+ MOCK_METHOD(status_t, renderOutputBuffer,
+ (const sp<MediaCodecBuffer> &buffer, int64_t timestampNs),
+ (override));
+ MOCK_METHOD(status_t, discardBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
+ MOCK_METHOD(void, getInputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
+ MOCK_METHOD(void, getOutputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
+};
+
+class MockCodec : public CodecBase {
+public:
+ MockCodec(std::function<void(const std::shared_ptr<MockBufferChannel> &)> mock) {
+ mMockBufferChannel = std::make_shared<MockBufferChannel>();
+ mock(mMockBufferChannel);
+ }
+ ~MockCodec() override = default;
+
+ MOCK_METHOD(void, initiateAllocateComponent, (const sp<AMessage> &msg), (override));
+ MOCK_METHOD(void, initiateConfigureComponent, (const sp<AMessage> &msg), (override));
+ MOCK_METHOD(void, initiateCreateInputSurface, (), (override));
+ MOCK_METHOD(void, initiateSetInputSurface, (const sp<PersistentSurface> &surface), (override));
+ MOCK_METHOD(void, initiateStart, (), (override));
+ MOCK_METHOD(void, initiateShutdown, (bool keepComponentAllocated), (override));
+ MOCK_METHOD(void, onMessageReceived, (const sp<AMessage> &msg), (override));
+ MOCK_METHOD(status_t, setSurface, (const sp<Surface> &surface), (override));
+ MOCK_METHOD(void, signalFlush, (), (override));
+ MOCK_METHOD(void, signalResume, (), (override));
+ MOCK_METHOD(void, signalRequestIDRFrame, (), (override));
+ MOCK_METHOD(void, signalSetParameters, (const sp<AMessage> &msg), (override));
+ MOCK_METHOD(void, signalEndOfInputStream, (), (override));
+
+ std::shared_ptr<BufferChannelBase> getBufferChannel() override {
+ return mMockBufferChannel;
+ }
+
+ const std::unique_ptr<CodecCallback> &callback() {
+ return mCallback;
+ }
+
+ std::shared_ptr<MockBufferChannel> mMockBufferChannel;
+};
+
+class Counter {
+public:
+ Counter() = default;
+ explicit Counter(int32_t initCount) : mCount(initCount) {}
+ ~Counter() = default;
+
+ int32_t advance() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ ++mCount;
+ mCondition.notify_all();
+ return mCount;
+ }
+
+ template <typename Rep, typename Period, typename ...Args>
+ int32_t waitFor(const std::chrono::duration<Rep, Period> &duration, Args... values) {
+ std::initializer_list<int32_t> list = {values...};
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait_for(
+ lock,
+ duration,
+ [&list, this]{
+ return std::find(list.begin(), list.end(), mCount) != list.end();
+ });
+ return mCount;
+ }
+
+ template <typename ...Args>
+ int32_t wait(Args... values) {
+ std::initializer_list<int32_t> list = {values...};
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(
+ lock,
+ [&list, this]{
+ return std::find(list.begin(), list.end(), mCount) != list.end();
+ });
+ return mCount;
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ int32_t mCount = 0;
+};
+
+} // namespace android
+
+using namespace android;
+using ::testing::_;
+
+static sp<MediaCodec> SetupMediaCodec(
+ const AString &owner,
+ const AString &codecName,
+ const AString &mediaType,
+ const sp<ALooper> &looper,
+ std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase) {
+ std::shared_ptr<MediaCodecListWriter> listWriter =
+ MediaTestHelper::CreateCodecListWriter();
+ std::unique_ptr<MediaCodecInfoWriter> infoWriter = listWriter->addMediaCodecInfo();
+ infoWriter->setName(codecName.c_str());
+ infoWriter->setOwner(owner.c_str());
+ infoWriter->addMediaType(mediaType.c_str());
+ std::vector<sp<MediaCodecInfo>> codecInfos;
+ MediaTestHelper::WriteCodecInfos(listWriter, &codecInfos);
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo =
+ [codecInfos](const AString &name, sp<MediaCodecInfo> *info) -> status_t {
+ auto it = std::find_if(
+ codecInfos.begin(), codecInfos.end(),
+ [&name](const sp<MediaCodecInfo> &info) {
+ return name.equalsIgnoreCase(info->getCodecName());
+ });
+
+ *info = (it == codecInfos.end()) ? nullptr : *it;
+ return (*info) ? OK : NAME_NOT_FOUND;
+ };
+
+ looper->start();
+ return MediaTestHelper::CreateCodec(
+ codecName, looper, getCodecBase, getCodecInfo);
+}
+
+TEST(MediaCodecTest, ReclaimReleaseRace) {
+ // Test scenario:
+ //
+ // 1) ResourceManager thread calls reclaim(), message posted to
+ // MediaCodec looper thread.
+ // 2) MediaCodec looper thread calls initiateShutdown(), shutdown being
+ // handled at the component thread.
+ // 3) Client thread calls release(), message posted to & handle at
+ // MediaCodec looper thread.
+ // 4) MediaCodec looper thread may call initiateShutdown().
+ // 5) initiateShutdown() from 2) is handled at onReleaseComplete() event
+ // posted to MediaCodec looper thread.
+ // 6) If called, initiateShutdown() from 4) is handled and
+ // onReleaseComplete() event posted to MediaCodec looper thread.
+
+ static const AString kCodecName{"test.codec"};
+ static const AString kCodecOwner{"nobody"};
+ static const AString kMediaType{"video/x-test"};
+
+ enum {
+ kInit,
+ kShutdownFromReclaimReceived,
+ kReleaseCalled,
+ };
+ Counter counter{kInit};
+ sp<MockCodec> mockCodec;
+ std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
+ [&mockCodec, &counter](const AString &, const char *) {
+ mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
+ // No mock setup, as we don't expect any buffer operations
+ // in this scenario.
+ });
+ ON_CALL(*mockCodec, initiateAllocateComponent(_))
+ .WillByDefault([mockCodec](const sp<AMessage> &) {
+ mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
+ });
+ ON_CALL(*mockCodec, initiateShutdown(_))
+ .WillByDefault([mockCodec, &counter](bool) {
+ int32_t stage = counter.wait(kInit, kReleaseCalled);
+ if (stage == kInit) {
+ // Mark that 2) happened, so test can proceed to 3)
+ counter.advance();
+ } else if (stage == kReleaseCalled) {
+ // Handle 6)
+ mockCodec->callback()->onReleaseCompleted();
+ }
+ });
+ return mockCodec;
+ };
+
+ sp<ALooper> looper{new ALooper};
+ sp<MediaCodec> codec = SetupMediaCodec(
+ kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
+ ASSERT_NE(nullptr, codec) << "Codec must not be null";
+ ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
+ std::promise<void> reclaimCompleted;
+ std::promise<void> releaseCompleted;
+ Counter threadExitCounter;
+ std::thread([codec, &reclaimCompleted]{
+ // Simulate ResourceManager thread. Proceed with 1)
+ MediaTestHelper::Reclaim(codec, true /* force */);
+ reclaimCompleted.set_value();
+ }).detach();
+ std::thread([codec, &counter, &releaseCompleted]{
+ // Simulate client thread. Wait until 2) is complete
+ (void)counter.wait(kShutdownFromReclaimReceived);
+ // Proceed to 3), and mark that 5) is ready to happen.
+ // NOTE: it's difficult to pinpoint when 4) happens, so we will sleep
+ // to meet the timing.
+ counter.advance();
+ codec->release();
+ releaseCompleted.set_value();
+ }).detach();
+ std::thread([mockCodec, &counter]{
+ // Simulate component thread. Wait until 3) is complete
+ (void)counter.wait(kReleaseCalled);
+ // We want 4) to complete before moving forward, but it is hard to
+ // wait for this exact event. Just sleep so that the other thread can
+ // proceed and complete 4).
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ // Proceed to 5).
+ mockCodec->callback()->onReleaseCompleted();
+ }).detach();
+ EXPECT_EQ(
+ std::future_status::ready,
+ reclaimCompleted.get_future().wait_for(std::chrono::seconds(5)))
+ << "reclaim timed out";
+ EXPECT_EQ(
+ std::future_status::ready,
+ releaseCompleted.get_future().wait_for(std::chrono::seconds(5)))
+ << "release timed out";
+ looper->stop();
+}
+
+TEST(MediaCodecTest, ErrorWhileStopping) {
+ // Test scenario:
+ //
+ // 1) Client thread calls stop(); MediaCodec looper thread calls
+ // initiateShutdown(); shutdown is being handled at the component thread.
+ // 2) Error occurred, but the shutdown operation is still being done.
+ // 3) MediaCodec looper thread handles the error.
+ // 4) Component thread completes shutdown and posts onStopCompleted()
+
+ static const AString kCodecName{"test.codec"};
+ static const AString kCodecOwner{"nobody"};
+ static const AString kMediaType{"video/x-test"};
+
+ std::promise<void> errorOccurred;
+ sp<MockCodec> mockCodec;
+ std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
+ [&mockCodec, &errorOccurred](const AString &, const char *) {
+ mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
+ // No mock setup, as we don't expect any buffer operations
+ // in this scenario.
+ });
+ ON_CALL(*mockCodec, initiateAllocateComponent(_))
+ .WillByDefault([mockCodec](const sp<AMessage> &) {
+ mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
+ });
+ ON_CALL(*mockCodec, initiateConfigureComponent(_))
+ .WillByDefault([mockCodec](const sp<AMessage> &msg) {
+ mockCodec->callback()->onComponentConfigured(
+ msg->dup(), msg->dup());
+ });
+ ON_CALL(*mockCodec, initiateStart())
+ .WillByDefault([mockCodec]() {
+ mockCodec->callback()->onStartCompleted();
+ });
+ ON_CALL(*mockCodec, initiateShutdown(true))
+ .WillByDefault([mockCodec, &errorOccurred](bool) {
+ mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+ // Mark that 1) and 2) are complete.
+ errorOccurred.set_value();
+ });
+ ON_CALL(*mockCodec, initiateShutdown(false))
+ .WillByDefault([mockCodec](bool) {
+ mockCodec->callback()->onReleaseCompleted();
+ });
+ return mockCodec;
+ };
+
+ sp<ALooper> looper{new ALooper};
+ sp<MediaCodec> codec = SetupMediaCodec(
+ kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
+ ASSERT_NE(nullptr, codec) << "Codec must not be null";
+ ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
+
+ std::thread([mockCodec, &errorOccurred]{
+ // Simulate component thread that handles stop()
+ errorOccurred.get_future().wait();
+ // Error occurred but shutdown request still got processed.
+ mockCodec->callback()->onStopCompleted();
+ }).detach();
+
+ codec->configure(new AMessage, nullptr, nullptr, 0);
+ codec->start();
+ codec->stop();
+ // Sleep here to give time for the MediaCodec looper thread
+ // to process the messages.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ codec->release();
+ looper->stop();
+}
diff --git a/media/libstagefright/tests/mediacodec/MediaTestHelper.cpp b/media/libstagefright/tests/mediacodec/MediaTestHelper.cpp
new file mode 100644
index 0000000..bbe3c05
--- /dev/null
+++ b/media/libstagefright/tests/mediacodec/MediaTestHelper.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecListWriter.h>
+
+#include "MediaTestHelper.h"
+
+namespace android {
+
+// static
+sp<MediaCodec> MediaTestHelper::CreateCodec(
+ const AString &name,
+ const sp<ALooper> &looper,
+ std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase,
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo) {
+ sp<MediaCodec> codec = new MediaCodec(
+ looper, MediaCodec::kNoPid, MediaCodec::kNoUid, getCodecBase, getCodecInfo);
+ if (codec->init(name) != OK) {
+ return nullptr;
+ }
+ return codec;
+}
+
+// static
+void MediaTestHelper::Reclaim(const sp<MediaCodec> &codec, bool force) {
+ codec->reclaim(force);
+}
+
+// static
+std::shared_ptr<MediaCodecListWriter> MediaTestHelper::CreateCodecListWriter() {
+ return std::shared_ptr<MediaCodecListWriter>(new MediaCodecListWriter);
+}
+
+// static
+void MediaTestHelper::WriteCodecInfos(
+ const std::shared_ptr<MediaCodecListWriter> &writer,
+ std::vector<sp<MediaCodecInfo>> *codecInfos) {
+ writer->writeCodecInfos(codecInfos);
+}
+
+} // namespace android
diff --git a/media/libstagefright/tests/mediacodec/MediaTestHelper.h b/media/libstagefright/tests/mediacodec/MediaTestHelper.h
new file mode 100644
index 0000000..f3d6110
--- /dev/null
+++ b/media/libstagefright/tests/mediacodec/MediaTestHelper.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_TEST_HELPER_H_
+
+#define MEDIA_TEST_HELPER_H_
+
+#include <media/stagefright/foundation/AString.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+struct ALooper;
+struct CodecBase;
+struct MediaCodec;
+struct MediaCodecInfo;
+struct MediaCodecListWriter;
+
+class MediaTestHelper {
+public:
+ // MediaCodec
+ static sp<MediaCodec> CreateCodec(
+ const AString &name,
+ const sp<ALooper> &looper,
+ std::function<sp<CodecBase>(const AString &, const char *)> getCodecBase,
+ std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo);
+ static void Reclaim(const sp<MediaCodec> &codec, bool force);
+
+ // MediaCodecListWriter
+ static std::shared_ptr<MediaCodecListWriter> CreateCodecListWriter();
+ static void WriteCodecInfos(
+ const std::shared_ptr<MediaCodecListWriter> &writer,
+ std::vector<sp<MediaCodecInfo>> *codecInfos);
+};
+
+} // namespace android
+
+#endif // MEDIA_TEST_HELPER_H_
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index f033d5c..91590e1 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -131,6 +131,7 @@
"statsd_codec.cpp",
"statsd_drm.cpp",
"statsd_extractor.cpp",
+ "statsd_mediaparser.cpp",
"statsd_nuplayer.cpp",
"statsd_recorder.cpp",
"StringUtils.cpp"
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 48e766e..bf6e428 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -468,6 +468,7 @@
"codec",
"extractor",
"mediadrm",
+ "mediaparser",
"nuplayer",
}) {
if (key == allowedKey) {
diff --git a/services/mediametrics/iface_statsd.cpp b/services/mediametrics/iface_statsd.cpp
index 6e51f72..16204de 100644
--- a/services/mediametrics/iface_statsd.cpp
+++ b/services/mediametrics/iface_statsd.cpp
@@ -64,6 +64,7 @@
{ "drmmanager", statsd_drmmanager },
{ "extractor", statsd_extractor },
{ "mediadrm", statsd_mediadrm },
+ { "mediaparser", statsd_mediaparser },
{ "nuplayer", statsd_nuplayer },
{ "nuplayer2", statsd_nuplayer },
{ "recorder", statsd_recorder },
diff --git a/services/mediametrics/iface_statsd.h b/services/mediametrics/iface_statsd.h
index 19505a4..9b49556 100644
--- a/services/mediametrics/iface_statsd.h
+++ b/services/mediametrics/iface_statsd.h
@@ -25,6 +25,7 @@
extern bool statsd_audiotrack(const mediametrics::Item *);
extern bool statsd_codec(const mediametrics::Item *);
extern bool statsd_extractor(const mediametrics::Item *);
+extern bool statsd_mediaparser(const mediametrics::Item *);
extern bool statsd_nuplayer(const mediametrics::Item *);
extern bool statsd_recorder(const mediametrics::Item *);
diff --git a/services/mediametrics/statsd_mediaparser.cpp b/services/mediametrics/statsd_mediaparser.cpp
new file mode 100644
index 0000000..3258ebf
--- /dev/null
+++ b/services/mediametrics/statsd_mediaparser.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "statsd_mediaparser"
+#include <utils/Log.h>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <statslog.h>
+
+#include "MediaMetricsService.h"
+#include "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.pb.h"
+#include "iface_statsd.h"
+
+namespace android {
+
+bool statsd_mediaparser(const mediametrics::Item *item)
+{
+ if (item == nullptr) {
+ return false;
+ }
+
+ // statsd wrapper data.
+ const nsecs_t timestamp = MediaMetricsService::roundTime(item->getTimestamp());
+ std::string pkgName = item->getPkgName();
+ int64_t pkgVersionCode = item->getPkgVersionCode();
+
+ std::string parserName;
+ item->getString("android.media.mediaparser.parserName", &parserName);
+
+ int32_t createdByName = -1;
+ item->getInt32("android.media.mediaparser.createdByName", &createdByName);
+
+ std::string parserPool;
+ item->getString("android.media.mediaparser.parserPool", &parserPool);
+
+ std::string lastException;
+ item->getString("android.media.mediaparser.lastException", &lastException);
+
+ int64_t resourceByteCount = -1;
+ item->getInt64("android.media.mediaparser.resourceByteCount", &resourceByteCount);
+
+ int64_t durationMillis = -1;
+ item->getInt64("android.media.mediaparser.durationMillis", &durationMillis);
+
+ std::string trackMimeTypes;
+ item->getString("android.media.mediaparser.trackMimeTypes", &trackMimeTypes);
+
+ std::string trackCodecs;
+ item->getString("android.media.mediaparser.trackCodecs", &trackCodecs);
+
+ std::string alteredParameters;
+ item->getString("android.media.mediaparser.alteredParameters", &alteredParameters);
+
+ int32_t videoWidth = -1;
+ item->getInt32("android.media.mediaparser.videoWidth", &videoWidth);
+
+ int32_t videoHeight = -1;
+ item->getInt32("android.media.mediaparser.videoHeight", &videoHeight);
+
+ if (enabled_statsd) {
+ (void) android::util::stats_write(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED,
+ timestamp,
+ pkgName.c_str(),
+ pkgVersionCode,
+ parserName.c_str(),
+ createdByName,
+ parserPool.c_str(),
+ lastException.c_str(),
+ resourceByteCount,
+ durationMillis,
+ trackMimeTypes.c_str(),
+ trackCodecs.c_str(),
+ alteredParameters.c_str(),
+ videoWidth,
+ videoHeight);
+ } else {
+ ALOGV("NOT sending MediaParser media metrics.");
+ }
+
+ return true;
+}
+
+} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 3d36f8e..db06a36 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -575,13 +575,19 @@
}
}
+ *_aidl_return = reclaimInternal(clients);
+ return Status::ok();
+}
+
+bool ResourceManagerService::reclaimInternal(
+ const Vector<std::shared_ptr<IResourceManagerClient>> &clients) {
if (clients.size() == 0) {
- return Status::ok();
+ return false;
}
std::shared_ptr<IResourceManagerClient> failedClient;
for (size_t i = 0; i < clients.size(); ++i) {
- log = String8::format("reclaimResource from client %p", clients[i].get());
+ String8 log = String8::format("reclaimResource from client %p", clients[i].get());
mServiceLog->add(log);
bool success;
Status status = clients[i]->reclaimResource(&success);
@@ -592,8 +598,7 @@
}
if (failedClient == NULL) {
- *_aidl_return = true;
- return Status::ok();
+ return true;
}
{
@@ -618,7 +623,7 @@
}
}
- return Status::ok();
+ return false;
}
Status ResourceManagerService::overridePid(
@@ -681,6 +686,36 @@
return Status::ok();
}
+Status ResourceManagerService::reclaimResourcesFromClientsPendingRemoval(int32_t pid) {
+ String8 log = String8::format("reclaimResourcesFromClientsPendingRemoval(pid %d)", pid);
+ mServiceLog->add(log);
+
+ Vector<std::shared_ptr<IResourceManagerClient>> clients;
+ {
+ Mutex::Autolock lock(mLock);
+ if (!mProcessInfo->isValidPid(pid)) {
+ ALOGE("Rejected reclaimResourcesFromClientsPendingRemoval call with invalid pid.");
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ for (MediaResource::Type type : {MediaResource::Type::kSecureCodec,
+ MediaResource::Type::kNonSecureCodec,
+ MediaResource::Type::kGraphicMemory,
+ MediaResource::Type::kDrmSession}) {
+ std::shared_ptr<IResourceManagerClient> client;
+ if (getBiggestClient_l(pid, type, &client, true /* pendingRemovalOnly */)) {
+ clients.add(client);
+ break;
+ }
+ }
+ }
+
+ if (!clients.empty()) {
+ reclaimInternal(clients);
+ }
+ return Status::ok();
+}
+
bool ResourceManagerService::getPriority_l(int pid, int* priority) {
int newPid = pid;
@@ -804,7 +839,8 @@
bool pendingRemovalOnly) {
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
- ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid);
+ ALOGE_IF(!pendingRemovalOnly,
+ "getBiggestClient_l: can't find resource info for pid %d", pid);
return false;
}
@@ -828,7 +864,9 @@
}
if (clientTemp == NULL) {
- ALOGE("getBiggestClient_l: can't find resource type %s for pid %d", asString(type), pid);
+ ALOGE_IF(!pendingRemovalOnly,
+ "getBiggestClient_l: can't find resource type %s for pid %d",
+ asString(type), pid);
return false;
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index ee982b7..3972d23 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -127,11 +127,18 @@
Status markClientForPendingRemoval(int32_t pid, int64_t clientId) override;
+ Status reclaimResourcesFromClientsPendingRemoval(int32_t pid) override;
+
Status removeResource(int pid, int64_t clientId, bool checkValid);
private:
friend class ResourceManagerServiceTest;
+ // Reclaims resources from |clients|. Returns true if reclaim succeeded
+ // for all clients.
+ bool reclaimInternal(
+ const Vector<std::shared_ptr<IResourceManagerClient>> &clients);
+
// Gets the list of all the clients who own the specified resource type.
// Returns false if any client belongs to a process with higher priority than the
// calling process. The clients will remain unchanged if returns false.
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 702935d..a6ecc09 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -520,6 +520,30 @@
// clean up client 3 which still left
mService->removeClient(kTestPid2, getId(mTestClient3));
}
+
+ {
+ addResource();
+ mService->mSupportsSecureWithNonSecureCodec = true;
+
+ mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient2));
+
+ // client marked for pending removal got reclaimed
+ EXPECT_TRUE(mService->reclaimResourcesFromClientsPendingRemoval(kTestPid2).isOk());
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
+
+ // No more clients marked for removal
+ EXPECT_TRUE(mService->reclaimResourcesFromClientsPendingRemoval(kTestPid2).isOk());
+ verifyClients(false /* c1 */, false /* c2 */, false /* c3 */);
+
+ mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient3));
+
+ // client marked for pending removal got reclaimed
+ EXPECT_TRUE(mService->reclaimResourcesFromClientsPendingRemoval(kTestPid2).isOk());
+ verifyClients(false /* c1 */, false /* c2 */, true /* c3 */);
+
+ // clean up client 1 which still left
+ mService->removeClient(kTestPid1, getId(mTestClient1));
+ }
}
void testRemoveClient() {