Merge "heic content_encoding field is optional" into pi-dev
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index d13f0dd..c082fc1 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -982,26 +982,24 @@
bool InfeBox::parseNullTerminatedString(
off64_t *offset, size_t *size, String8 *out) {
- char tmp[256];
- size_t len = 0;
+ char tmp;
+ Vector<char> buf;
+ buf.setCapacity(256);
off64_t newOffset = *offset;
off64_t stopOffset = *offset + *size;
while (newOffset < stopOffset) {
- if (!source()->readAt(newOffset++, &tmp[len], 1)) {
+ if (!source()->readAt(newOffset++, &tmp, 1)) {
return false;
}
- if (tmp[len] == 0) {
- out->append(tmp, len);
+ buf.push_back(tmp);
+ if (tmp == 0) {
+ out->setTo(buf.array());
*offset = newOffset;
*size = stopOffset - newOffset;
return true;
}
- if (++len >= sizeof(tmp)) {
- out->append(tmp, len);
- len = 0;
- }
}
return false;
}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 2dfbdca..63130c4 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -140,6 +140,11 @@
// have been caught above.
CHECK(offset >= mCachedOffset);
+ off64_t resultOffset;
+ if (__builtin_add_overflow(offset, size, &resultOffset)) {
+ return ERROR_IO;
+ }
+
if (size == 0) {
return 0;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 9ab9aae..30c0b1c 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -89,6 +89,13 @@
static const char *kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
static const char *kRecorderWidth = "android.media.mediarecorder.width";
+// new fields, not yet frozen in the public Java API definitions
+static const char *kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
+static const char *kRecorderVideoMime = "android.media.mediarecorder.video.mime";
+static const char *kRecorderDurationMs = "android.media.mediarecorder.durationMs";
+static const char *kRecorderPaused = "android.media.mediarecorder.pausedMs";
+static const char *kRecorderNumPauses = "android.media.mediarecorder.NPauses";
+
// To collect the encoder usage for the battery app
static void addBatteryData(uint32_t params) {
@@ -126,21 +133,18 @@
}
// log the current record, provided it has some information worth recording
- if (mAnalyticsDirty && mAnalyticsItem != NULL) {
- updateMetrics();
- if (mAnalyticsItem->count() > 0) {
- mAnalyticsItem->selfrecord();
- }
- delete mAnalyticsItem;
- mAnalyticsItem = NULL;
- }
+ // NB: this also reclaims & clears mAnalyticsItem.
+ flushAndResetMetrics(false);
}
void StagefrightRecorder::updateMetrics() {
ALOGV("updateMetrics");
- // we'll populate the values from the raw fields.
- // (NOT going to populate as we go through the various set* ops)
+ // we run as part of the media player service; what we really want to
+ // know is the app which requested the recording.
+ mAnalyticsItem->setUid(mClientUid);
+
+ // populate the values from the raw fields.
// TBD mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
// TBD mAudioEncoder = AUDIO_ENCODER_AMR_NB;
@@ -168,7 +172,6 @@
// TBD mTrackEveryTimeDurationUs = 0;
mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
- // TBD mCaptureFps = -1.0;
// TBD mCameraSourceTimeLapse = NULL;
// TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
// TBD mEncoderProfiles = MediaProfiles::getInstance();
@@ -177,14 +180,17 @@
// PII mLongitudex10000 = -3600000;
// TBD mTotalBitRate = 0;
- // TBD: some duration information (capture, paused)
- //
-
+ // duration information (recorded, paused, # of pauses)
+ mAnalyticsItem->setInt64(kRecorderDurationMs, (mDurationRecordedUs+500)/1000 );
+ if (mNPauses != 0) {
+ mAnalyticsItem->setInt64(kRecorderPaused, (mDurationPausedUs+500)/1000 );
+ mAnalyticsItem->setInt32(kRecorderNumPauses, mNPauses);
+ }
}
-void StagefrightRecorder::resetMetrics() {
- ALOGV("resetMetrics");
- // flush anything we have, restart the record
+void StagefrightRecorder::flushAndResetMetrics(bool reinitialize) {
+ ALOGV("flushAndResetMetrics");
+ // flush anything we have, maybe setup a new record
if (mAnalyticsDirty && mAnalyticsItem != NULL) {
updateMetrics();
if (mAnalyticsItem->count() > 0) {
@@ -193,8 +199,10 @@
delete mAnalyticsItem;
mAnalyticsItem = NULL;
}
- mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
mAnalyticsDirty = false;
+ if (reinitialize) {
+ mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
+ }
}
status_t StagefrightRecorder::init() {
@@ -1030,6 +1038,8 @@
mAnalyticsDirty = true;
mStarted = true;
+ mStartedRecordingUs = systemTime() / 1000;
+
uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted;
if (mAudioSource != AUDIO_SOURCE_CNT) {
params |= IMediaPlayerService::kBatteryDataTrackAudio;
@@ -1109,6 +1119,14 @@
return NULL;
}
+ // log audio mime type for media metrics
+ if (mAnalyticsItem != NULL) {
+ AString audiomime;
+ if (format->findString("mime", &audiomime)) {
+ mAnalyticsItem->setCString(kRecorderAudioMime, audiomime.c_str());
+ }
+ }
+
int32_t maxInputSize;
CHECK(audioSource->getFormat()->findInt32(
kKeyMaxInputSize, &maxInputSize));
@@ -1655,6 +1673,14 @@
break;
}
+ // log video mime type for media metrics
+ if (mAnalyticsItem != NULL) {
+ AString videomime;
+ if (format->findString("mime", &videomime)) {
+ mAnalyticsItem->setCString(kRecorderVideoMime, videomime.c_str());
+ }
+ }
+
if (cameraSource != NULL) {
sp<MetaData> meta = cameraSource->getFormat();
@@ -1917,6 +1943,13 @@
sp<MetaData> meta = new MetaData;
meta->setInt64(kKeyTime, mPauseStartTimeUs);
+ if (mStartedRecordingUs != 0) {
+ // should always be true
+ int64_t recordingUs = mPauseStartTimeUs - mStartedRecordingUs;
+ mDurationRecordedUs += recordingUs;
+ mStartedRecordingUs = 0;
+ }
+
if (mAudioEncoderSource != NULL) {
mAudioEncoderSource->pause();
}
@@ -1975,6 +2008,16 @@
source->setInputBufferTimeOffset((int64_t)timeOffset);
source->start(meta.get());
}
+
+
+ // sum info on pause duration
+ // (ignore the 30msec of overlap adjustment factored into mTotalPausedDurationUs)
+ int64_t pausedUs = resumeStartTimeUs - mPauseStartTimeUs;
+ mDurationPausedUs += pausedUs;
+ mNPauses++;
+ // and a timestamp marking that we're back to recording....
+ mStartedRecordingUs = resumeStartTimeUs;
+
mPauseStartTimeUs = 0;
return OK;
@@ -2003,10 +2046,28 @@
mWriter.clear();
}
- resetMetrics();
+ // account for the last 'segment' -- whether paused or recording
+ if (mPauseStartTimeUs != 0) {
+ // we were paused
+ int64_t additive = stopTimeUs - mPauseStartTimeUs;
+ mDurationPausedUs += additive;
+ mNPauses++;
+ } else if (mStartedRecordingUs != 0) {
+ // we were recording
+ int64_t additive = stopTimeUs - mStartedRecordingUs;
+ mDurationRecordedUs += additive;
+ } else {
+ ALOGW("stop while neither recording nor paused");
+ }
+ flushAndResetMetrics(true);
+
+ mDurationRecordedUs = 0;
+ mDurationPausedUs = 0;
+ mNPauses = 0;
mTotalPausedDurationUs = 0;
mPauseStartTimeUs = 0;
+ mStartedRecordingUs = 0;
mGraphicBufferProducer.clear();
mPersistentSurface.clear();
@@ -2085,6 +2146,12 @@
mLongitudex10000 = -3600000;
mTotalBitRate = 0;
+ // tracking how long we recorded.
+ mDurationRecordedUs = 0;
+ mStartedRecordingUs = 0;
+ mDurationPausedUs = 0;
+ mNPauses = 0;
+
mOutputFd = -1;
return OK;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 18c9256..faa2e59 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -95,7 +95,7 @@
MediaAnalyticsItem *mAnalyticsItem;
bool mAnalyticsDirty;
- void resetMetrics();
+ void flushAndResetMetrics(bool reinitialize);
void updateMetrics();
audio_source_t mAudioSource;
@@ -127,6 +127,11 @@
int32_t mStartTimeOffsetMs;
int32_t mTotalBitRate;
+ int64_t mDurationRecordedUs;
+ int64_t mStartedRecordingUs;
+ int64_t mDurationPausedUs;
+ int32_t mNPauses;
+
bool mCaptureFpsEnable;
double mCaptureFps;
int64_t mTimeBetweenCaptureUs;
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index d2eee33..7b3fa02 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -70,6 +70,10 @@
if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
return false;
}
+ if (!buffer) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ }
if (buffer->data().type() != C2BufferData::LINEAR) {
return false;
}
@@ -89,7 +93,7 @@
bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
// We assume that all canCopyLinear() checks passed.
- if (buffer->data().linearBlocks().size() == 0u) {
+ if (!buffer || buffer->data().linearBlocks().size() == 0u) {
setRange(0, 0);
return true;
}
@@ -207,4 +211,445 @@
return std::move(mBufferRef);
}
+// GraphicView2MediaImageConverter
+
+namespace {
+
+class GraphicView2MediaImageConverter {
+public:
+ explicit GraphicView2MediaImageConverter(const C2GraphicView &view)
+ : mInitCheck(NO_INIT),
+ mView(view),
+ mWidth(view.width()),
+ mHeight(view.height()),
+ mAllocatedDepth(0),
+ mBackBufferSize(0),
+ mMediaImage(new ABuffer(sizeof(MediaImage2))) {
+ if (view.error() != C2_OK) {
+ ALOGD("Converter: view.error() = %d", view.error());
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base();
+ const C2PlanarLayout &layout = view.layout();
+ if (layout.numPlanes == 0) {
+ ALOGD("Converter: 0 planes");
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ mAllocatedDepth = layout.planes[0].allocatedDepth;
+ uint32_t bitDepth = layout.planes[0].bitDepth;
+
+ switch (layout.type) {
+ case C2PlanarLayout::TYPE_YUV:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break;
+ case C2PlanarLayout::TYPE_YUVA:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break;
+ case C2PlanarLayout::TYPE_RGB:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break;
+ case C2PlanarLayout::TYPE_RGBA:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break;
+ default:
+ mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break;
+ }
+ mediaImage->mNumPlanes = layout.numPlanes;
+ mediaImage->mWidth = mWidth;
+ mediaImage->mHeight = mHeight;
+ mediaImage->mBitDepth = bitDepth;
+ mediaImage->mBitDepthAllocated = mAllocatedDepth;
+
+ uint32_t bufferSize = 0;
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ if (plane.rightShift != 0) {
+ ALOGV("rightShift value of %u unsupported", plane.rightShift);
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ if (plane.endianness != C2PlaneInfo::NATIVE) {
+ ALOGV("endianness value of %u unsupported", plane.endianness);
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) {
+ ALOGV("different allocatedDepth/bitDepth per plane unsupported");
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+ bufferSize += mWidth * mHeight
+ / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8);
+ }
+
+ mBackBufferSize = bufferSize;
+ mInitCheck = OK;
+ }
+
+ status_t initCheck() const { return mInitCheck; }
+
+ uint32_t backBufferSize() const { return mBackBufferSize; }
+
+ /**
+ * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content
+ * is not copied over in this function --- the caller should use
+ * CopyGraphicView2MediaImage() function to do that explicitly.
+ *
+ * \param view[in] source C2GraphicView object.
+ * \param alloc[in] allocator function for ABuffer.
+ * \param mediaImage[out] destination MediaImage2 object.
+ * \param buffer[out] new buffer object.
+ * \param wrapped[out] whether we wrapped around existing map or
+ * allocated a new buffer
+ *
+ * \return true if conversion succeeds,
+ * false otherwise; all output params should be ignored.
+ */
+ sp<ABuffer> wrap() {
+ MediaImage2 *mediaImage = getMediaImage();
+ const C2PlanarLayout &layout = mView.layout();
+ if (layout.numPlanes == 1) {
+ const C2PlaneInfo &plane = layout.planes[0];
+ ssize_t offset = plane.minOffset(mWidth, mHeight);
+ mediaImage->mPlane[0].mOffset = -offset;
+ mediaImage->mPlane[0].mColInc = plane.colInc;
+ mediaImage->mPlane[0].mRowInc = plane.rowInc;
+ mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling;
+ return new ABuffer(
+ const_cast<uint8_t *>(mView.data()[0] + offset),
+ plane.maxOffset(mWidth, mHeight) - offset + 1);
+ }
+ const uint8_t *minPtr = mView.data()[0];
+ const uint8_t *maxPtr = mView.data()[0];
+ int32_t planeSize = 0;
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ ssize_t minOffset = plane.minOffset(mWidth, mHeight);
+ ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
+ if (minPtr > mView.data()[i] + minOffset) {
+ minPtr = mView.data()[i] + minOffset;
+ }
+ if (maxPtr < mView.data()[i] + maxOffset) {
+ maxPtr = mView.data()[i] + maxOffset;
+ }
+ planeSize += std::abs(plane.rowInc) * mHeight
+ / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8);
+ }
+
+ if ((maxPtr - minPtr + 1) <= planeSize) {
+ // FIXME: this is risky as reading/writing data out of bound results in
+ // an undefined behavior.
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr;
+ mediaImage->mPlane[i].mColInc = plane.colInc;
+ mediaImage->mPlane[i].mRowInc = plane.rowInc;
+ mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+ }
+ return new ABuffer(const_cast<uint8_t *>(minPtr), maxPtr - minPtr + 1);
+ }
+
+ return nullptr;
+ }
+
+ bool setBackBuffer(const sp<ABuffer> &backBuffer) {
+ if (backBuffer->capacity() < mBackBufferSize) {
+ return false;
+ }
+ backBuffer->setRange(0, mBackBufferSize);
+
+ const C2PlanarLayout &layout = mView.layout();
+ MediaImage2 *mediaImage = getMediaImage();
+ uint32_t offset = 0;
+ // TODO: keep interleaved planes together
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ mediaImage->mPlane[i].mOffset = offset;
+ mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8;
+ mediaImage->mPlane[i].mRowInc =
+ mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling;
+ mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+ mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+ offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling;
+ }
+ mBackBuffer = backBuffer;
+ return true;
+ }
+
+ /**
+ * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is
+ * an output from GraphicView2MediaImage(), so it mostly skips sanity check.
+ *
+ * \param view[in] source C2GraphicView object.
+ * \param mediaImage[in] destination MediaImage2 object.
+ * \param buffer[out] new buffer object.
+ */
+ void copy() {
+ // TODO: more efficient copying --- e.g. one row at a time, copying
+ // interleaved planes together, etc.
+ const C2PlanarLayout &layout = mView.layout();
+ MediaImage2 *mediaImage = getMediaImage();
+ uint8_t *dst = mBackBuffer->base();
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ const uint8_t *src = mView.data()[i];
+ int32_t planeW = mWidth / plane.colSampling;
+ int32_t planeH = mHeight / plane.rowSampling;
+ for (int32_t row = 0; row < planeH; ++row) {
+ for(int32_t col = 0; col < planeW; ++col) {
+ memcpy(dst, src, mAllocatedDepth / 8);
+ dst += mediaImage->mPlane[i].mColInc;
+ src += plane.colInc;
+ }
+ dst -= mediaImage->mPlane[i].mColInc * planeW;
+ dst += mediaImage->mPlane[i].mRowInc;
+ src -= plane.colInc * planeW;
+ src += plane.rowInc;
+ }
+ }
+ }
+
+ const sp<ABuffer> &imageData() const { return mMediaImage; }
+
+private:
+ status_t mInitCheck;
+
+ const C2GraphicView mView;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mAllocatedDepth;
+ uint32_t mBackBufferSize;
+ sp<ABuffer> mMediaImage;
+ std::function<sp<ABuffer>(size_t)> mAlloc;
+
+ sp<ABuffer> mBackBuffer;
+
+ MediaImage2 *getMediaImage() {
+ return (MediaImage2 *)mMediaImage->base();
+ }
+};
+
+} // namespace
+
+// GraphicBlockBuffer
+
+// static
+sp<GraphicBlockBuffer> GraphicBlockBuffer::Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ C2GraphicView view(block->map().get());
+ if (view.error() != C2_OK) {
+ ALOGD("C2GraphicBlock::map failed: %d", view.error());
+ return nullptr;
+ }
+ GraphicView2MediaImageConverter converter(view);
+ if (converter.initCheck() != OK) {
+ ALOGD("Converter init failed: %d", converter.initCheck());
+ return nullptr;
+ }
+ bool wrapped = true;
+ sp<ABuffer> buffer = converter.wrap();
+ if (buffer == nullptr) {
+ buffer = alloc(converter.backBufferSize());
+ if (!converter.setBackBuffer(buffer)) {
+ ALOGD("Converter failed to set back buffer");
+ return nullptr;
+ }
+ wrapped = false;
+ }
+ return new GraphicBlockBuffer(
+ format,
+ buffer,
+ std::move(view),
+ block,
+ converter.imageData(),
+ wrapped);
+}
+
+GraphicBlockBuffer::GraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &buffer,
+ C2GraphicView &&view,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ const sp<ABuffer> &imageData,
+ bool wrapped)
+ : Codec2Buffer(format, buffer),
+ mView(view),
+ mBlock(block),
+ mImageData(imageData),
+ mWrapped(wrapped) {
+ meta()->setBuffer("image-data", imageData);
+}
+
+std::shared_ptr<C2Buffer> GraphicBlockBuffer::asC2Buffer() {
+ uint32_t width = mView.width();
+ uint32_t height = mView.height();
+ if (!mWrapped) {
+ MediaImage2 *mediaImage = imageData();
+ const C2PlanarLayout &layout = mView.layout();
+ for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ int32_t planeW = width / plane.colSampling;
+ int32_t planeH = height / plane.rowSampling;
+ const uint8_t *src = base() + mediaImage->mPlane[i].mOffset;
+ uint8_t *dst = mView.data()[i];
+ for (int32_t row = 0; row < planeH; ++row) {
+ for (int32_t col = 0; col < planeW; ++col) {
+ memcpy(dst, src, mediaImage->mBitDepthAllocated / 8);
+ src += mediaImage->mPlane[i].mColInc;
+ dst += plane.colInc;
+ }
+ src -= mediaImage->mPlane[i].mColInc * planeW;
+ dst -= plane.colInc * planeW;
+ src += mediaImage->mPlane[i].mRowInc;
+ dst += plane.rowInc;
+ }
+ }
+ }
+ return C2Buffer::CreateGraphicBuffer(
+ mBlock->share(C2Rect(width, height), C2Fence()));
+}
+
+// ConstGraphicBlockBuffer
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ if (!buffer
+ || buffer->data().type() != C2BufferData::GRAPHIC
+ || buffer->data().graphicBlocks().size() != 1u) {
+ ALOGD("C2Buffer precond fail");
+ return nullptr;
+ }
+ std::unique_ptr<const C2GraphicView> view(std::make_unique<const C2GraphicView>(
+ buffer->data().graphicBlocks()[0].map().get()));
+ std::unique_ptr<const C2GraphicView> holder;
+
+ GraphicView2MediaImageConverter converter(*view);
+ if (converter.initCheck() != OK) {
+ ALOGD("Converter init failed: %d", converter.initCheck());
+ return nullptr;
+ }
+ bool wrapped = true;
+ sp<ABuffer> aBuffer = converter.wrap();
+ if (aBuffer == nullptr) {
+ aBuffer = alloc(converter.backBufferSize());
+ if (!converter.setBackBuffer(aBuffer)) {
+ ALOGD("Converter failed to set back buffer");
+ return nullptr;
+ }
+ wrapped = false;
+ converter.copy();
+ // We don't need the view.
+ holder = std::move(view);
+ }
+ return new ConstGraphicBlockBuffer(
+ format,
+ aBuffer,
+ std::move(view),
+ buffer,
+ converter.imageData(),
+ wrapped);
+}
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::AllocateEmpty(
+ const sp<AMessage> &format,
+ std::function<sp<ABuffer>(size_t)> alloc) {
+ int32_t width, height;
+ if (!format->findInt32("width", &width)
+ || !format->findInt32("height", &height)) {
+ ALOGD("format had no width / height");
+ return nullptr;
+ }
+ sp<ABuffer> aBuffer(alloc(width * height * 4));
+ return new ConstGraphicBlockBuffer(
+ format,
+ aBuffer,
+ nullptr,
+ nullptr,
+ nullptr,
+ false);
+}
+
+ConstGraphicBlockBuffer::ConstGraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &aBuffer,
+ std::unique_ptr<const C2GraphicView> &&view,
+ const std::shared_ptr<C2Buffer> &buffer,
+ const sp<ABuffer> &imageData,
+ bool wrapped)
+ : Codec2Buffer(format, aBuffer),
+ mView(std::move(view)),
+ mBufferRef(buffer),
+ mWrapped(wrapped) {
+ if (imageData != nullptr) {
+ meta()->setBuffer("image-data", imageData);
+ }
+}
+
+std::shared_ptr<C2Buffer> ConstGraphicBlockBuffer::asC2Buffer() {
+ mView.reset();
+ return std::move(mBufferRef);
+}
+
+bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ if (mWrapped || mBufferRef) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s",
+ mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist");
+ return false;
+ }
+ if (!buffer) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ }
+ if (buffer->data().type() != C2BufferData::GRAPHIC) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied");
+ return false;
+ }
+ if (buffer->data().graphicBlocks().size() == 0) {
+ return true;
+ } else if (buffer->data().graphicBlocks().size() != 1u) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks");
+ return false;
+ }
+ GraphicView2MediaImageConverter converter(
+ buffer->data().graphicBlocks()[0].map().get());
+ if (converter.initCheck() != OK) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck());
+ return false;
+ }
+ if (converter.backBufferSize() > capacity()) {
+ ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu",
+ converter.backBufferSize(), capacity());
+ return false;
+ }
+ return true;
+}
+
+bool ConstGraphicBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ if (!buffer || buffer->data().graphicBlocks().size() == 0) {
+ setRange(0, 0);
+ return true;
+ }
+ GraphicView2MediaImageConverter converter(
+ buffer->data().graphicBlocks()[0].map().get());
+ if (converter.initCheck() != OK) {
+ ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck());
+ return false;
+ }
+ sp<ABuffer> aBuffer = new ABuffer(base(), capacity());
+ if (!converter.setBackBuffer(aBuffer)) {
+ ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed");
+ return false;
+ }
+ converter.copy();
+ meta()->setBuffer("image-data", converter.imageData());
+ mBufferRef = buffer;
+ return true;
+}
+
} // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index f62ee41..0bdd808 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -238,14 +238,25 @@
return mChannel;
}
+status_t CCodec::tryAndReportOnError(std::function<status_t()> job) {
+ status_t err = job();
+ if (err != C2_OK) {
+ mCallback->onError(err, ACTION_CODE_FATAL);
+ }
+ return err;
+}
+
void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
- {
+ auto setAllocating = [this] {
Mutexed<State>::Locked state(mState);
if (state->get() != RELEASED) {
- mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
- return;
+ return INVALID_OPERATION;
}
state->set(ALLOCATING);
+ return OK;
+ };
+ if (tryAndReportOnError(setAllocating) != OK) {
+ return;
}
AString componentName;
@@ -259,7 +270,7 @@
}
void CCodec::allocate(const AString &componentName) {
- // TODO: use C2ComponentStore to create component
+ ALOGV("allocate(%s)", componentName.c_str());
mListener.reset(new CCodecListener(this));
std::shared_ptr<C2Component> comp;
@@ -282,29 +293,30 @@
}
ALOGV("Success Create component: %s", componentName.c_str());
comp->setListener_vb(mListener, C2_MAY_BLOCK);
- {
+ mChannel->setComponent(comp);
+ auto setAllocated = [this, comp] {
Mutexed<State>::Locked state(mState);
if (state->get() != ALLOCATING) {
state->set(RELEASED);
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
state->set(ALLOCATED);
state->comp = comp;
+ return OK;
+ };
+ if (tryAndReportOnError(setAllocated) != OK) {
+ return;
}
- mChannel->setComponent(comp);
mCallback->onComponentAllocated(comp->intf()->getName().c_str());
}
void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
- {
+ auto checkAllocated = [this] {
Mutexed<State>::Locked state(mState);
- if (state->get() != ALLOCATED) {
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- return;
- }
+ return (state->get() != ALLOCATED) ? UNKNOWN_ERROR : OK;
+ };
+ if (tryAndReportOnError(checkAllocated) != OK) {
+ return;
}
sp<AMessage> msg(new AMessage(kWhatConfigure, this));
@@ -314,21 +326,22 @@
void CCodec::configure(const sp<AMessage> &msg) {
std::shared_ptr<C2ComponentInterface> intf;
- {
+ auto checkAllocated = [this, &intf] {
Mutexed<State>::Locked state(mState);
if (state->get() != ALLOCATED) {
state->set(RELEASED);
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
intf = state->comp->intf();
+ return OK;
+ };
+ if (tryAndReportOnError(checkAllocated) != OK) {
+ return;
}
sp<AMessage> inputFormat(new AMessage);
sp<AMessage> outputFormat(new AMessage);
- if (status_t err = [=] {
+ auto doConfig = [=] {
AString mime;
if (!msg->findString("mime", &mime)) {
return BAD_VALUE;
@@ -339,6 +352,11 @@
encoder = false;
}
+ // TODO: read from intf()
+ if ((!encoder) != (intf->getName().find("encoder") == std::string::npos)) {
+ return UNKNOWN_ERROR;
+ }
+
sp<RefBase> obj;
if (msg->findObject("native-window", &obj)) {
sp<Surface> surface = static_cast<Surface *>(obj.get());
@@ -386,14 +404,22 @@
if (audio) {
outputFormat->setInt32("channel-count", 2);
outputFormat->setInt32("sample-rate", 44100);
+ } else {
+ int32_t tmp;
+ if (msg->findInt32("width", &tmp)) {
+ outputFormat->setInt32("width", tmp);
+ }
+ if (msg->findInt32("height", &tmp)) {
+ outputFormat->setInt32("height", tmp);
+ }
}
}
// TODO
return OK;
- }() != OK) {
- mCallback->onError(err, ACTION_CODE_FATAL);
+ };
+ if (tryAndReportOnError(doConfig) != OK) {
return;
}
@@ -406,6 +432,22 @@
}
void CCodec::initiateCreateInputSurface() {
+ status_t err = [this] {
+ Mutexed<State>::Locked state(mState);
+ if (state->get() != ALLOCATED) {
+ return UNKNOWN_ERROR;
+ }
+ // TODO: read it from intf() properly.
+ if (state->comp->intf()->getName().find("encoder") == std::string::npos) {
+ return INVALID_OPERATION;
+ }
+ return OK;
+ }();
+ if (err != OK) {
+ mCallback->onInputSurfaceCreationFailed(err);
+ return;
+ }
+
(new AMessage(kWhatCreateInputSurface, this))->post();
}
@@ -477,15 +519,16 @@
}
void CCodec::initiateStart() {
- {
+ auto setStarting = [this] {
Mutexed<State>::Locked state(mState);
if (state->get() != ALLOCATED) {
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
state->set(STARTING);
+ return OK;
+ };
+ if (tryAndReportOnError(setStarting) != OK) {
+ return;
}
(new AMessage(kWhatStart, this))->post();
@@ -493,16 +536,18 @@
void CCodec::start() {
std::shared_ptr<C2Component> comp;
- {
+ auto checkStarting = [this, &comp] {
Mutexed<State>::Locked state(mState);
if (state->get() != STARTING) {
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
comp = state->comp;
+ return OK;
+ };
+ if (tryAndReportOnError(checkStarting) != OK) {
+ return;
}
+
c2_status_t err = comp->start();
if (err != C2_OK) {
// TODO: convert err into status_t
@@ -516,17 +561,22 @@
inputFormat = formats->inputFormat;
outputFormat = formats->outputFormat;
}
- mChannel->start(inputFormat, outputFormat);
+ status_t err2 = mChannel->start(inputFormat, outputFormat);
+ if (err2 != OK) {
+ mCallback->onError(err2, ACTION_CODE_FATAL);
+ return;
+ }
- {
+ auto setRunning = [this] {
Mutexed<State>::Locked state(mState);
if (state->get() != STARTING) {
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
state->set(RUNNING);
+ return OK;
+ };
+ if (tryAndReportOnError(setRunning) != OK) {
+ return;
}
mCallback->onStartCompleted();
}
@@ -652,13 +702,26 @@
}
void CCodec::signalFlush() {
- {
+ status_t err = [this] {
Mutexed<State>::Locked state(mState);
+ if (state->get() == FLUSHED) {
+ return ALREADY_EXISTS;
+ }
if (state->get() != RUNNING) {
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- return;
+ return UNKNOWN_ERROR;
}
state->set(FLUSHING);
+ return OK;
+ }();
+ switch (err) {
+ case ALREADY_EXISTS:
+ mCallback->onFlushCompleted();
+ return;
+ case OK:
+ break;
+ default:
+ mCallback->onError(err, ACTION_CODE_FATAL);
+ return;
}
(new AMessage(kWhatFlush, this))->post();
@@ -666,15 +729,16 @@
void CCodec::flush() {
std::shared_ptr<C2Component> comp;
- {
+ auto checkFlushing = [this, &comp] {
Mutexed<State>::Locked state(mState);
if (state->get() != FLUSHING) {
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
comp = state->comp;
+ return OK;
+ };
+ if (tryAndReportOnError(checkFlushing) != OK) {
+ return;
}
mChannel->stop();
@@ -696,18 +760,19 @@
}
void CCodec::signalResume() {
- {
+ auto setResuming = [this] {
Mutexed<State>::Locked state(mState);
if (state->get() != FLUSHED) {
- state.unlock();
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
- state.lock();
- return;
+ return UNKNOWN_ERROR;
}
state->set(RESUMING);
+ return OK;
+ };
+ if (tryAndReportOnError(setResuming) != OK) {
+ return;
}
- mChannel->start(nullptr, nullptr);
+ (void)mChannel->start(nullptr, nullptr);
{
Mutexed<State>::Locked state(mState);
@@ -727,6 +792,8 @@
}
void CCodec::signalEndOfInputStream() {
+ // TODO
+ mCallback->onSignaledInputEOS(INVALID_OPERATION);
}
void CCodec::signalRequestIDRFrame() {
@@ -735,9 +802,7 @@
void CCodec::onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
- for (std::unique_ptr<C2Work> &item : workItems) {
- queue->push_back(std::move(item));
- }
+ queue->splice(queue->end(), workItems);
(new AMessage(kWhatWorkDone, this))->post();
}
@@ -834,8 +899,8 @@
}
ALOGW("previous call to %s exceeded timeout", name.c_str());
- mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
initiateRelease(false);
+ mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
}
} // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 5ce49d9..65d637b 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -60,7 +60,10 @@
/**
* Set format for MediaCodec-facing buffers.
*/
- void setFormat(const sp<AMessage> &format) { mFormat = format; }
+ void setFormat(const sp<AMessage> &format) {
+ CHECK(format != nullptr);
+ mFormat = format;
+ }
/**
* Returns true if the buffers are operating under array mode.
@@ -179,21 +182,158 @@
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
-sp<LinearBlockBuffer> allocateLinearBuffer(
+/**
+ * Simple local buffer pool backed by std::vector.
+ */
+class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
+public:
+ /**
+ * Create a new LocalBufferPool object.
+ *
+ * \param poolCapacity max total size of buffers managed by this pool.
+ *
+ * \return a newly created pool object.
+ */
+ static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity) {
+ return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+ }
+
+ /**
+ * Return an ABuffer object whose size is at least |capacity|.
+ *
+ * \param capacity requested capacity
+ * \return nullptr if the pool capacity is reached
+ * an ABuffer object otherwise.
+ */
+ sp<ABuffer> newBuffer(size_t capacity) {
+ Mutex::Autolock lock(mMutex);
+ auto it = std::find_if(
+ mPool.begin(), mPool.end(),
+ [capacity](const std::vector<uint8_t> &vec) {
+ return vec.capacity() >= capacity;
+ });
+ if (it != mPool.end()) {
+ sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
+ mPool.erase(it);
+ return buffer;
+ }
+ if (mUsedSize + capacity > mPoolCapacity) {
+ while (!mPool.empty()) {
+ mUsedSize -= mPool.back().capacity();
+ mPool.pop_back();
+ }
+ if (mUsedSize + capacity > mPoolCapacity) {
+ ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
+ mUsedSize, capacity, mPoolCapacity);
+ return nullptr;
+ }
+ }
+ std::vector<uint8_t> vec(capacity);
+ mUsedSize += vec.capacity();
+ return new VectorBuffer(std::move(vec), shared_from_this());
+ }
+
+private:
+ /**
+ * ABuffer backed by std::vector.
+ */
+ class VectorBuffer : public ::android::ABuffer {
+ public:
+ /**
+ * Construct a VectorBuffer by taking the ownership of supplied vector.
+ *
+ * \param vec backing vector of the buffer. this object takes
+ * ownership at construction.
+ * \param pool a LocalBufferPool object to return the vector at
+ * destruction.
+ */
+ VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
+ : ABuffer(vec.data(), vec.capacity()),
+ mVec(std::move(vec)),
+ mPool(pool) {
+ }
+
+ ~VectorBuffer() override {
+ std::shared_ptr<LocalBufferPool> pool = mPool.lock();
+ if (pool) {
+ // If pool is alive, return the vector back to the pool so that
+ // it can be recycled.
+ pool->returnVector(std::move(mVec));
+ }
+ }
+
+ private:
+ std::vector<uint8_t> mVec;
+ std::weak_ptr<LocalBufferPool> mPool;
+ };
+
+ Mutex mMutex;
+ size_t mPoolCapacity;
+ size_t mUsedSize;
+ std::list<std::vector<uint8_t>> mPool;
+
+ /**
+ * Private constructor to prevent constructing non-managed LocalBufferPool.
+ */
+ explicit LocalBufferPool(size_t poolCapacity)
+ : mPoolCapacity(poolCapacity), mUsedSize(0) {
+ }
+
+ /**
+ * Take back the ownership of vec from the destructed VectorBuffer and put
+ * it in front of the pool.
+ */
+ void returnVector(std::vector<uint8_t> &&vec) {
+ Mutex::Autolock lock(mMutex);
+ mPool.push_front(std::move(vec));
+ }
+
+ DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
+};
+
+sp<LinearBlockBuffer> AllocateLinearBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
size_t size,
const C2MemoryUsage &usage) {
std::shared_ptr<C2LinearBlock> block;
- status_t err = pool->fetchLinearBlock(size, usage, &block);
- if (err != OK) {
+ c2_status_t err = pool->fetchLinearBlock(size, usage, &block);
+ if (err != C2_OK) {
return nullptr;
}
return LinearBlockBuffer::Allocate(format, block);
}
+sp<GraphicBlockBuffer> AllocateGraphicBuffer(
+ const std::shared_ptr<C2BlockPool> &pool,
+ const sp<AMessage> &format,
+ uint32_t pixelFormat,
+ const C2MemoryUsage &usage,
+ const std::shared_ptr<LocalBufferPool> &localBufferPool) {
+ int32_t width, height;
+ if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
+ ALOGD("format lacks width or height");
+ return nullptr;
+ }
+
+ std::shared_ptr<C2GraphicBlock> block;
+ c2_status_t err = pool->fetchGraphicBlock(
+ width, height, pixelFormat, usage, &block);
+ if (err != C2_OK) {
+ ALOGD("fetch graphic block failed: %d", err);
+ return nullptr;
+ }
+
+ return GraphicBlockBuffer::Allocate(
+ format,
+ block,
+ [localBufferPool](size_t capacity) {
+ return localBufferPool->newBuffer(capacity);
+ });
+}
+
class BuffersArrayImpl;
/**
@@ -383,6 +523,7 @@
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
public:
InputBuffersArray() = default;
+ ~InputBuffersArray() override = default;
void initialize(
const FlexBuffersImpl &impl,
@@ -432,7 +573,8 @@
// TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
+ sp<LinearBlockBuffer> newBuffer = AllocateLinearBuffer(
+ mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
@@ -458,7 +600,7 @@
kMinBufferArraySize,
[pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+ return AllocateLinearBuffer(pool, format, kLinearBufferSize, usage);
});
return std::move(array);
}
@@ -469,7 +611,54 @@
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
public:
- GraphicInputBuffers() = default;
+ GraphicInputBuffers() : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {}
+ ~GraphicInputBuffers() override = default;
+
+ bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+ // TODO: proper max input size
+ // TODO: read usage from intf
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
+ mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+ if (newBuffer == nullptr) {
+ return false;
+ }
+ *index = mImpl.assignSlot(newBuffer);
+ *buffer = newBuffer;
+ return true;
+ }
+
+ std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+ return mImpl.releaseSlot(buffer);
+ }
+
+ void flush() override {
+ // This is no-op by default unless we're in array mode where we need to keep
+ // track of the flushed work.
+ }
+
+ std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+ std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
+ array->setFormat(mFormat);
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ return AllocateGraphicBuffer(
+ pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
+ });
+ return std::move(array);
+ }
+
+private:
+ FlexBuffersImpl mImpl;
+ std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+class DummyInputBuffers : public CCodecBufferChannel::InputBuffers {
+public:
+ DummyInputBuffers() = default;
bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
return false;
@@ -495,7 +684,8 @@
class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
public:
- using CCodecBufferChannel::OutputBuffers::OutputBuffers;
+ OutputBuffersArray() = default;
+ ~OutputBuffersArray() override = default;
void initialize(
const FlexBuffersImpl &impl,
@@ -522,12 +712,14 @@
return clientBuffer->canCopy(buffer);
});
if (err != OK) {
- return false;
- }
- if (!c2Buffer->copy(buffer)) {
+ ALOGD("grabBuffer failed: %d", err);
return false;
}
c2Buffer->setFormat(mFormat);
+ if (!c2Buffer->copy(buffer)) {
+ ALOGD("copy buffer failed");
+ return false;
+ }
*clientBuffer = c2Buffer;
return true;
}
@@ -585,6 +777,7 @@
size_t *index,
sp<MediaCodecBuffer> *clientBuffer) override {
sp<Codec2Buffer> newBuffer = wrap(buffer);
+ newBuffer->setFormat(mFormat);
*index = mImpl.assignSlot(newBuffer);
*clientBuffer = newBuffer;
return true;
@@ -627,8 +820,21 @@
return std::move(array);
}
+ /**
+ * Return an appropriate Codec2Buffer object for the type of buffers.
+ *
+ * \param buffer C2Buffer object to wrap.
+ *
+ * \return appropriate Codec2Buffer object to wrap |buffer|.
+ */
virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+ /**
+ * Return an appropriate Codec2Buffer object for the type of buffers, to be
+ * used as an empty array buffer.
+ *
+ * \return appropriate Codec2Buffer object which can copy() from C2Buffers.
+ */
virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
private:
@@ -673,6 +879,34 @@
}
};
+class RawGraphicOutputBuffers : public FlexOutputBuffers {
+public:
+ RawGraphicOutputBuffers()
+ : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {
+ }
+ ~RawGraphicOutputBuffers() override = default;
+
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+ return ConstGraphicBlockBuffer::Allocate(
+ mFormat,
+ buffer,
+ [lbp = mLocalBufferPool](size_t capacity) {
+ return lbp->newBuffer(capacity);
+ });
+ }
+
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ return ConstGraphicBlockBuffer::AllocateEmpty(
+ mFormat,
+ [lbp = mLocalBufferPool](size_t capacity) {
+ return lbp->newBuffer(capacity);
+ });
+ }
+
+private:
+ std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
} // namespace
CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -735,55 +969,6 @@
void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
mComponent = component;
-
- C2StreamFormatConfig::input inputFormat(0u);
- C2StreamFormatConfig::output outputFormat(0u);
- c2_status_t err = mComponent->intf()->query_vb(
- { &inputFormat, &outputFormat },
- {},
- C2_DONT_BLOCK,
- nullptr);
- if (err != C2_OK) {
- // TODO: error
- return;
- }
-
- {
- Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-
- bool graphic = (inputFormat.value == C2FormatVideo);
- if (graphic) {
- buffers->reset(new GraphicInputBuffers);
- } else {
- buffers->reset(new LinearInputBuffers);
- }
-
- ALOGV("graphic = %s", graphic ? "true" : "false");
- std::shared_ptr<C2BlockPool> pool;
- if (graphic) {
- err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &pool);
- } else {
- err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION,
- component, &pool);
- }
-
- if (err == C2_OK) {
- (*buffers)->setPool(pool);
- } else {
- // TODO: error
- }
- }
-
- {
- Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-
- bool graphic = (outputFormat.value == C2FormatVideo);
- if (graphic) {
- buffers->reset(new GraphicOutputBuffers);
- } else {
- buffers->reset(new LinearOutputBuffers);
- }
- }
}
status_t CCodecBufferChannel::setInputSurface(
@@ -812,6 +997,7 @@
if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
flags |= C2FrameData::FLAG_CODEC_CONFIG;
}
+ ALOGV("queueInputBuffer: buffer->size() = %zu", buffer->size());
std::unique_ptr<C2Work> work(new C2Work);
work->input.flags = (C2FrameData::flags_t)flags;
work->input.ordinal.timestamp = timeUs;
@@ -968,13 +1154,68 @@
(*buffers)->getArray(array);
}
-void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+status_t CCodecBufferChannel::start(
+ const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+ C2StreamFormatConfig::input iStreamFormat(0u);
+ C2StreamFormatConfig::output oStreamFormat(0u);
+ c2_status_t err = mComponent->intf()->query_vb(
+ { &iStreamFormat, &oStreamFormat },
+ {},
+ C2_DONT_BLOCK,
+ nullptr);
+ if (err != C2_OK) {
+ return UNKNOWN_ERROR;
+ }
+
if (inputFormat != nullptr) {
Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+
+ bool graphic = (iStreamFormat.value == C2FormatVideo);
+ if (graphic) {
+ if (mInputSurface) {
+ buffers->reset(new DummyInputBuffers);
+ } else {
+ buffers->reset(new GraphicInputBuffers);
+ }
+ } else {
+ buffers->reset(new LinearInputBuffers);
+ }
(*buffers)->setFormat(inputFormat);
+
+ ALOGV("graphic = %s", graphic ? "true" : "false");
+ std::shared_ptr<C2BlockPool> pool;
+ if (graphic) {
+ err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, mComponent, &pool);
+ } else {
+ err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION,
+ mComponent, &pool);
+ }
+ if (err == C2_OK) {
+ (*buffers)->setPool(pool);
+ } else {
+ // TODO: error
+ }
}
+
if (outputFormat != nullptr) {
+ bool hasOutputSurface = false;
+ {
+ Mutexed<sp<Surface>>::Locked surface(mSurface);
+ hasOutputSurface = (*surface != nullptr);
+ }
+
Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+
+ bool graphic = (oStreamFormat.value == C2FormatVideo);
+ if (graphic) {
+ if (hasOutputSurface) {
+ buffers->reset(new GraphicOutputBuffers);
+ } else {
+ buffers->reset(new RawGraphicOutputBuffers);
+ }
+ } else {
+ buffers->reset(new LinearOutputBuffers);
+ }
(*buffers)->setFormat(outputFormat);
}
@@ -989,9 +1230,7 @@
if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
if (i == 0) {
ALOGE("start: cannot allocate memory at all");
- buffers.unlock();
- mOnError(NO_MEMORY, ACTION_CODE_FATAL);
- buffers.lock();
+ return NO_MEMORY;
} else {
ALOGV("start: cannot allocate memory, only %zu buffers allocated", i);
}
@@ -1003,6 +1242,7 @@
} else {
(void)mInputSurface->connect(mComponent);
}
+ return OK;
}
void CCodecBufferChannel::stop() {
@@ -1010,6 +1250,7 @@
mFirstValidFrameIndex = mFrameIndex.load();
if (mInputSurface != nullptr) {
mInputSurface->disconnect();
+ mInputSurface.reset();
}
}
@@ -1025,8 +1266,13 @@
}
void CCodecBufferChannel::onWorkDone(const std::unique_ptr<C2Work> &work) {
- if (work->result != OK) {
- ALOGE("work failed to complete: %d", work->result);
+ if (work->result != C2_OK) {
+ if (work->result == C2_NOT_FOUND) {
+ // TODO: Define what flushed work's result is.
+ ALOGD("flushed work; ignored.");
+ return;
+ }
+ ALOGD("work failed to complete: %d", work->result);
mOnError(work->result, ACTION_CODE_FATAL);
return;
}
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index 9b39ae9..1fc2c8c 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -37,15 +37,6 @@
#include "ih264d_defs.h"
-namespace {
-
-template <class T>
-inline int32_t floor32(T arg) {
- return (int32_t) std::llround(std::floor(arg));
-}
-
-} // namespace
-
namespace android {
struct iv_obj_t : public ::iv_obj_t {};
@@ -83,6 +74,20 @@
.build();
}
+void CopyPlane(
+ uint8_t *dst, const C2PlaneInfo &plane,
+ const uint8_t *src, uint32_t width, uint32_t height) {
+ for (uint32_t row = 0; row < height; ++row) {
+ for (uint32_t col = 0; col < width; ++col) {
+ *dst = *src;
+ dst += plane.colInc;
+ ++src;
+ }
+ dst -= plane.colInc * width;
+ dst += plane.rowInc;
+ }
+}
+
void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
@@ -103,7 +108,7 @@
c2_node_id_t id)
: SimpleC2Component(BuildIntf(name, id)),
mCodecCtx(NULL),
- mFlushOutBuffer(NULL),
+ mOutBuffer(NULL),
mIvColorFormat(IV_YUV_420P),
mChangingResolution(false),
mSignalledError(false),
@@ -164,9 +169,9 @@
}
}
- if (mFlushOutBuffer) {
- free(mFlushOutBuffer);
- mFlushOutBuffer = NULL;
+ if (mOutBuffer) {
+ free(mOutBuffer);
+ mOutBuffer = NULL;
}
return C2_OK;
}
@@ -244,6 +249,16 @@
return UNKNOWN_ERROR;
}
+
+ if (mOutBuffer != NULL) {
+ free(mOutBuffer);
+ }
+ uint32_t bufferSize = mWidth * mHeight * 3 / 2;
+ mOutBuffer = (uint8_t *)memalign(128, bufferSize);
+ if (NULL == mOutBuffer) {
+ ALOGE("Could not allocate output buffer of size %u", bufferSize);
+ return C2_NO_MEMORY;
+ }
return OK;
}
@@ -321,17 +336,6 @@
return UNKNOWN_ERROR;
}
- /* Allocate a picture buffer to flushed data */
- uint32_t displayStride = mWidth;
- uint32_t displayHeight = mHeight;
-
- uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
- mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
- if (NULL == mFlushOutBuffer) {
- ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
- return C2_NO_MEMORY;
- }
-
return OK;
}
@@ -499,14 +503,29 @@
outBuffer->width(), outBuffer->height(), width, height);
return false;
}
+ ALOGV("width = %u, stride[0] = %u, stride[1] = %u, stride[2] = %u",
+ outBuffer->width(),
+ outBuffer->layout().planes[0].rowInc,
+ outBuffer->layout().planes[1].rowInc,
+ outBuffer->layout().planes[2].rowInc);
+ const C2PlanarLayout &layout = outBuffer->layout();
ps_dec_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[0];
+ if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+ }
ps_dec_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[1];
+ if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+ }
ps_dec_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[2];
+ if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
+ }
} else {
- // mFlushOutBuffer always has the right size.
- ps_dec_ip->s_out_buffer.pu1_bufs[0] = mFlushOutBuffer;
- ps_dec_ip->s_out_buffer.pu1_bufs[1] = mFlushOutBuffer + sizeY;
- ps_dec_ip->s_out_buffer.pu1_bufs[2] = mFlushOutBuffer + sizeY + sizeUV;
+ // mOutBuffer always has the right size.
+ ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+ ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+ ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
}
ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
@@ -544,7 +563,8 @@
}
void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
- std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mAllocatedBlock));
+ std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(mAllocatedBlock);
+ mAllocatedBlock.reset();
auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
@@ -603,34 +623,54 @@
break;
}
(void)ensureDecoderState(pool);
- C2GraphicView output = mAllocatedBlock->map().get();
- if (output.error() != OK) {
- ALOGE("mapped err = %d", output.error());
- }
-
ivd_video_decode_ip_t s_dec_ip;
ivd_video_decode_op_t s_dec_op;
WORD32 timeDelay, timeTaken;
//size_t sizeY, sizeUV;
- if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
- ALOGE("Decoder arg setup failed");
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- mSignalledError = true;
- break;
+ {
+ C2GraphicView output = mAllocatedBlock->map().get();
+ if (output.error() != C2_OK) {
+ ALOGE("mapped err = %d", output.error());
+ work->result = output.error();
+ fillEmptyWork(work);
+ return;
+ }
+ if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
+ ALOGE("Decoder arg setup failed");
+ // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ mSignalledError = true;
+ break;
+ }
+ ALOGV("Decoder arg setup succeeded");
+ // If input dump is enabled, then write to file
+ DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ IV_API_CALL_STATUS_T status;
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
+ if (s_dec_op.u4_output_present) {
+ const C2PlanarLayout &layout = output.layout();
+ if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+ CopyPlane(output.data()[0], layout.planes[0], mOutBuffer, mWidth, mHeight);
+ }
+ if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+ CopyPlane(
+ output.data()[1], layout.planes[1],
+ mOutBuffer + (mWidth * mHeight), mWidth / 2, mHeight / 2);
+ }
+ if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+ CopyPlane(
+ output.data()[2], layout.planes[2],
+ mOutBuffer + (mWidth * mHeight * 5 / 4), mWidth / 2, mHeight / 2);
+ }
+ }
}
- ALOGV("Decoder arg setup succeeded");
- // If input dump is enabled, then write to file
- DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
-
- GETTIME(&mTimeStart, NULL);
- /* Compute time elapsed between end of previous decode()
- * to start of current decode() */
- TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
-
- IV_API_CALL_STATUS_T status;
- status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
- ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
bool unsupportedResolution =
(IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 6632bf3..d324a0f 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -189,7 +189,7 @@
struct timeval mTimeEnd; // Time at the end of decode()
// Internal buffer to be used to flush out the buffers from decoder
- uint8_t *mFlushOutBuffer;
+ uint8_t *mOutBuffer;
#ifdef FILE_DUMP_ENABLE
char mInFile[200];
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index eb3255f..51eee10 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -88,7 +88,7 @@
* Start queueing buffers to the component. This object should never queue
* buffers before this call.
*/
- void start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
+ status_t start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
/**
* Stop queueing buffers to the component. This object should never queue
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 9766b41..eeb889d 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -20,6 +20,7 @@
#include <C2Buffer.h>
+#include <media/hardware/VideoAPI.h>
#include <media/MediaCodecBuffer.h>
namespace android {
@@ -109,6 +110,11 @@
public:
/**
* Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param block C2LinearBlock object to wrap around.
+ * \return LinearBlockBuffer object with writable mapping.
+ * nullptr if unsuccessful.
*/
static sp<LinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
@@ -137,6 +143,11 @@
public:
/**
* Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param buffer linear C2Buffer object to wrap around.
+ * \return ConstLinearBlockBuffer object with readable mapping.
+ * nullptr if unsuccessful.
*/
static sp<ConstLinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
@@ -156,6 +167,110 @@
std::shared_ptr<C2Buffer> mBufferRef;
};
+/**
+ * MediaCodecBuffer implementation wraps around C2GraphicBlock.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class GraphicBlockBuffer : public Codec2Buffer {
+public:
+ /**
+ * Allocate a new GraphicBlockBuffer wrapping around C2GraphicBlock object.
+ * If |block| is not in good color formats, it allocates YV12 local buffer
+ * and copies the content over at asC2Buffer().
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param block C2GraphicBlock object to wrap around.
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return GraphicBlockBuffer object with writable mapping.
+ * nullptr if unsuccessful.
+ */
+ static sp<GraphicBlockBuffer> Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+
+ virtual ~GraphicBlockBuffer() = default;
+
+private:
+ GraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &buffer,
+ C2GraphicView &&view,
+ const std::shared_ptr<C2GraphicBlock> &block,
+ const sp<ABuffer> &imageData,
+ bool wrapped);
+ GraphicBlockBuffer() = delete;
+
+ inline MediaImage2 *imageData() { return (MediaImage2 *)mImageData->data(); }
+
+ C2GraphicView mView;
+ std::shared_ptr<C2GraphicBlock> mBlock;
+ sp<ABuffer> mImageData;
+ const bool mWrapped;
+};
+
+/**
+ * MediaCodecBuffer implementation wraps around graphic C2Buffer object.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class ConstGraphicBlockBuffer : public Codec2Buffer {
+public:
+ /**
+ * Allocate a new ConstGraphicBlockBuffer wrapping around C2Buffer object.
+ * If |buffer| is not in good color formats, it allocates YV12 local buffer
+ * and copies the content of |buffer| over to expose.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param buffer graphic C2Buffer object to wrap around.
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return ConstGraphicBlockBuffer object with readable mapping.
+ * nullptr if unsuccessful.
+ */
+ static sp<ConstGraphicBlockBuffer> Allocate(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ /**
+ * Allocate a new ConstGraphicBlockBuffer which allocates YV12 local buffer
+ * and copies the content of |buffer| over to expose.
+ *
+ * \param format mandatory buffer format for MediaCodecBuffer
+ * \param alloc a function to allocate backing ABuffer if needed.
+ * \return ConstGraphicBlockBuffer object with no wrapping buffer.
+ */
+ static sp<ConstGraphicBlockBuffer> AllocateEmpty(
+ const sp<AMessage> &format,
+ std::function<sp<ABuffer>(size_t)> alloc);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+ virtual ~ConstGraphicBlockBuffer() = default;
+
+private:
+ ConstGraphicBlockBuffer(
+ const sp<AMessage> &format,
+ const sp<ABuffer> &aBuffer,
+ std::unique_ptr<const C2GraphicView> &&view,
+ const std::shared_ptr<C2Buffer> &buffer,
+ const sp<ABuffer> &imageData,
+ bool wrapped);
+ ConstGraphicBlockBuffer() = delete;
+
+ sp<ABuffer> mImageData;
+ std::unique_ptr<const C2GraphicView> mView;
+ std::shared_ptr<C2Buffer> mBufferRef;
+ const bool mWrapped;
+};
+
} // namespace android
#endif // CODEC2_BUFFER_H_
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index 3a2670d..078b03e 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -69,6 +69,8 @@
private:
typedef std::chrono::time_point<std::chrono::steady_clock> TimePoint;
+ status_t tryAndReportOnError(std::function<status_t()> job);
+
void initiateStop();
void initiateRelease(bool sendCallback = true);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 3578bba..65faac9 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -779,7 +779,35 @@
int lastJpegStreamId = mJpegProcessor->getStreamId();
// If jpeg stream will slow down preview, make sure we remove it before starting preview
if (params.slowJpegMode) {
- mJpegProcessor->deleteStream();
+ // Pause preview if we are streaming
+ int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
+ if (activeRequestId != 0) {
+ res = mStreamingProcessor->togglePauseStream(/*pause*/true);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't pause streaming: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ res = mDevice->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ }
+
+ res = mJpegProcessor->deleteStream();
+
+ if (res != OK) {
+ ALOGE("%s: Camera %d: delete Jpeg stream failed: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+
+ if (activeRequestId != 0) {
+ res = mStreamingProcessor->togglePauseStream(/*pause*/false);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't unpause streaming: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ }
} else {
res = updateProcessorStream(mJpegProcessor, params);
if (res != OK) {
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index cc4249f..b7020fe 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -199,7 +199,11 @@
return INVALID_OPERATION;
}
- device->deleteStream(mCaptureStreamId);
+ status_t res = device->deleteStream(mCaptureStreamId);
+ if (res != OK) {
+ ALOGE("%s: delete stream %d failed!", __FUNCTION__, mCaptureStreamId);
+ return res;
+ }
mCaptureHeap.clear();
mCaptureWindow.clear();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 42bcb2d..67b5e06 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1550,7 +1550,7 @@
// CameraDevice semantics require device to already be idle before
// deleteStream is called, unlike for createStream.
if (mStatus == STATUS_ACTIVE) {
- ALOGV("%s: Camera %s: Device not idle", __FUNCTION__, mId.string());
+ ALOGW("%s: Camera %s: Device not idle", __FUNCTION__, mId.string());
return -EBUSY;
}
diff --git a/services/codec2/Android.bp b/services/codec2/Android.bp
new file mode 100644
index 0000000..4cfca1d
--- /dev/null
+++ b/services/codec2/Android.bp
@@ -0,0 +1,101 @@
+cc_binary {
+ name: "vendor.google.media.c2@1.0-service",
+ defaults: ["hidl_defaults"],
+ soc_specific: true,
+ relative_install_path: "hw",
+ srcs: [
+ "vendor.cpp",
+ ],
+
+ init_rc: ["vendor.google.media.c2@1.0-service.rc"],
+
+ shared_libs: [
+ "vendor.google.media.c2@1.0",
+ "libavservices_minijail_vendor",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libstagefright_codec2_hidl@1.0",
+ "libstagefright_codec2_vndk",
+ "libutils",
+ ],
+
+ arch: {
+ arm: {
+ required: ["codec2.vendor.base.policy"],
+ },
+ x86: {
+ required: ["codec2.vendor.base.policy"],
+ },
+ },
+
+ compile_multilib: "32",
+}
+
+cc_binary {
+ name: "vendor.google.media.c2@1.0-service-system",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ srcs: [
+ "system.cpp",
+ ],
+
+ init_rc: ["vendor.google.media.c2@1.0-service-system.rc"],
+
+ shared_libs: [
+ "vendor.google.media.c2@1.0",
+ "libavservices_minijail",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libstagefright_codec2_hidl@1.0",
+ "libstagefright_codec2_vndk",
+ "libutils",
+ "libv4l2_c2componentstore",
+ ],
+
+ arch: {
+ arm: {
+ required: ["codec2.system.base.policy"],
+ },
+ x86: {
+ required: ["codec2.system.base.policy"],
+ },
+ },
+
+ required: [
+ "libstagefright_soft_c2avcdec",
+ "libstagefright_soft_c2avcenc",
+ "libstagefright_soft_c2aacdec",
+ "libstagefright_soft_c2aacenc",
+ "libstagefright_soft_c2amrnbdec",
+ "libstagefright_soft_c2amrnbenc",
+ "libstagefright_soft_c2amrwbdec",
+ "libstagefright_soft_c2amrwbenc",
+ "libstagefright_soft_c2hevcdec",
+ "libstagefright_soft_c2g711alawdec",
+ "libstagefright_soft_c2g711mlawdec",
+ "libstagefright_soft_c2mpeg2dec",
+ "libstagefright_soft_c2h263dec",
+ "libstagefright_soft_c2h263enc",
+ "libstagefright_soft_c2mpeg4dec",
+ "libstagefright_soft_c2mpeg4enc",
+ "libstagefright_soft_c2mp3dec",
+ "libstagefright_soft_c2vorbisdec",
+ "libstagefright_soft_c2opusdec",
+ "libstagefright_soft_c2vp8dec",
+ "libstagefright_soft_c2vp9dec",
+ "libstagefright_soft_c2vp8enc",
+ "libstagefright_soft_c2vp9enc",
+ "libstagefright_soft_c2rawdec",
+ "libstagefright_soft_c2flacdec",
+ "libstagefright_soft_c2flacenc",
+ "libstagefright_soft_c2gsmdec",
+ ],
+
+ compile_multilib: "32",
+}
+
diff --git a/services/codec2/Android.mk b/services/codec2/Android.mk
new file mode 100644
index 0000000..fa49875
--- /dev/null
+++ b/services/codec2/Android.mk
@@ -0,0 +1,43 @@
+LOCAL_PATH := $(call my-dir)
+
+# vendor service seccomp policy
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
+include $(CLEAR_VARS)
+LOCAL_MODULE := codec2.vendor.base.policy
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy
+LOCAL_REQUIRED_MODULES := crash_dump.policy
+ifdef TARGET_2ND_ARCH
+ ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
+ LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy
+ else
+ LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
+ endif
+else
+ LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
+endif
+include $(BUILD_PREBUILT)
+endif
+
+# system service seccomp policy
+ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
+include $(CLEAR_VARS)
+LOCAL_MODULE := codec2.system.base.policy
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
+LOCAL_REQUIRED_MODULES := crash_dump.policy
+ifdef TARGET_2ND_ARCH
+ ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
+ LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_2ND_ARCH).policy
+ else
+ LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy
+ endif
+else
+ LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy
+endif
+include $(BUILD_PREBUILT)
+endif
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
+
diff --git a/services/codec2/seccomp_policy/codec2.system.base-arm.policy b/services/codec2/seccomp_policy/codec2.system.base-arm.policy
new file mode 100644
index 0000000..d5871d1
--- /dev/null
+++ b/services/codec2/seccomp_policy/codec2.system.base-arm.policy
@@ -0,0 +1,73 @@
+# Copyright (C) 2018 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.
+
+# Organized by frequency of systemcall - in descending order for
+# best performance.
+futex: 1
+ioctl: 1
+write: 1
+prctl: 1
+clock_gettime: 1
+getpriority: 1
+read: 1
+close: 1
+writev: 1
+dup: 1
+ppoll: 1
+mmap2: 1
+getrandom: 1
+
+# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
+# parser support for '<' is in this needs to be modified to also prevent
+# |old_address| and |new_address| from touching the exception vector page, which
+# on ARM is statically loaded at 0xffff 0000. See
+# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
+# for more details.
+mremap: arg3 == 3
+munmap: 1
+mprotect: 1
+madvise: 1
+openat: 1
+sigaltstack: 1
+clone: 1
+setpriority: 1
+getuid32: 1
+fstat64: 1
+fstatfs64: 1
+pread64: 1
+faccessat: 1
+readlinkat: 1
+exit: 1
+rt_sigprocmask: 1
+set_tid_address: 1
+restart_syscall: 1
+exit_group: 1
+rt_sigreturn: 1
+pipe2: 1
+gettimeofday: 1
+sched_yield: 1
+nanosleep: 1
+lseek: 1
+_llseek: 1
+sched_get_priority_max: 1
+sched_get_priority_min: 1
+statfs64: 1
+sched_setscheduler: 1
+fstatat64: 1
+ugetrlimit: 1
+getdents64: 1
+getrandom: 1
+
+@include /system/etc/seccomp_policy/crash_dump.arm.policy
+
diff --git a/services/codec2/seccomp_policy/codec2.system.base-x86.policy b/services/codec2/seccomp_policy/codec2.system.base-x86.policy
new file mode 100644
index 0000000..20c7625
--- /dev/null
+++ b/services/codec2/seccomp_policy/codec2.system.base-x86.policy
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 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.
+
+read: 1
+mprotect: 1
+prctl: 1
+openat: 1
+getuid32: 1
+writev: 1
+ioctl: 1
+close: 1
+mmap2: 1
+fstat64: 1
+madvise: 1
+fstatat64: 1
+futex: 1
+munmap: 1
+faccessat: 1
+_llseek: 1
+lseek: 1
+clone: 1
+sigaltstack: 1
+setpriority: 1
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+ugetrlimit: 1
+readlinkat: 1
+_llseek: 1
+fstatfs64: 1
+pread64: 1
+mremap: 1
+dup: 1
+set_tid_address: 1
+write: 1
+nanosleep: 1
+
+# Required by AddressSanitizer
+gettid: 1
+sched_yield: 1
+getpid: 1
+gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86.policy
+
diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy
new file mode 100644
index 0000000..d5871d1
--- /dev/null
+++ b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy
@@ -0,0 +1,73 @@
+# Copyright (C) 2018 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.
+
+# Organized by frequency of systemcall - in descending order for
+# best performance.
+futex: 1
+ioctl: 1
+write: 1
+prctl: 1
+clock_gettime: 1
+getpriority: 1
+read: 1
+close: 1
+writev: 1
+dup: 1
+ppoll: 1
+mmap2: 1
+getrandom: 1
+
+# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
+# parser support for '<' is in this needs to be modified to also prevent
+# |old_address| and |new_address| from touching the exception vector page, which
+# on ARM is statically loaded at 0xffff 0000. See
+# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
+# for more details.
+mremap: arg3 == 3
+munmap: 1
+mprotect: 1
+madvise: 1
+openat: 1
+sigaltstack: 1
+clone: 1
+setpriority: 1
+getuid32: 1
+fstat64: 1
+fstatfs64: 1
+pread64: 1
+faccessat: 1
+readlinkat: 1
+exit: 1
+rt_sigprocmask: 1
+set_tid_address: 1
+restart_syscall: 1
+exit_group: 1
+rt_sigreturn: 1
+pipe2: 1
+gettimeofday: 1
+sched_yield: 1
+nanosleep: 1
+lseek: 1
+_llseek: 1
+sched_get_priority_max: 1
+sched_get_priority_min: 1
+statfs64: 1
+sched_setscheduler: 1
+fstatat64: 1
+ugetrlimit: 1
+getdents64: 1
+getrandom: 1
+
+@include /system/etc/seccomp_policy/crash_dump.arm.policy
+
diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy
new file mode 100644
index 0000000..20c7625
--- /dev/null
+++ b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 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.
+
+read: 1
+mprotect: 1
+prctl: 1
+openat: 1
+getuid32: 1
+writev: 1
+ioctl: 1
+close: 1
+mmap2: 1
+fstat64: 1
+madvise: 1
+fstatat64: 1
+futex: 1
+munmap: 1
+faccessat: 1
+_llseek: 1
+lseek: 1
+clone: 1
+sigaltstack: 1
+setpriority: 1
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+ugetrlimit: 1
+readlinkat: 1
+_llseek: 1
+fstatfs64: 1
+pread64: 1
+mremap: 1
+dup: 1
+set_tid_address: 1
+write: 1
+nanosleep: 1
+
+# Required by AddressSanitizer
+gettid: 1
+sched_yield: 1
+getpid: 1
+gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86.policy
+
diff --git a/services/codec2/system.cpp b/services/codec2/system.cpp
new file mode 100644
index 0000000..d6ec644
--- /dev/null
+++ b/services/codec2/system.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018 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 "vendor.google.media.c2@1.0-service"
+
+#include <C2PlatformSupport.h>
+#include <C2V4l2Support.h>
+#include <cutils/properties.h>
+
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <hidl/HidlTransportSupport.h>
+#include <minijail.h>
+
+// TODO: Remove this once "setenv()" call is removed.
+#include <stdlib.h>
+
+// This is created by module "codec2.system.base.policy". This can be modified.
+static constexpr char kBaseSeccompPolicyPath[] =
+ "/system/etc/seccomp_policy/codec2.system.base.policy";
+
+// Additional device-specific seccomp permissions can be added in this file.
+static constexpr char kExtSeccompPolicyPath[] =
+ "/system/etc/seccomp_policy/codec2.system.ext.policy";
+
+int main(int /* argc */, char** /* argv */) {
+ ALOGD("vendor.google.media.c2@1.0-service-system starting...");
+
+ // TODO: Remove this when all the build settings and sepolicies are in place.
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+
+ signal(SIGPIPE, SIG_IGN);
+ android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
+
+ // Extra threads may be needed to handle a stacked IPC sequence that
+ // contains alternating binder and hwbinder calls. (See b/35283480.)
+ android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Create IComponentStore service.
+ {
+ using namespace ::vendor::google::media::c2::V1_0;
+ android::sp<IComponentStore> store =
+ new implementation::ComponentStore(
+ android::GetCodec2PlatformComponentStore());
+ if (store == nullptr) {
+ ALOGE("Cannot create Codec2's IComponentStore system service.");
+ } else {
+ if (store->registerAsService("system") != android::OK) {
+ ALOGE("Cannot register Codec2's "
+ "IComponentStore system service.");
+ } else {
+ ALOGI("Codec2's IComponentStore system service created.");
+ }
+ }
+
+ // To enable the v4l2 service, set this sysprop and add "v4l2" instance
+ // to the system manifest file.
+ if (property_get_bool("debug.stagefright.ccodec_v4l2", false)) {
+ store = new implementation::ComponentStore(
+ android::GetCodec2VDAComponentStore());
+ if (store == nullptr) {
+ ALOGE("Cannot create Codec2's IComponentStore V4L2 service.");
+ } else {
+ if (store->registerAsService("v4l2") != android::OK) {
+ ALOGE("Cannot register Codec2's "
+ "IComponentStore V4L2 service.");
+ } else {
+ ALOGI("Codec2's IComponentStore V4L2 service created.");
+ }
+ }
+ }
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return 0;
+}
+
diff --git a/services/codec2/vendor.cpp b/services/codec2/vendor.cpp
new file mode 100644
index 0000000..60b51e2
--- /dev/null
+++ b/services/codec2/vendor.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018 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 "vendor.google.media.c2@1.0-service"
+
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <hidl/HidlTransportSupport.h>
+#include <minijail.h>
+
+#include <C2Component.h>
+
+// TODO: Remove this once "setenv()" call is removed.
+#include <stdlib.h>
+
+// This is created by module "codec2.vendor.base.policy". This can be modified.
+static constexpr char kBaseSeccompPolicyPath[] =
+ "/vendor/etc/seccomp_policy/codec2.vendor.base.policy";
+
+// Additional device-specific seccomp permissions can be added in this file.
+static constexpr char kExtSeccompPolicyPath[] =
+ "/vendor/etc/seccomp_policy/codec2.vendor.ext.policy";
+
+// TODO: Replace with a valid C2ComponentStore implementation.
+class DummyC2Store : public C2ComponentStore {
+public:
+ DummyC2Store() = default;
+
+ virtual ~DummyC2Store() override = default;
+
+ virtual C2String getName() const override {
+ return "default";
+ }
+
+ virtual c2_status_t createComponent(
+ C2String /*name*/,
+ std::shared_ptr<C2Component>* const /*component*/) override {
+ return C2_NOT_FOUND;
+ }
+
+ virtual c2_status_t createInterface(
+ C2String /* name */,
+ std::shared_ptr<C2ComponentInterface>* const /* interface */) override {
+ return C2_NOT_FOUND;
+ }
+
+ virtual std::vector<std::shared_ptr<const C2Component::Traits>>
+ listComponents() override {
+ return {};
+ }
+
+ virtual c2_status_t copyBuffer(
+ std::shared_ptr<C2GraphicBuffer> /* src */,
+ std::shared_ptr<C2GraphicBuffer> /* dst */) override {
+ return C2_OMITTED;
+ }
+
+ virtual c2_status_t query_sm(
+ const std::vector<C2Param*>& /* stackParams */,
+ const std::vector<C2Param::Index>& /* heapParamIndices */,
+ std::vector<std::unique_ptr<C2Param>>* const /* heapParams */) const override {
+ return C2_OMITTED;
+ }
+
+ virtual c2_status_t config_sm(
+ const std::vector<C2Param*>& /* params */,
+ std::vector<std::unique_ptr<C2SettingResult>>* const /* failures */) override {
+ return C2_OMITTED;
+ }
+
+ virtual std::shared_ptr<C2ParamReflector> getParamReflector() const override {
+ return nullptr;
+ }
+
+ virtual c2_status_t querySupportedParams_nb(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const /* params */) const override {
+ return C2_OMITTED;
+ }
+
+ virtual c2_status_t querySupportedValues_sm(
+ std::vector<C2FieldSupportedValuesQuery>& /* fields */) const override {
+ return C2_OMITTED;
+ }
+};
+
+int main(int /* argc */, char** /* argv */) {
+ ALOGD("vendor.google.media.c2@1.0-service starting...");
+
+ // TODO: Remove this when all the build settings and sepolicies are in place.
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+
+ signal(SIGPIPE, SIG_IGN);
+ android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
+
+ // Extra threads may be needed to handle a stacked IPC sequence that
+ // contains alternating binder and hwbinder calls. (See b/35283480.)
+ android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Create IComponentStore service.
+ {
+ using namespace ::vendor::google::media::c2::V1_0;
+ android::sp<IComponentStore> store =
+ new implementation::ComponentStore(
+ // TODO: Replace this with a valid C2ComponentStore
+ // implementation.
+ std::make_shared<DummyC2Store>());
+ if (store == nullptr) {
+ ALOGE("Cannot create Codec2's IComponentStore service.");
+ } else {
+ if (store->registerAsService("default") != android::OK) {
+ ALOGE("Cannot register Codec2's "
+ "IComponentStore service.");
+ } else {
+ ALOGI("Codec2's IComponentStore service created.");
+ }
+ }
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return 0;
+}
+
diff --git a/services/codec2/vendor.google.media.c2@1.0-service-system.rc b/services/codec2/vendor.google.media.c2@1.0-service-system.rc
new file mode 100644
index 0000000..0577a1d
--- /dev/null
+++ b/services/codec2/vendor.google.media.c2@1.0-service-system.rc
@@ -0,0 +1,7 @@
+service vendor-google-media-c2-system-hal-1-0 /system/bin/hw/vendor.google.media.c2@1.0-service-system
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
+
diff --git a/services/codec2/vendor.google.media.c2@1.0-service.rc b/services/codec2/vendor.google.media.c2@1.0-service.rc
new file mode 100644
index 0000000..3e7e0a6
--- /dev/null
+++ b/services/codec2/vendor.google.media.c2@1.0-service.rc
@@ -0,0 +1,7 @@
+service vendor-google-media-c2-hal-1-0 /vendor/bin/hw/vendor.google.media.c2@1.0-service
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
+