Merge "Revert "Disable integer sanitization for amrwbenc""
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 20a23e0..3505154 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -25,6 +25,7 @@
const int OutputConfiguration::INVALID_ROTATION = -1;
+const int OutputConfiguration::INVALID_SET_ID = -1;
// Read empty strings without printing a false error message.
String16 OutputConfiguration::readMaybeEmptyString16(const Parcel& parcel) {
@@ -45,6 +46,10 @@
return mRotation;
}
+int OutputConfiguration::getSurfaceSetID() const {
+ return mSurfaceSetID;
+}
+
OutputConfiguration::OutputConfiguration(const Parcel& parcel) {
status_t err;
int rotation = 0;
@@ -55,24 +60,36 @@
return;
}
+ int setID = INVALID_SET_ID;
+ if ((err = parcel.readInt32(&setID)) != OK) {
+ ALOGE("%s: Failed to read surface set ID from parcel", __FUNCTION__);
+ mGbp = NULL;
+ mSurfaceSetID = INVALID_SET_ID;
+ return;
+ }
+
String16 name = readMaybeEmptyString16(parcel);
const sp<IGraphicBufferProducer>& gbp =
interface_cast<IGraphicBufferProducer>(parcel.readStrongBinder());
mGbp = gbp;
mRotation = rotation;
+ mSurfaceSetID = setID;
ALOGV("%s: OutputConfiguration: bp = %p, name = %s", __FUNCTION__,
gbp.get(), String8(name).string());
}
-OutputConfiguration::OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation) {
+OutputConfiguration::OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+ int surfaceSetID) {
mGbp = gbp;
mRotation = rotation;
+ mSurfaceSetID = surfaceSetID;
}
status_t OutputConfiguration::writeToParcel(Parcel& parcel) const {
parcel.writeInt32(mRotation);
+ parcel.writeInt32(mSurfaceSetID);
parcel.writeString16(String16("unknown_name")); // name of surface
sp<IBinder> b(IInterface::asBinder(mGbp));
parcel.writeStrongBinder(b);
diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h
index 5bcbe15..d6b74a9 100644
--- a/include/camera/camera2/OutputConfiguration.h
+++ b/include/camera/camera2/OutputConfiguration.h
@@ -28,8 +28,10 @@
public:
static const int INVALID_ROTATION;
+ static const int INVALID_SET_ID;
sp<IGraphicBufferProducer> getGraphicBufferProducer() const;
int getRotation() const;
+ int getSurfaceSetID() const;
/**
* Keep impl up-to-date with OutputConfiguration.java in frameworks/base
@@ -39,12 +41,13 @@
// getRotation will be INVALID_ROTATION if error occurred
OutputConfiguration(const Parcel& parcel);
- OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation);
+ OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+ int surfaceSetID = INVALID_SET_ID);
private:
sp<IGraphicBufferProducer> mGbp;
int mRotation;
-
+ int mSurfaceSetID;
// helper function
static String16 readMaybeEmptyString16(const Parcel& parcel);
};
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 6c14a94..91341b8 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -185,6 +185,9 @@
// MPEG user data offsets
kKeyMpegUserData = 'mpud', // size_t[]
+
+ // Size of NALU length in mkv/mp4
+ kKeyNalLengthSize = 'nals', // int32_t
};
enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 1ba9545..b8bb824 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -114,6 +114,7 @@
bool getTotalBitrate(int64_t *bitRate) const;
void updateDurationAndBitrate();
+ status_t appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
};
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index 034cb35..c6035bd 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -154,6 +154,18 @@
media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
/**
+ * Dynamically sets the output surface of a codec.
+ *
+ * This can only be used if the codec was configured with an output surface. The
+ * new output surface should have a compatible usage type to the original output surface.
+ * E.g. codecs may not support switching from a SurfaceTexture (GPU readable) output
+ * to ImageReader (software readable) output.
+ *
+ * For more details, see the Java documentation for MediaCodec.setOutputSurface.
+ */
+media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface);
+
+/**
* If you are done with a buffer, use this call to update its surface timestamp
* and return it to the codec to render it on the output surface. If you
* have not specified an output surface when configuring this video codec,
@@ -164,7 +176,6 @@
media_status_t AMediaCodec_releaseOutputBufferAtTime(
AMediaCodec *mData, size_t idx, int64_t timestampNs);
-
typedef enum {
AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 42f1f40..f48bac1 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -89,6 +89,7 @@
preproc_session_t *session; // session the effect is on
const preproc_ops_t *ops; // effect ops table
preproc_fx_handle_t engine; // handle on webRTC engine
+ uint32_t type; // subtype of effect
#ifdef DUAL_MIC_TEST
bool aux_channels_on; // support auxiliary channels
size_t cur_channel_config; // current auciliary channel configuration
@@ -559,6 +560,21 @@
ALOGV("NsInit");
webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine);
ns->set_level(kNsDefaultLevel);
+ webrtc::Config config;
+ std::vector<webrtc::Point> geometry;
+ // TODO(aluebs): Make the geometry settable.
+ geometry.push_back(webrtc::Point(-0.03f, 0.f, 0.f));
+ geometry.push_back(webrtc::Point(-0.01f, 0.f, 0.f));
+ geometry.push_back(webrtc::Point(0.01f, 0.f, 0.f));
+ geometry.push_back(webrtc::Point(0.03f, 0.f, 0.f));
+ // The geometry needs to be set with Beamforming enabled.
+ config.Set<webrtc::Beamforming>(
+ new webrtc::Beamforming(true, geometry));
+ effect->session->apm->SetExtraOptions(config);
+ config.Set<webrtc::Beamforming>(
+ new webrtc::Beamforming(false, geometry));
+ effect->session->apm->SetExtraOptions(config);
+ effect->type = NS_TYPE_SINGLE_CHANNEL;
return 0;
}
@@ -584,11 +600,35 @@
return status;
}
-int NsSetParameter (preproc_effect_t *effect __unused,
- void *pParam __unused,
- void *pValue __unused)
+int NsSetParameter (preproc_effect_t *effect, void *pParam, void *pValue)
{
int status = 0;
+ webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine);
+ uint32_t param = *(uint32_t *)pParam;
+ uint32_t value = *(uint32_t *)pValue;
+ switch(param) {
+ case NS_PARAM_LEVEL:
+ ns->set_level((webrtc::NoiseSuppression::Level)value);
+ ALOGV("NsSetParameter() level %d", value);
+ break;
+ case NS_PARAM_TYPE:
+ {
+ webrtc::Config config;
+ std::vector<webrtc::Point> geometry;
+ bool is_beamforming_enabled =
+ value == NS_TYPE_MULTI_CHANNEL && ns->is_enabled();
+ config.Set<webrtc::Beamforming>(
+ new webrtc::Beamforming(is_beamforming_enabled, geometry));
+ effect->session->apm->SetExtraOptions(config);
+ effect->type = value;
+ ALOGV("NsSetParameter() type %d", value);
+ break;
+ }
+ default:
+ ALOGW("NsSetParameter() unknown param %08x value %08x", param, value);
+ status = -EINVAL;
+ }
+
return status;
}
@@ -597,6 +637,12 @@
webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine);
ALOGV("NsEnable ns %p", ns);
ns->Enable(true);
+ if (effect->type == NS_TYPE_MULTI_CHANNEL) {
+ webrtc::Config config;
+ std::vector<webrtc::Point> geometry;
+ config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
+ effect->session->apm->SetExtraOptions(config);
+ }
}
void NsDisable(preproc_effect_t *effect)
@@ -604,6 +650,10 @@
ALOGV("NsDisable");
webrtc::NoiseSuppression *ns = static_cast<webrtc::NoiseSuppression *>(effect->engine);
ns->Enable(false);
+ webrtc::Config config;
+ std::vector<webrtc::Point> geometry;
+ config.Set<webrtc::Beamforming>(new webrtc::Beamforming(false, geometry));
+ effect->session->apm->SetExtraOptions(config);
}
static const preproc_ops_t sNsOps = {
@@ -897,17 +947,6 @@
config->inputCfg.samplingRate, config->inputCfg.channels);
int status;
- // if at least one process is enabled, do not accept configuration changes
- if (session->enabledMsk) {
- if (session->samplingRate != config->inputCfg.samplingRate ||
- session->inChannelCount != inCnl ||
- session->outChannelCount != outCnl) {
- return -ENOSYS;
- } else {
- return 0;
- }
- }
-
// AEC implementation is limited to 16kHz
if (config->inputCfg.samplingRate >= 32000 && !(session->createdMsk & (1 << PREPROC_AEC))) {
session->apmSamplingRate = 32000;
@@ -1294,8 +1333,7 @@
}
session->framesIn = 0;
}
- session->procFrame->samples_per_channel_ =
- session->apmFrameCount * session->inChannelCount;
+ session->procFrame->samples_per_channel_ = session->apmFrameCount;
effect->session->apm->ProcessStream(session->procFrame);
@@ -1822,8 +1860,7 @@
}
session->framesRev = 0;
}
- session->revFrame->samples_per_channel_ =
- session->apmFrameCount * session->inChannelCount;
+ session->revFrame->samples_per_channel_ = session->apmFrameCount;
effect->session->apm->AnalyzeReverseStream(session->revFrame);
return 0;
} else {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5c13633..cede584 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -2142,11 +2142,7 @@
mPausedForBuffering = true;
onPause();
}
- // fall-thru
- }
- case Source::kWhatBufferingStart:
- {
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
break;
}
@@ -2164,11 +2160,7 @@
onResume();
}
}
- // fall-thru
- }
- case Source::kWhatBufferingEnd:
- {
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0);
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index ce87f87..4678956 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -525,7 +525,10 @@
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
msg->setBuffer("buffer", buffer);
mCSDsToSubmit.removeAt(0);
- CHECK(onInputBufferFetched(msg));
+ if (!onInputBufferFetched(msg)) {
+ handleError(UNKNOWN_ERROR);
+ return false;
+ }
return true;
}
@@ -862,7 +865,11 @@
// copy into codec buffer
if (buffer != codecBuffer) {
- CHECK_LE(buffer->size(), codecBuffer->capacity());
+ if (buffer->size() > codecBuffer->capacity()) {
+ handleError(ERROR_BUFFER_TOO_SMALL);
+ mDequeuedInputBuffers.push_back(bufferIx);
+ return false;
+ }
codecBuffer->setRange(0, buffer->size());
memcpy(codecBuffer->data(), buffer->data(), buffer->size());
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 65e3a6e..332fef6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -334,8 +334,8 @@
// down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
// current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
// getCurrentPosition here.
- int msec;
- getCurrentPosition(&msec);
+ int unused;
+ getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
@@ -364,11 +364,12 @@
status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
status_t err = mPlayer->setPlaybackSettings(rate);
if (err == OK) {
+ // try to update position
+ int unused;
+ getCurrentPosition(&unused);
Mutex::Autolock autoLock(mLock);
if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
mState = STATE_PAUSED;
- // try to update position
- (void)mPlayer->getCurrentPosition(&mPositionUs);
notifyListener_l(MEDIA_PAUSED);
} else if (rate.mSpeed != 0.f && mState == STATE_PAUSED) {
mState = STATE_RUNNING;
@@ -423,7 +424,7 @@
int64_t tempUs = 0;
{
Mutex::Autolock autoLock(mLock);
- if (mSeekInProgress || mState == STATE_PAUSED) {
+ if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
*msec = (int)divRound(tempUs, (int64_t)(1000));
return OK;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index fba4540..0176eafa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -46,8 +46,6 @@
kWhatFlagsChanged,
kWhatVideoSizeChanged,
kWhatBufferingUpdate,
- kWhatBufferingStart,
- kWhatBufferingEnd,
kWhatPauseOnBufferingStart,
kWhatResumeOnBufferingEnd,
kWhatCacheStats,
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index c861fd1..2ecab6d 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -725,7 +725,7 @@
mBuffering = true;
sp<AMessage> notify = dupNotify();
- notify->setInt32("what", kWhatBufferingStart);
+ notify->setInt32("what", kWhatPauseOnBufferingStart);
notify->post();
}
}
@@ -741,7 +741,7 @@
mBuffering = false;
sp<AMessage> notify = dupNotify();
- notify->setInt32("what", kWhatBufferingEnd);
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
notify->post();
}
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 67d9921..8809640 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -449,6 +449,59 @@
return OK;
}
+status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
+ int32_t numPageSamples;
+ if (!info->mSample->meta_data()->findInt32(
+ kKeyValidSamples, &numPageSamples)) {
+ numPageSamples = -1;
+ }
+
+ memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+ &numPageSamples,
+ sizeof(numPageSamples));
+
+ uint32_t type;
+ const void *data;
+ size_t size, size2;
+ if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ // Signal numPageSamples (a plain int32_t) is appended at the end,
+ // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
+ if (SIZE_MAX - size < sizeof(int32_t)) {
+ return -ENOMEM;
+ }
+
+ size_t newSize = size + sizeof(int32_t);
+ sp<ABuffer> abuf = new ABuffer(newSize);
+ uint8_t *adata = static_cast<uint8_t *>(abuf->data());
+ if (adata == NULL) {
+ return -ENOMEM;
+ }
+
+ // append 0 to encrypted sizes
+ int32_t zero = 0;
+ memcpy(adata, data, size);
+ memcpy(adata + size, &zero, sizeof(zero));
+ info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
+
+ if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
+ if (size2 != size) {
+ return ERROR_MALFORMED;
+ }
+ memcpy(adata, data, size);
+ } else {
+ // if sample meta data does not include plain size array, assume filled with zeros,
+ // i.e. entire buffer is encrypted
+ memset(adata, 0, size);
+ }
+ // append sizeof(numPageSamples) to plain sizes.
+ int32_t int32Size = sizeof(numPageSamples);
+ memcpy(adata + size, &int32Size, sizeof(int32Size));
+ info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
+ }
+
+ return OK;
+}
+
status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
Mutex::Autolock autoLock(mLock);
@@ -478,21 +531,16 @@
memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+ status_t err = OK;
if (info->mTrackFlags & kIsVorbis) {
- int32_t numPageSamples;
- if (!info->mSample->meta_data()->findInt32(
- kKeyValidSamples, &numPageSamples)) {
- numPageSamples = -1;
- }
-
- memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
- &numPageSamples,
- sizeof(numPageSamples));
+ err = appendVorbisNumPageSamples(info, buffer);
}
- buffer->setRange(0, sampleSize);
+ if (err == OK) {
+ buffer->setRange(0, sampleSize);
+ }
- return OK;
+ return err;
}
status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2a8b635..17fa239 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -461,6 +461,13 @@
msg->setBuffer("csd-2", buffer);
}
+ // TODO expose "crypto-key"/kKeyCryptoKey through public api
+ if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ msg->setBuffer("crypto-key", buffer);
+ memcpy(buffer->data(), data, size);
+ }
+
*format = msg;
return OK;
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 193408c..b0cbf08 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -7,6 +7,7 @@
LOCAL_C_INCLUDES:= \
$(TOP)/external/libvpx/libwebm \
$(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/av/media/libstagefright/include \
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
LOCAL_CLANG := true
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 82b471f..861bdc5 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -19,9 +19,11 @@
#include <utils/Log.h>
#include "MatroskaExtractor.h"
+#include "avc_utils.h"
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -144,12 +146,13 @@
Type mType;
bool mIsAudio;
BlockIterator mBlockIter;
- size_t mNALSizeLen; // for type AVC
+ ssize_t mNALSizeLen; // for type AVC
List<MediaBuffer *> mPendingFrames;
status_t advance();
+ status_t setWebmBlockCryptoInfo(MediaBuffer *mbuf);
status_t readBlock();
void clearPendingFrames();
@@ -213,7 +216,7 @@
mBlockIter(mExtractor.get(),
mExtractor->mTracks.itemAt(index).mTrackNum,
index),
- mNALSizeLen(0) {
+ mNALSizeLen(-1) {
sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
const char *mime;
@@ -227,13 +230,18 @@
uint32_t dummy;
const uint8_t *avcc;
size_t avccSize;
- CHECK(meta->findData(
- kKeyAVCC, &dummy, (const void **)&avcc, &avccSize));
-
- CHECK_GE(avccSize, 5u);
-
- mNALSizeLen = 1 + (avcc[4] & 3);
- ALOGV("mNALSizeLen = %zu", mNALSizeLen);
+ int32_t nalSizeLen = 0;
+ if (meta->findInt32(kKeyNalLengthSize, &nalSizeLen)) {
+ if (nalSizeLen >= 0 && nalSizeLen <= 4) {
+ mNALSizeLen = nalSizeLen;
+ }
+ } else if (meta->findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)
+ && avccSize >= 5u) {
+ mNALSizeLen = 1 + (avcc[4] & 3);
+ ALOGV("mNALSizeLen = %zd", mNALSizeLen);
+ } else {
+ ALOGE("No mNALSizeLen");
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
mType = AAC;
}
@@ -244,6 +252,10 @@
}
status_t MatroskaSource::start(MetaData * /* params */) {
+ if (mType == AVC && mNALSizeLen < 0) {
+ return ERROR_MALFORMED;
+ }
+
mBlockIter.reset();
return OK;
@@ -492,6 +504,9 @@
}
int64_t BlockIterator::blockTimeUs() const {
+ if (mCluster == NULL || mBlockEntry == NULL) {
+ return -1;
+ }
return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll;
}
@@ -511,6 +526,72 @@
}
}
+status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBuffer *mbuf) {
+ if (mbuf->range_length() < 1 || mbuf->range_length() - 1 > INT32_MAX) {
+ // 1-byte signal
+ return ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = (const uint8_t *)mbuf->data() + mbuf->range_offset();
+ bool blockEncrypted = data[0] & 0x1;
+ if (blockEncrypted && mbuf->range_length() < 9) {
+ // 1-byte signal + 8-byte IV
+ return ERROR_MALFORMED;
+ }
+
+ sp<MetaData> meta = mbuf->meta_data();
+ if (blockEncrypted) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Signal Byte | |
+ * +-+-+-+-+-+-+-+-+ IV |
+ * | |
+ * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |
+ * |-+-+-+-+-+-+-+-+ |
+ * : Bytes 1..N of encrypted frame :
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ int32_t plainSizes[] = { 0 };
+ int32_t encryptedSizes[] = { static_cast<int32_t>(mbuf->range_length() - 9) };
+ uint8_t ctrCounter[16] = { 0 };
+ uint32_t type;
+ const uint8_t *keyId;
+ size_t keyIdSize;
+ sp<MetaData> trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+ CHECK(trackMeta->findData(kKeyCryptoKey, &type, (const void **)&keyId, &keyIdSize));
+ meta->setData(kKeyCryptoKey, 0, keyId, keyIdSize);
+ memcpy(ctrCounter, data + 1, 8);
+ meta->setData(kKeyCryptoIV, 0, ctrCounter, 16);
+ meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ mbuf->set_range(9, mbuf->range_length() - 9);
+ } else {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Signal Byte | |
+ * +-+-+-+-+-+-+-+-+ |
+ * : Bytes 1..N of unencrypted frame :
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ int32_t plainSizes[] = { static_cast<int32_t>(mbuf->range_length() - 1) };
+ int32_t encryptedSizes[] = { 0 };
+ meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ mbuf->set_range(1, mbuf->range_length() - 1);
+ }
+
+ return OK;
+}
+
status_t MatroskaSource::readBlock() {
CHECK(mPendingFrames.empty());
@@ -529,13 +610,19 @@
mbuf->meta_data()->setInt64(kKeyTime, timeUs);
mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
- long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data());
- if (n != 0) {
+ status_t err = frame.Read(mExtractor->mReader, static_cast<uint8_t *>(mbuf->data()));
+ if (err == OK
+ && mExtractor->mIsWebm
+ && mExtractor->mTracks.itemAt(mTrackIndex).mEncrypted) {
+ err = setWebmBlockCryptoInfo(mbuf);
+ }
+
+ if (err != OK) {
mPendingFrames.clear();
mBlockIter.advance();
mbuf->release();
- return ERROR_IO;
+ return err;
}
mPendingFrames.push_back(mbuf);
@@ -582,7 +669,7 @@
MediaBuffer *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());
- if (mType != AVC) {
+ if (mType != AVC || mNALSizeLen == 0) {
if (targetSampleTimeUs >= 0ll) {
frame->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
@@ -598,6 +685,9 @@
// followed by a corresponding number of bytes containing the fragment.
// We output all these fragments into a single large buffer separated
// by startcodes (0x00 0x00 0x00 0x01).
+ //
+ // When mNALSizeLen is 0, we assume the data is already in the format
+ // desired.
const uint8_t *srcPtr =
(const uint8_t *)frame->data() + frame->range_offset();
@@ -939,6 +1029,35 @@
return OK;
}
+status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) {
+ BlockIterator iter(this, trackInfo->mTrackNum, index);
+ if (iter.eos()) {
+ return ERROR_MALFORMED;
+ }
+
+ const mkvparser::Block *block = iter.block();
+ if (block->GetFrameCount() <= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ const mkvparser::Block::Frame &frame = block->GetFrame(0);
+ sp<ABuffer> abuf = new ABuffer(frame.len);
+ long n = frame.Read(mReader, abuf->data());
+ if (n != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ sp<MetaData> avcMeta = MakeAVCCodecSpecificData(abuf);
+ if (avcMeta == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ // Override the synthesized nal length size, which is arbitrary
+ avcMeta->setInt32(kKeyNalLengthSize, 0);
+ trackInfo->mMeta = avcMeta;
+ return OK;
+}
+
void MatroskaExtractor::addTracks() {
const mkvparser::Tracks *tracks = mSegment->GetTracks();
@@ -1051,10 +1170,31 @@
meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
mTracks.push();
- TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
+ size_t n = mTracks.size() - 1;
+ TrackInfo *trackInfo = &mTracks.editItemAt(n);
trackInfo->mTrackNum = track->GetNumber();
trackInfo->mMeta = meta;
trackInfo->mExtractor = this;
+
+ trackInfo->mEncrypted = false;
+ for(size_t i = 0; i < track->GetContentEncodingCount() && !trackInfo->mEncrypted; i++) {
+ const mkvparser::ContentEncoding *encoding = track->GetContentEncodingByIndex(i);
+ for(size_t j = 0; j < encoding->GetEncryptionCount(); j++) {
+ const mkvparser::ContentEncoding::ContentEncryption *encryption;
+ encryption = encoding->GetEncryptionByIndex(j);
+ meta->setData(kKeyCryptoKey, 0, encryption->key_id, encryption->key_id_len);
+ trackInfo->mEncrypted = true;
+ break;
+ }
+ }
+
+ if (!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) {
+ // Attempt to recover from AVC track without codec private data
+ err = synthesizeAVCC(trackInfo, n);
+ if (err != OK) {
+ mTracks.pop();
+ }
+ }
}
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 120ef82..a1d6b00 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -55,6 +55,7 @@
struct TrackInfo {
unsigned long mTrackNum;
+ bool mEncrypted;
sp<MetaData> mMeta;
const MatroskaExtractor *mExtractor;
Vector<const mkvparser::CuePoint*> mCuePoints;
@@ -74,6 +75,7 @@
bool mIsWebm;
int64_t mSeekPreRollNs;
+ status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index);
void addTracks();
void findThumbnails();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 8e36065..5bb2dcd 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -359,6 +359,15 @@
return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
}
+EXPORT
+media_status_t AMediaCodec_setOutputSurface(AMediaCodec *mData, ANativeWindow* window) {
+ sp<Surface> surface = NULL;
+ if (window != NULL) {
+ surface = (Surface*) window;
+ }
+ return translate_error(mData->mCodec->setSurface(surface));
+}
+
//EXPORT
media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback,
void *userdata) {
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 9ba8f3f..d416353 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -46,6 +46,7 @@
device3/Camera3ZslStream.cpp \
device3/Camera3DummyStream.cpp \
device3/StatusTracker.cpp \
+ device3/Camera3BufferManager.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
utils/AutoConditionLock.cpp
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index bd9fea3..240a33f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -456,7 +456,8 @@
return BAD_VALUE;
}
- int streamId = -1;
+ // TODO: Hookup the stream set id with upper layer.
+ int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
res = mDevice->createStream(surface, width, height, format, dataSpace,
static_cast<camera3_stream_rotation_t>
(outputConfiguration.getRotation()),
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7b083a3..6fd2b39 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -31,6 +31,7 @@
#include "camera/CaptureResult.h"
#include "common/CameraModule.h"
#include "gui/IGraphicBufferProducer.h"
+#include "device3/Camera3StreamInterface.h"
namespace android {
@@ -108,7 +109,8 @@
*/
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID) = 0;
/**
* Create an input stream of width, height, and format.
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.cpp b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
new file mode 100644
index 0000000..b2a3076
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.cpp
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Camera3-BufferManager"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "utils/CameraTraces.h"
+#include "Camera3BufferManager.h"
+
+namespace android {
+
+namespace camera3 {
+
+Camera3BufferManager::Camera3BufferManager(const sp<IGraphicBufferAlloc>& allocator) :
+ mAllocator(allocator) {
+ if (allocator == NULL) {
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ mAllocator = composer->createGraphicBufferAlloc();
+ if (mAllocator == NULL) {
+ ALOGE("createGraphicBufferAlloc failed");
+ }
+ }
+}
+
+Camera3BufferManager::~Camera3BufferManager() {
+}
+
+status_t Camera3BufferManager::registerStream(const StreamInfo& streamInfo) {
+ ATRACE_CALL();
+
+ int streamId = streamInfo.streamId;
+ int streamSetId = streamInfo.streamSetId;
+
+ if (streamId == CAMERA3_STREAM_ID_INVALID || streamSetId == CAMERA3_STREAM_SET_ID_INVALID) {
+ ALOGE("%s: Stream id (%d) or stream set id (%d) is invalid",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+ if (streamInfo.totalBufferCount > kMaxBufferCount || streamInfo.totalBufferCount == 0) {
+ ALOGE("%s: Stream id (%d) with stream set id (%d) total buffer count %zu is invalid",
+ __FUNCTION__, streamId, streamSetId, streamInfo.totalBufferCount);
+ return BAD_VALUE;
+ }
+ if (!streamInfo.isConfigured) {
+ ALOGE("%s: Stream (%d) is not configured", __FUNCTION__, streamId);
+ return BAD_VALUE;
+ }
+
+ // For Gralloc v1, try to allocate a buffer and see if it is successful, otherwise, stream
+ // buffer sharing for this newly added stream is not supported. For Gralloc v0, we don't
+ // need check this, as the buffers are not really shared between streams, the buffers are
+ // allocated for each stream individually, the allocation failure will be checked in
+ // getBufferForStream() call.
+ if (mGrallocVersion > HARDWARE_DEVICE_API_VERSION(0,1)) {
+ // TODO: To be implemented.
+
+ // In case allocation fails, return invalid operation
+ return INVALID_OPERATION;
+ }
+
+ Mutex::Autolock l(mLock);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ // Check if this stream was registered with different stream set ID, if so, error out.
+ for (size_t i = 0; i < mStreamSetMap.size(); i++) {
+ ssize_t streamIdx = mStreamSetMap[i].streamInfoMap.indexOfKey(streamId);
+ if (streamIdx != NAME_NOT_FOUND &&
+ mStreamSetMap[i].streamInfoMap[streamIdx].streamSetId != streamInfo.streamSetId) {
+ ALOGE("%s: It is illegal to register the same stream id with different stream set",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ }
+ // Check if there is an existing stream set registered; if not, create one; otherwise, add this
+ // stream info to the existing stream set entry.
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+ if (setIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream set %d is not registered to stream set map yet, create it.",
+ __FUNCTION__, streamSetId);
+ // Create stream info map, then add to mStreamsetMap.
+ StreamSet newStreamSet;
+ setIdx = mStreamSetMap.add(streamSetId, newStreamSet);
+ }
+ // Update stream set map and water mark.
+ StreamSet& currentStreamSet = mStreamSetMap.editValueAt(setIdx);
+ ssize_t streamIdx = currentStreamSet.streamInfoMap.indexOfKey(streamId);
+ if (streamIdx != NAME_NOT_FOUND) {
+ ALOGW("%s: stream %d was already registered with stream set %d",
+ __FUNCTION__, streamId, streamSetId);
+ return OK;
+ }
+ currentStreamSet.streamInfoMap.add(streamId, streamInfo);
+ currentStreamSet.handoutBufferCountMap.add(streamId, 0);
+
+ // The watermark should be the max of buffer count of each stream inside a stream set.
+ if (streamInfo.totalBufferCount > currentStreamSet.allocatedBufferWaterMark) {
+ currentStreamSet.allocatedBufferWaterMark = streamInfo.totalBufferCount;
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::unregisterStream(int streamId, int streamSetId) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: unregister stream %d with stream set %d", __FUNCTION__,
+ streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ ALOGE("%s: stream %d with set id %d wasn't properly registered to this buffer manager!",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+
+ // De-list all the buffers associated with this stream first.
+ StreamSet& currentSet = mStreamSetMap.editValueFor(streamSetId);
+ BufferList& freeBufs = currentSet.freeBuffers;
+ BufferCountMap& handOutBufferCounts = currentSet.handoutBufferCountMap;
+ InfoMap& infoMap = currentSet.streamInfoMap;
+ removeBuffersFromBufferListLocked(freeBufs, streamId);
+ handOutBufferCounts.removeItem(streamId);
+
+ // Remove the stream info from info map and recalculate the buffer count water mark.
+ infoMap.removeItem(streamId);
+ currentSet.allocatedBufferWaterMark = 0;
+ for (size_t i = 0; i < infoMap.size(); i++) {
+ if (infoMap[i].totalBufferCount > currentSet.allocatedBufferWaterMark) {
+ currentSet.allocatedBufferWaterMark = infoMap[i].totalBufferCount;
+ }
+ }
+
+ // Remove this stream set if all its streams have been removed.
+ if (freeBufs.size() == 0 && handOutBufferCounts.size() == 0 && infoMap.size() == 0) {
+ mStreamSetMap.removeItem(streamSetId);
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::getBufferForStream(int streamId, int streamSetId,
+ sp<GraphicBuffer>* gb, int* fenceFd) {
+ ATRACE_CALL();
+
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: get buffer for stream %d with stream set %d", __FUNCTION__,
+ streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)) {
+ ALOGE("%s: stream %d is not registered with stream set %d yet!!!",
+ __FUNCTION__, streamId, streamSetId);
+ return BAD_VALUE;
+ }
+
+ StreamSet &streamSet = mStreamSetMap.editValueFor(streamSetId);
+ GraphicBufferEntry buffer =
+ getFirstBufferFromBufferListLocked(streamSet.freeBuffers, streamId);
+
+ if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
+ // Allocate one if there is no free buffer available.
+ if (buffer.graphicBuffer == nullptr) {
+ const StreamInfo& info = streamSet.streamInfoMap.valueFor(streamId);
+ status_t res = OK;
+ buffer.fenceFd = -1;
+ buffer.graphicBuffer = mAllocator->createGraphicBuffer(
+ info.width, info.height, info.format, info.combinedUsage, &res);
+ ALOGV("%s: allocate a new graphic buffer %p with handle %p",
+ __FUNCTION__, buffer.graphicBuffer.get(), buffer.graphicBuffer->handle);
+ if (res != OK) {
+ ALOGE("%s: graphic buffer allocation failed: (error %d %s) ",
+ __FUNCTION__, res, strerror(-res));
+ return res;
+ }
+ }
+
+ // Increase the hand-out buffer count for tracking purpose.
+ BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
+ size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
+ bufferCount++;
+
+ *gb = buffer.graphicBuffer;
+ *fenceFd = buffer.fenceFd;
+ ALOGV("%s: get buffer (%p) with handle (%p).",
+ __FUNCTION__, buffer.graphicBuffer.get(), buffer.graphicBuffer->handle);
+
+ // Proactively free buffers for other streams if the current number of allocated buffers
+ // exceeds the water mark. This only for Gralloc V1, for V2, this logic can also be handled
+ // in returnBufferForStream() if we want to free buffer more quickly.
+ // TODO: probably should find out all the inactive stream IDs, and free the firstly found
+ // buffers for them.
+ StreamId firstOtherStreamId = CAMERA3_STREAM_ID_INVALID;
+ if (streamSet.streamInfoMap.size() > 1) {
+ for (size_t i = 0; i < streamSet.streamInfoMap.size(); i++) {
+ firstOtherStreamId = streamSet.streamInfoMap[i].streamId;
+ if (firstOtherStreamId != streamId) {
+ break;
+ }
+ }
+ if (firstOtherStreamId == CAMERA3_STREAM_ID_INVALID) {
+ return OK;
+ }
+
+ // This will drop the reference to one free buffer, which will effectively free one
+ // buffer (from the free buffer list) for the inactive streams.
+ size_t totalAllocatedBufferCount = streamSet.freeBuffers.size();
+ for (size_t i = 0; i < streamSet.handoutBufferCountMap.size(); i++) {
+ totalAllocatedBufferCount += streamSet.handoutBufferCountMap[i];
+ }
+ if (totalAllocatedBufferCount > streamSet.allocatedBufferWaterMark) {
+ getFirstBufferFromBufferListLocked(streamSet.freeBuffers, firstOtherStreamId);
+ }
+ }
+ } else {
+ // TODO: implement this.
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+status_t Camera3BufferManager::returnBufferForStream(int streamId,
+ int streamSetId, const sp<GraphicBuffer>& buffer, int fenceFd) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ ALOGV_IF(buffer != 0, "%s: return buffer (%p) with handle (%p) for stream %d and stream set %d",
+ __FUNCTION__, buffer.get(), buffer->handle, streamId, streamSetId);
+ if (mAllocator == NULL) {
+ ALOGE("%s: allocator is NULL, buffer manager is bad state.", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ if (!checkIfStreamRegisteredLocked(streamId, streamSetId)){
+ ALOGV("%s: returning buffer for an already unregistered stream (stream %d with set id %d),"
+ "buffer will be dropped right away!", __FUNCTION__, streamId, streamSetId);
+ return OK;
+ }
+
+ if (mGrallocVersion < HARDWARE_DEVICE_API_VERSION(1,0)) {
+ // Add to the freeBuffer list.
+ StreamSet& streamSet = mStreamSetMap.editValueFor(streamSetId);
+ if (buffer != 0) {
+ BufferEntry entry;
+ entry.add(streamId, GraphicBufferEntry(buffer, fenceFd));
+ status_t res = addBufferToBufferListLocked(streamSet.freeBuffers, entry);
+ if (res != OK) {
+ ALOGE("%s: add buffer to free buffer list failed", __FUNCTION__);
+ return res;
+ }
+ }
+
+ // Update the hand-out buffer count for this buffer.
+ BufferCountMap& handOutBufferCounts = streamSet.handoutBufferCountMap;
+ size_t& bufferCount = handOutBufferCounts.editValueFor(streamId);
+ bufferCount--;
+ } else {
+ // TODO: implement this.
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+void Camera3BufferManager::dump(int fd, const Vector<String16>& args) const {
+ Mutex::Autolock l(mLock);
+
+ (void) args;
+ String8 lines;
+ lines.appendFormat(" Total stream sets: %zu\n", mStreamSetMap.size());
+ for (size_t i = 0; i < mStreamSetMap.size(); i++) {
+ lines.appendFormat(" Stream set %d has below streams:\n", mStreamSetMap.keyAt(i));
+ for (size_t j = 0; j < mStreamSetMap[i].streamInfoMap.size(); j++) {
+ lines.appendFormat(" Stream %d\n", mStreamSetMap[i].streamInfoMap[j].streamId);
+ }
+ lines.appendFormat(" Stream set buffer count water mark: %zu\n",
+ mStreamSetMap[i].allocatedBufferWaterMark);
+ lines.appendFormat(" Handout buffer counts:\n");
+ for (size_t m = 0; m < mStreamSetMap[i].handoutBufferCountMap.size(); m++) {
+ int streamId = mStreamSetMap[i].handoutBufferCountMap.keyAt(m);
+ size_t bufferCount = mStreamSetMap[i].handoutBufferCountMap.valueAt(m);
+ lines.appendFormat(" stream id: %d, buffer count: %zu.\n",
+ streamId, bufferCount);
+ }
+
+ lines.appendFormat(" Free buffer count: %zu\n",
+ mStreamSetMap[i].freeBuffers.size());
+ for (auto& bufEntry : mStreamSetMap[i].freeBuffers) {
+ for (size_t m = 0; m < bufEntry.size(); m++) {
+ const sp<GraphicBuffer>& buffer = bufEntry.valueAt(m).graphicBuffer;
+ int streamId = bufEntry.keyAt(m);
+ lines.appendFormat(" stream id: %d, buffer: %p, handle: %p.\n",
+ streamId, buffer.get(), buffer->handle);
+ }
+ }
+ }
+ write(fd, lines.string(), lines.size());
+}
+
+bool Camera3BufferManager::checkIfStreamRegisteredLocked(int streamId, int streamSetId) const {
+ ssize_t setIdx = mStreamSetMap.indexOfKey(streamSetId);
+ if (setIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream set %d is not registered to stream set map yet!",
+ __FUNCTION__, streamSetId);
+ return false;
+ }
+
+ ssize_t streamIdx = mStreamSetMap.valueAt(setIdx).streamInfoMap.indexOfKey(streamId);
+ if (streamIdx == NAME_NOT_FOUND) {
+ ALOGV("%s: stream %d is not registered to stream info map yet!", __FUNCTION__, streamId);
+ return false;
+ }
+
+ size_t bufferWaterMark = mStreamSetMap[setIdx].allocatedBufferWaterMark;
+ if (bufferWaterMark == 0 || bufferWaterMark > kMaxBufferCount) {
+ ALOGW("%s: stream %d with stream set %d is not registered correctly to stream set map,"
+ " as the water mark (%zu) is wrong!",
+ __FUNCTION__, streamId, streamSetId, bufferWaterMark);
+ return false;
+ }
+
+ return true;
+}
+
+status_t Camera3BufferManager::addBufferToBufferListLocked(BufferList& bufList,
+ const BufferEntry& buffer) {
+ // TODO: need add some sanity check here.
+ bufList.push_back(buffer);
+
+ return OK;
+}
+
+status_t Camera3BufferManager::removeBuffersFromBufferListLocked(BufferList& bufferList,
+ int streamId) {
+ BufferList::iterator i = bufferList.begin();
+ while (i != bufferList.end()) {
+ ssize_t idx = i->indexOfKey(streamId);
+ if (idx != NAME_NOT_FOUND) {
+ i->removeItem(streamId);
+ if (i->isEmpty()) {
+ i = bufferList.erase(i);
+ }
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ ALOGW_IF(i == bufferList.end(), "%s: Unable to find buffers for stream %d",
+ __FUNCTION__, streamId);
+
+ return OK;
+}
+
+Camera3BufferManager::GraphicBufferEntry Camera3BufferManager::getFirstBufferFromBufferListLocked(
+ BufferList& buffers, int streamId) {
+ // Try to get the first buffer from the free buffer list if there is one.
+ GraphicBufferEntry entry;
+ BufferList::iterator i = buffers.begin();
+ while (i != buffers.end()) {
+ ssize_t idx = i->indexOfKey(streamId);
+ if (idx != NAME_NOT_FOUND) {
+ entry = GraphicBufferEntry(i->valueAt(idx));
+ i = buffers.erase(i);
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ ALOGV_IF(entry.graphicBuffer == 0, "%s: Unable to find free buffer for stream %d",
+ __FUNCTION__, streamId);
+ return entry;
+}
+
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3BufferManager.h b/services/camera/libcameraservice/device3/Camera3BufferManager.h
new file mode 100644
index 0000000..0b4f55c
--- /dev/null
+++ b/services/camera/libcameraservice/device3/Camera3BufferManager.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_BUFFER_MANAGER_H
+#define ANDROID_SERVERS_CAMERA3_BUFFER_MANAGER_H
+
+#include <list>
+#include <algorithm>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include "Camera3OutputStream.h"
+
+namespace android {
+
+namespace camera3 {
+
+struct StreamInfo;
+
+/**
+ * A class managing the graphic buffers that is used by camera output streams. It allocates and
+ * hands out Gralloc buffers to the clients (e.g., Camera3OutputStream) based on the requests.
+ * When clients request a buffer, buffer manager will pick a buffer if there are some already
+ * allocated buffer available, will allocate a buffer otherwise. When there are too many allocated
+ * buffer maintained by the buffer manager, it will dynamically deallocate some buffers that are
+ * solely owned by this buffer manager.
+ * In doing so, it reduces the memory footprint unless it is already minimal without impacting
+ * performance.
+ *
+ */
+class Camera3BufferManager: public virtual RefBase {
+public:
+ Camera3BufferManager(const sp<IGraphicBufferAlloc>& allocator = NULL);
+
+ virtual ~Camera3BufferManager();
+
+ /**
+ * This method registers an output stream to this buffer manager by using the provided stream
+ * information.
+ *
+ * The stream info includes the necessary information such as stream size, format, buffer count,
+ * usage flags, etc. for the buffer manager to allocate and hand out buffers for this stream.
+ *
+ * It's illegal to call this method if the stream is not CONFIGURED yet, as some critical
+ * stream properties (e.g., combined usage flags) are only available in this state. It is also
+ * illegal to call this method with an invalid stream set ID (CAMERA3_STREAM_SET_ID_INVALID),
+ * as the invalid stream set ID indicates that this stream doesn't intend to use buffer manager.
+ *
+ *
+ * Once a stream is successfully registered to this buffer manager, the buffer manager takes
+ * over the buffer allocation role and provides buffers to this stream via getBufferForStream().
+ * The returned buffer can be sent to the camera HAL for image output, and then queued to the
+ * ANativeWindow (Surface) for downstream consumer to acquire. Once the image buffer is released
+ * by the consumer end point, the BufferQueueProducer callback onBufferReleased will call
+ * returnBufferForStream() to return the free buffer to this buffer manager. If the stream
+ * uses buffer manager to manage the stream buffers, it should disable the BufferQueue
+ * allocation via IGraphicBufferProducer::allowAllocation(false).
+ *
+ * Registering an already registered stream has no effect.
+ *
+ * Return values:
+ *
+ * OK: Registration of the new stream was successful.
+ * BAD_VALUE: This stream is not at CONFIGURED state, or the stream ID or stream set
+ * ID are invalid, or attempting to register the same stream to multiple
+ * stream sets, or other stream properties are invalid.
+ * INVALID_OPERATION: This buffer manager doesn't support buffer sharing across this stream
+ * and other streams that were already registered with the same stream set
+ * ID.
+ */
+ status_t registerStream(const StreamInfo &streamInfo);
+
+ /**
+ * This method unregisters a stream from this buffer manager.
+ *
+ * After a stream is unregistered, further getBufferForStream() calls will fail for this stream.
+ * After all streams for a given stream set are unregistered, all the buffers solely owned (for
+ * this stream set) by this buffer manager will be freed; all buffers subsequently returned to
+ * this buffer manager for this stream set will be freed immediately.
+ *
+ * Return values:
+ *
+ * OK: Removal of the a stream from this buffer manager was successful.
+ * BAD_VALUE: stream ID or stream set ID are invalid, or stream ID and stream set ID
+ * combination doesn't match what was registered, or this stream wasn't registered
+ * to this buffer manager before.
+ */
+ status_t unregisterStream(int streamId, int streamSetId);
+
+ /**
+ * This method obtains a buffer for a stream from this buffer manager.
+ *
+ * This method returns the first free buffer from the free buffer list (associated with this
+ * stream set) if there is any. Otherwise, it will allocate a buffer for this stream, return
+ * it and increment its count of handed-out buffers. When the total number of allocated buffers
+ * is too high, it may deallocate the unused buffers to save memory footprint of this stream
+ * set.
+ *
+ * After this call, the client takes over the ownership of this buffer if it is not freed.
+ *
+ * Return values:
+ *
+ * OK: Getting buffer for this stream was successful.
+ * BAD_VALUE: stream ID or streamSetId are invalid, or stream ID and stream set ID
+ * combination doesn't match what was registered, or this stream wasn't registered
+ * to this buffer manager before.
+ * NO_MEMORY: Unable to allocate a buffer for this stream at this time.
+ */
+ status_t getBufferForStream(int streamId, int streamSetId, sp<GraphicBuffer>* gb, int* fenceFd);
+
+ /**
+ * This method returns a buffer for a stream to this buffer manager.
+ *
+ * When a buffer is returned, it is treated as a free buffer and may either be reused for future
+ * getBufferForStream() calls, or freed if there total number of outstanding allocated buffers
+ * is too large. The latter only applies to the case where the buffer are physically shared
+ * between streams in the same stream set. A physically shared buffer is the buffer that has one
+ * physical back store but multiple handles. Multiple stream can access the same physical memory
+ * with their own handles. Physically shared buffer can only be supported by Gralloc HAL V1.
+ * See hardware/libhardware/include/hardware/gralloc1.h for more details.
+ *
+ *
+ * This call takes the ownership of the returned buffer if it was allocated by this buffer
+ * manager; clients should not use this buffer after this call. Attempting to access this buffer
+ * after this call will have undefined behavior. Holding a reference to this buffer after this
+ * call may cause memory leakage. If a BufferQueue is used to track the buffers handed out by
+ * this buffer queue, it is recommended to call detachNextBuffer() from the buffer queue after
+ * BufferQueueProducer onBufferReleased callback is fired, and return it to this buffer manager.
+ *
+ * OK: Buffer return for this stream was successful.
+ * BAD_VALUE: stream ID or streamSetId are invalid, or stream ID and stream set ID combination
+ * doesn't match what was registered, or this stream wasn't registered to this
+ * buffer manager before.
+ */
+ status_t returnBufferForStream(int streamId, int streamSetId, const sp<GraphicBuffer>& buffer,
+ int fenceFd);
+
+ /**
+ * Dump the buffer manager statistics.
+ */
+ void dump(int fd, const Vector<String16> &args) const;
+
+private:
+ /**
+ * Lock to synchronize the access to the methods of this class.
+ */
+ mutable Mutex mLock;
+
+ static const size_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+ /**
+ * mAllocator is the connection to SurfaceFlinger that is used to allocate new GraphicBuffer
+ * objects.
+ */
+ sp<IGraphicBufferAlloc> mAllocator;
+
+ struct GraphicBufferEntry {
+ sp<GraphicBuffer> graphicBuffer;
+ int fenceFd;
+ GraphicBufferEntry(const sp<GraphicBuffer>& gb = 0, int fd = -1) :
+ graphicBuffer(gb),
+ fenceFd(fd) {}
+ };
+
+ /**
+ * A buffer entry (indexed by stream ID) represents a single physically allocated buffer. For
+ * Gralloc V0, since each physical buffer is associated with one stream, this is
+ * a single entry map. For Gralloc V1, one physical buffer can be shared between different
+ * streams in one stream set, so this entry may include multiple entries, where the different
+ * graphic buffers have the same common Gralloc backing store.
+ */
+ typedef int StreamId;
+ typedef KeyedVector<StreamId, GraphicBufferEntry> BufferEntry;
+
+ typedef std::list<BufferEntry> BufferList;
+
+ /**
+ * Stream info map (indexed by stream ID) tracks all the streams registered to a particular
+ * stream set.
+ */
+ typedef KeyedVector<StreamId, StreamInfo> InfoMap;
+
+ /**
+ * Stream set buffer count map (indexed by stream ID) tracks all buffer counts of the streams
+ * registered to a particular stream set.
+ */
+ typedef KeyedVector<StreamId, size_t> BufferCountMap;
+
+ /**
+ * StreamSet keeps track of the stream info, free buffer list and hand-out buffer counts for
+ * each stream set.
+ */
+ struct StreamSet {
+ /**
+ * Stream set buffer count water mark representing the max number of allocated buffers
+ * (hand-out buffers + free buffers) count for each stream set. For a given stream set, when
+ * getBufferForStream() is called on this buffer manager, if the total allocated buffer
+ * count exceeds this water mark, the buffer manager will attempt to reduce it as follows:
+ *
+ * In getBufferForStream(), find a buffer associated with other streams (inside the same
+ * stream set) on the free buffer list and free it. For Gralloc V1, can just free the top
+ * of the free buffer list if the physical buffer sharing in this stream is supported.
+ *
+ * For a particular stream set, a larger allocatedBufferWaterMark increases the memory
+ * footprint of the stream set, but reduces the chance that getBufferForStream() will have
+ * to allocate a new buffer. We assume that the streams in one stream set are not streaming
+ * simultaneously, the max allocated buffer count water mark for a stream set will the max
+ * of all streams' total buffer counts. This will avoid new buffer allocation in steady
+ * streaming state.
+ */
+ size_t allocatedBufferWaterMark;
+ /**
+ * The stream info for all streams in this set
+ */
+ InfoMap streamInfoMap;
+ /**
+ * The free buffer list for all the buffers belong to this set. The free buffers are
+ * returned by the returnBufferForStream() call, and available for reuse.
+ */
+ BufferList freeBuffers;
+ /**
+ * The count of the buffers that were handed out to the streams of this set.
+ */
+ BufferCountMap handoutBufferCountMap;
+ StreamSet() {
+ allocatedBufferWaterMark = 0;
+ }
+ };
+
+ /**
+ * Stream set map managed by this buffer manager.
+ */
+ typedef int StreamSetId;
+ KeyedVector<StreamSetId, StreamSet> mStreamSetMap;
+
+ // TODO: There is no easy way to query the Gralloc version in this code yet, we have different
+ // code paths for different Gralloc versions, hardcode something here for now.
+ const uint32_t mGrallocVersion = GRALLOC_DEVICE_API_VERSION_0_1;
+
+ /**
+ * Check if this stream was successfully registered already. This method needs to be called with
+ * mLock held.
+ */
+ bool checkIfStreamRegisteredLocked(int streamId, int streamSetId) const;
+
+ /**
+ * Add a buffer entry to the BufferList. This method needs to be called with mLock held.
+ */
+ status_t addBufferToBufferListLocked(BufferList &bufList, const BufferEntry &buffer);
+
+ /**
+ * Remove all buffers from the BufferList.
+ *
+ * Note that this doesn't mean that the buffers are freed after this call. A buffer is freed
+ * only if all other references to it are dropped.
+ *
+ * This method needs to be called with mLock held.
+ */
+ status_t removeBuffersFromBufferListLocked(BufferList &bufList, int streamId);
+
+ /**
+ * Get the first available buffer from the buffer list for this stream. The graphicBuffer inside
+ * this entry will be NULL if there is no any GraphicBufferEntry found. After this call, the
+ * GraphicBufferEntry will be removed from the BufferList if a GraphicBufferEntry is found.
+ *
+ * This method needs to be called with mLock held.
+ *
+ */
+ GraphicBufferEntry getFirstBufferFromBufferListLocked(BufferList& buffers, int streamId);
+};
+
+} // namespace camera3
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA3_BUFFER_MANAGER_H
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6c07aef..ea1e5f6 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -167,6 +167,9 @@
return res;
}
+ /** Create buffer manager */
+ mBufferManager = new Camera3BufferManager();
+
bool aeLockAvailable = false;
camera_metadata_ro_entry aeLockAvailableEntry;
res = find_camera_metadata_ro_entry(info.static_camera_characteristics,
@@ -293,6 +296,7 @@
mRequestThread.clear();
mStatusTracker.clear();
+ mBufferManager.clear();
hal3Device = mHal3Device;
}
@@ -502,6 +506,10 @@
mOutputStreams[i]->dump(fd,args);
}
+ lines = String8(" Camera3 Buffer Manager:\n");
+ write(fd, lines.string(), lines.size());
+ mBufferManager->dump(fd, args);
+
lines = String8(" In-flight requests:\n");
if (mInFlightMap.size() == 0) {
lines.append(" None\n");
@@ -926,7 +934,7 @@
status_t Camera3Device::createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
- camera3_stream_rotation_t rotation, int *id) {
+ camera3_stream_rotation_t rotation, int *id, int streamSetId) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -963,6 +971,11 @@
assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
+ // Overwrite stream set id to invalid for HAL3.1 or lower, as buffer manager does support
+ // such devices.
+ if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+ streamSetId = CAMERA3_STREAM_SET_ID_INVALID;
+ }
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
if (dataSpace != HAL_DATASPACE_DEPTH) {
@@ -979,7 +992,7 @@
}
}
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, blobBufferSize, format, dataSpace, rotation);
+ width, height, blobBufferSize, format, dataSpace, rotation, streamSetId);
} else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
if (rawOpaqueBufferSize <= 0) {
@@ -987,13 +1000,22 @@
return BAD_VALUE;
}
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, rawOpaqueBufferSize, format, dataSpace, rotation);
+ width, height, rawOpaqueBufferSize, format, dataSpace, rotation, streamSetId);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, format, dataSpace, rotation);
+ width, height, format, dataSpace, rotation, streamSetId);
}
newStream->setStatusTracker(mStatusTracker);
+ /**
+ * Camera3 Buffer manager is only supported by HAL3.2 onwards, as the older HALs requires
+ * buffers to be statically allocated for internal static buffer registration, while the
+ * buffers provided by buffer manager are really dynamically allocated.
+ */
+ if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
+ newStream->setBufferManager(mBufferManager);
+ }
+
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
SET_ERR_L("Can't add new stream to set: %s (%d)", strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7e20b0d..f70a153 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -29,6 +29,7 @@
#include "common/CameraDeviceBase.h"
#include "device3/StatusTracker.h"
+#include "device3/Camera3BufferManager.h"
/**
* Function pointer types with C calling convention to
@@ -97,7 +98,8 @@
// stream, reconfiguring device, and unpausing.
virtual status_t createStream(sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+ int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID);
virtual status_t createInputStream(
uint32_t width, uint32_t height, int format,
int *id);
@@ -721,6 +723,13 @@
sp<camera3::StatusTracker> mStatusTracker;
/**
+ * Graphic buffer manager for output streams. Each device has a buffer manager, which is used
+ * by the output streams to get and return buffers if these streams are registered to this
+ * buffer manager.
+ */
+ sp<camera3::Camera3BufferManager> mBufferManager;
+
+ /**
* Thread for preparing streams
*/
class PreparerThread : private Thread, public virtual RefBase {
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 1d9d04f..fe04eb1 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -38,7 +38,7 @@
status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *buffer) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
@@ -46,7 +46,7 @@
const camera3_stream_buffer &buffer,
nsecs_t timestamp) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
@@ -57,7 +57,7 @@
/*out*/
sp<Fence> *releaseFenceOut) {
ATRACE_CALL();
- ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
+ ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 7b298e6..4824974 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -31,9 +31,9 @@
Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3Stream(id, type,
- width, height, maxSize, format, dataSpace, rotation),
+ width, height, maxSize, format, dataSpace, rotation, setId),
mTotalBufferCount(0),
mHandoutTotalBufferCount(0),
mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index f5727e8..35dda39 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -34,7 +34,8 @@
protected:
Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
public:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 2504bfd..7dab2e3 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -169,7 +169,7 @@
if (producer == NULL) {
return BAD_VALUE;
} else if (mProducer == NULL) {
- ALOGE("%s: No input stream is configured");
+ ALOGE("%s: No input stream is configured", __FUNCTION__);
return INVALID_OPERATION;
}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index a5aa1fa..c721144 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -34,28 +34,34 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
- /*maxSize*/0, format, dataSpace, rotation),
+ /*maxSize*/0, format, dataSpace, rotation, setId),
mConsumer(consumer),
mTransform(0),
- mTraceFirstBuffer(true) {
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
if (mConsumer == NULL) {
ALOGE("%s: Consumer is NULL!", __FUNCTION__);
mState = STATE_ERROR;
}
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
}
Camera3OutputStream::Camera3OutputStream(int id,
sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
- format, dataSpace, rotation),
+ format, dataSpace, rotation, setId),
mConsumer(consumer),
mTransform(0),
- mTraceFirstBuffer(true) {
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
if (format != HAL_PIXEL_FORMAT_BLOB && format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__,
@@ -67,17 +73,28 @@
ALOGE("%s: Consumer is NULL!", __FUNCTION__);
mState = STATE_ERROR;
}
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
}
Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height,
int format,
android_dataspace dataSpace,
- camera3_stream_rotation_t rotation) :
+ camera3_stream_rotation_t rotation,
+ int setId) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
- format, dataSpace, rotation),
- mTransform(0) {
+ format, dataSpace, rotation, setId),
+ mTransform(0),
+ mTraceFirstBuffer(true),
+ mUseBufferManager(false) {
+
+ if (setId > CAMERA3_STREAM_SET_ID_INVALID) {
+ mBufferReleasedListener = new BufferReleasedListener(this);
+ }
// Subclasses expected to initialize mConsumer themselves
}
@@ -96,28 +113,46 @@
}
ANativeWindowBuffer* anb;
- int fenceFd;
+ int fenceFd = -1;
+ if (mUseBufferManager) {
+ sp<GraphicBuffer> gb;
+ res = mBufferManager->getBufferForStream(getId(), getStreamSetId(), &gb, &fenceFd);
+ 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;
+ }
+ // 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;
+ }
+ } else {
+ /**
+ * 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();
- /**
- * 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);
- return res;
+ 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);
+ return res;
+ }
}
/**
@@ -270,9 +305,9 @@
ALOG_ASSERT(mConsumer != 0, "mConsumer should never be NULL");
- // Configure consumer-side ANativeWindow interface
- res = native_window_api_connect(mConsumer.get(),
- NATIVE_WINDOW_API_CAMERA);
+ // Configure consumer-side ANativeWindow interface. The listener may be used
+ // to notify buffer manager (if it is used) of the returned buffers.
+ res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/mBufferReleasedListener);
if (res != OK) {
ALOGE("%s: Unable to connect to native window for stream %d",
__FUNCTION__, mId);
@@ -366,6 +401,30 @@
__FUNCTION__, mTransform, strerror(-res), res);
}
+ /**
+ * Camera3 Buffer manager is only supported by HAL3.2 onwards, as the older HALs requires
+ * buffers to be statically allocated for internal static buffer registration, while the
+ * buffers provided by buffer manager are really dynamically allocated. Camera3Device only
+ * sets the mBufferManager if device version is >= HAL3.2, which guarantees that the buffer
+ * manager setup is skipped in below code.
+ */
+ if (mBufferManager != 0 && mSetId > CAMERA3_STREAM_SET_ID_INVALID) {
+ StreamInfo streamInfo(
+ getId(), getStreamSetId(), getWidth(), getHeight(), getFormat(), getDataSpace(),
+ camera3_stream::usage, mTotalBufferCount, /*isConfigured*/true);
+ res = mBufferManager->registerStream(streamInfo);
+ if (res == OK) {
+ // Disable buffer allocation for this BufferQueue, buffer manager will take over
+ // the buffer allocation responsibility.
+ mConsumer->getIGraphicBufferProducer()->allowAllocation(false);
+ mUseBufferManager = true;
+ } else {
+ ALOGE("%s: Unable to register stream %d to camera3 buffer manager, "
+ "(error %d %s), fall back to BufferQueue for buffer management!",
+ __FUNCTION__, mId, res, strerror(-res));
+ }
+ }
+
return OK;
}
@@ -376,6 +435,8 @@
return res;
}
+ ALOGV("%s: disconnecting stream %d from native window", __FUNCTION__, getId());
+
res = native_window_api_disconnect(mConsumer.get(),
NATIVE_WINDOW_API_CAMERA);
@@ -396,6 +457,21 @@
return res;
}
+ // Since device is already idle, there is no getBuffer call to buffer manager, unregister the
+ // stream at this point should be safe.
+ if (mUseBufferManager) {
+ res = mBufferManager->unregisterStream(getId(), getStreamSetId());
+ if (res != OK) {
+ ALOGE("%s: Unable to unregister stream %d from buffer manager "
+ "(error %d %s)", __FUNCTION__, mId, res, strerror(-res));
+ mState = STATE_ERROR;
+ return res;
+ }
+ // Note that, to make prepare/teardown case work, we must not mBufferManager.clear(), as
+ // the stream is still in usable state after this call.
+ mUseBufferManager = false;
+ }
+
mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
: STATE_CONSTRUCTED;
return OK;
@@ -437,6 +513,59 @@
return (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) != 0;
}
+status_t Camera3OutputStream::setBufferManager(sp<Camera3BufferManager> bufferManager) {
+ Mutex::Autolock l(mLock);
+ if (mState != STATE_CONSTRUCTED) {
+ ALOGE("%s: this method can only be called when stream in in CONSTRUCTED state.",
+ __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ mBufferManager = bufferManager;
+
+ return OK;
+}
+
+void Camera3OutputStream::BufferReleasedListener::onBufferReleased() {
+ sp<Camera3OutputStream> stream = mParent.promote();
+ if (stream == nullptr) {
+ ALOGV("%s: Parent camera3 output stream was destroyed", __FUNCTION__);
+ return;
+ }
+
+ Mutex::Autolock l(stream->mLock);
+ if (!(stream->mUseBufferManager)) {
+ return;
+ }
+
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ int fenceFd = -1;
+ status_t res = stream->mConsumer->detachNextBuffer(&buffer, &fence);
+ if (res == NO_MEMORY) {
+ // This may rarely happen, which indicates that the released buffer was freed by other
+ // call (e.g., attachBuffer, dequeueBuffer etc.) before reaching here. We should notify the
+ // buffer manager that this buffer has been freed. It's not fatal, but should be avoided,
+ // therefore log a warning.
+ buffer = 0;
+ ALOGW("%s: the released buffer has already been freed by the buffer queue!", __FUNCTION__);
+ } else if (res != OK) {
+ // Other errors are fatal.
+ ALOGE("%s: detach next buffer failed: %s (%d).", __FUNCTION__, strerror(-res), res);
+ stream->mState = STATE_ERROR;
+ return;
+ }
+
+ if (fence!= 0 && fence->isValid()) {
+ fenceFd = fence->dup();
+ }
+ res = stream->mBufferManager->returnBufferForStream(stream->getId(), stream->getStreamSetId(),
+ buffer, fenceFd);
+ if (res != OK) {
+ ALOGE("%s: return buffer to buffer manager failed: %s (%d).", __FUNCTION__,
+ strerror(-res), res);
+ stream->mState = STATE_ERROR;
+ }
+}
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 3c083ec..e222e2c 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -18,16 +18,54 @@
#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H
#include <utils/RefBase.h>
+#include <gui/IProducerListener.h>
#include <gui/Surface.h>
#include "Camera3Stream.h"
#include "Camera3IOStreamBase.h"
#include "Camera3OutputStreamInterface.h"
+#include "Camera3BufferManager.h"
namespace android {
namespace camera3 {
+class Camera3BufferManager;
+
+/**
+ * Stream info structure that holds the necessary stream info for buffer manager to use for
+ * buffer allocation and management.
+ */
+struct StreamInfo {
+ int streamId;
+ int streamSetId;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ android_dataspace dataSpace;
+ uint32_t combinedUsage;
+ size_t totalBufferCount;
+ bool isConfigured;
+ StreamInfo(int id = CAMERA3_STREAM_ID_INVALID,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID,
+ uint32_t w = 0,
+ uint32_t h = 0,
+ uint32_t fmt = 0,
+ android_dataspace ds = HAL_DATASPACE_UNKNOWN,
+ uint32_t usage = 0,
+ size_t bufferCount = 0,
+ bool configured = false) :
+ streamId(id),
+ streamSetId(setId),
+ width(w),
+ height(h),
+ format(fmt),
+ dataSpace(ds),
+ combinedUsage(usage),
+ totalBufferCount(bufferCount),
+ isConfigured(configured){}
+};
+
/**
* A class for managing a single stream of output data from the camera device.
*/
@@ -37,18 +75,24 @@
public:
/**
* Set up a stream for formats that have 2 dimensions, such as RAW and YUV.
+ * A valid stream set id needs to be set to support buffer sharing between multiple
+ * streams.
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
/**
* Set up a stream for formats that have a variable buffer size for the same
* dimensions, such as compressed JPEG.
+ * A valid stream set id needs to be set to support buffer sharing between multiple
+ * streams.
*/
Camera3OutputStream(int id, sp<Surface> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
virtual ~Camera3OutputStream();
@@ -69,10 +113,32 @@
*/
bool isVideoStream() const;
+ class BufferReleasedListener : public BnProducerListener {
+ public:
+ BufferReleasedListener(wp<Camera3OutputStream> parent) : mParent(parent) {}
+
+ /**
+ * Implementation of IProducerListener, used to notify this stream that the consumer
+ * has returned a buffer and it is ready to return to Camera3BufferManager for reuse.
+ */
+ virtual void onBufferReleased();
+
+ private:
+ wp<Camera3OutputStream> mParent;
+ };
+
+ /**
+ * Set the graphic buffer manager to get/return the stream buffers.
+ *
+ * It is only legal to call this method when stream is in STATE_CONSTRUCTED state.
+ */
+ status_t setBufferManager(sp<Camera3BufferManager> bufferManager);
+
protected:
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId = CAMERA3_STREAM_SET_ID_INVALID);
/**
* Note that we release the lock briefly in this function
@@ -98,6 +164,22 @@
String8 mConsumerName;
/**
+ * GraphicBuffer manager this stream is registered to. Used to replace the buffer
+ * allocation/deallocation role of BufferQueue.
+ */
+ sp<Camera3BufferManager> mBufferManager;
+
+ /**
+ * Buffer released listener, used to notify the buffer manager that a buffer is released
+ * from consumer side.
+ */
+ sp<BufferReleasedListener> mBufferReleasedListener;
+
+ /**
+ * Flag indicating if the buffer manager is used to allocate the stream buffers
+ */
+ bool mUseBufferManager;
+ /**
* Internal Camera3Stream interface
*/
virtual status_t getBufferLocked(camera3_stream_buffer *buffer);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 141f6c3..ed3ab97 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -47,13 +47,19 @@
Camera3Stream::Camera3Stream(int id,
camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
camera3_stream(),
mId(id),
+ mSetId(setId),
mName(String8::format("Camera3Stream[%d]", id)),
mMaxSize(maxSize),
mState(STATE_CONSTRUCTED),
mStatusId(StatusTracker::NO_STATUS_ID),
+ oldUsage(0),
+ oldMaxBuffers(0),
+ mStreamUnpreparable(false),
+ mPrepared(false),
+ mPreparedBufferIdx(0),
mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX) {
camera3_stream::stream_type = type;
@@ -77,6 +83,10 @@
return mId;
}
+int Camera3Stream::getStreamSetId() const {
+ return mSetId;
+}
+
uint32_t Camera3Stream::getWidth() const {
return camera3_stream::width;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 753280b..fe51ab5 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -130,6 +130,11 @@
int getId() const;
/**
+ * Get the output stream set id.
+ */
+ int getStreamSetId() const;
+
+ /**
* Get the stream's dimensions and format
*/
uint32_t getWidth() const;
@@ -350,6 +355,21 @@
protected:
const int mId;
+ /**
+ * Stream set id, used to indicate which group of this stream belongs to for buffer sharing
+ * across multiple streams.
+ *
+ * The default value is set to CAMERA3_STREAM_SET_ID_INVALID, which indicates that this stream
+ * doesn't intend to share buffers with any other streams, and this stream will fall back to
+ * the existing BufferQueue mechanism to manage the buffer allocations and buffer circulation.
+ * When a valid stream set id is set, this stream intends to use the Camera3BufferManager to
+ * manage the buffer allocations; the BufferQueue will only handle the buffer transaction
+ * between the producer and consumer. For this case, upon successfully registration, the streams
+ * with the same stream set id will potentially share the buffers allocated by
+ * Camera3BufferManager.
+ */
+ const int mSetId;
+
const String8 mName;
// Zero for formats with fixed buffer size for given dimensions.
const size_t mMaxSize;
@@ -367,7 +387,8 @@
Camera3Stream(int id, camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace, camera3_stream_rotation_t rotation);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+ int setId);
/**
* Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 54009ae..3f7e7a7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -26,6 +26,20 @@
namespace camera3 {
+enum {
+ /**
+ * This stream set ID indicates that the set ID is invalid, and this stream doesn't intend to
+ * share buffers with any other stream. It is illegal to register this kind of stream to
+ * Camera3BufferManager.
+ */
+ CAMERA3_STREAM_SET_ID_INVALID = -1,
+
+ /**
+ * Invalid output stream ID.
+ */
+ CAMERA3_STREAM_ID_INVALID = -1,
+};
+
class StatusTracker;
/**
@@ -45,6 +59,11 @@
virtual int getId() const = 0;
/**
+ * Get the output stream set id.
+ */
+ virtual int getStreamSetId() const = 0;
+
+ /**
* Get the stream's dimensions and format
*/
virtual uint32_t getWidth() const = 0;
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index 243ea31..28dc5d5 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -189,4 +189,4 @@
} // namespace android
-#endif // ANDROID_GUI_CPUCONSUMER_H
+#endif // ANDROID_GUI_RINGBUFFERCONSUMER_H