Merge "heic content_encoding field is optional"
diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h
new file mode 120000
index 0000000..e2dd082
--- /dev/null
+++ b/include/media/ExtractorUtils.h
@@ -0,0 +1 @@
+../../media/libmediaextractor/include/media/ExtractorUtils.h
\ No newline at end of file
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 3f832bc..fc60fd4 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -22,6 +22,7 @@
#include "MatroskaExtractor.h"
#include <media/DataSourceBase.h>
+#include <media/ExtractorUtils.h>
#include <media/MediaTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -1108,7 +1109,7 @@
meta.setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
int32_t maxInputSize = 64 << 10;
- sp<FLACDecoder> flacDecoder = FLACDecoder::Create();
+ FLACDecoder *flacDecoder = FLACDecoder::Create();
if (flacDecoder != NULL
&& flacDecoder->parseMetadata((const uint8_t*)codecPrivate, codecPrivateSize) == OK) {
FLAC__StreamMetadata_StreamInfo streamInfo = flacDecoder->getStreamInfo();
@@ -1120,6 +1121,7 @@
&& streamInfo.channels != 0
&& ((streamInfo.bits_per_sample + 7) / 8) >
INT32_MAX / streamInfo.max_blocksize / streamInfo.channels) {
+ delete flacDecoder;
return ERROR_MALFORMED;
}
maxInputSize = ((streamInfo.bits_per_sample + 7) / 8)
@@ -1128,6 +1130,7 @@
}
meta.setInt32(kKeyMaxInputSize, maxInputSize);
+ delete flacDecoder;
return OK;
}
@@ -1143,13 +1146,13 @@
}
const mkvparser::Block::Frame &frame = block->GetFrame(0);
- sp<ABuffer> abuf = new ABuffer(frame.len);
- long n = frame.Read(mReader, abuf->data());
+ auto tmpData = heapbuffer<unsigned char>(frame.len);
+ long n = frame.Read(mReader, tmpData.get());
if (n != 0) {
return ERROR_MALFORMED;
}
- if (!MakeAVCCodecSpecificData(trackInfo->mMeta, abuf)) {
+ if (!MakeAVCCodecSpecificData(trackInfo->mMeta, tmpData.get(), frame.len)) {
return ERROR_MALFORMED;
}
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 4d49013..b2fe69c 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -22,6 +22,7 @@
#include <cutils/properties.h>
#include <media/DataSourceBase.h>
+#include <media/ExtractorUtils.h>
#include <media/MediaTrack.h>
#include <media/VorbisComment.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -990,21 +991,11 @@
return OK;
}
-struct TmpData {
- uint8_t *data;
- TmpData(size_t size) {
- data = (uint8_t*) malloc(size);
- }
- ~TmpData() {
- free(data);
- }
-};
-
status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) {
// add artificial framing bit so we can reuse _vorbis_unpack_comment
int32_t commentSize = buffer->range_length() + 1;
- TmpData commentDataHolder(commentSize);
- uint8_t *commentData = commentDataHolder.data;
+ auto tmp = heapbuffer<uint8_t>(commentSize);
+ uint8_t *commentData = tmp.get();
if (commentData == nullptr) {
return ERROR_MALFORMED;
}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 2dfbdca..4b17a9b 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -332,8 +332,8 @@
if (frameInfo != nullptr) {
frameInfo->set(
- videoFrame->mDisplayWidth,
- videoFrame->mDisplayHeight,
+ videoFrame->mWidth,
+ videoFrame->mHeight,
videoFrame->mRotationAngle,
videoFrame->mBytesPerPixel,
videoFrame->mIccSize,
@@ -410,8 +410,8 @@
if (frameInfo != nullptr) {
frameInfo->set(
- videoFrame->mDisplayWidth,
- videoFrame->mDisplayHeight,
+ videoFrame->mWidth,
+ videoFrame->mHeight,
videoFrame->mRotationAngle,
videoFrame->mBytesPerPixel,
videoFrame->mIccSize,
@@ -430,12 +430,12 @@
return false;
}
VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
- if (mCurScanline >= videoFrame->mDisplayHeight) {
+ if (mCurScanline >= videoFrame->mHeight) {
ALOGE("no more scanline available");
return false;
}
uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
- memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
+ memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
return true;
}
@@ -447,8 +447,8 @@
uint32_t oldScanline = mCurScanline;
mCurScanline += count;
- if (mCurScanline > videoFrame->mDisplayHeight) {
- mCurScanline = videoFrame->mDisplayHeight;
+ if (mCurScanline > videoFrame->mHeight) {
+ mCurScanline = videoFrame->mHeight;
}
return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
}
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index 1096df4..96dd004 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -55,6 +55,8 @@
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BUS),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_PROXY),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_HEADSET),
+ MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HEARING_AID),
+ MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_ECHO_CANCELLER),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_DEFAULT),
// STUB must be after DEFAULT, so the latter is picked up by toString first.
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_STUB),
@@ -115,9 +117,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DIRECT_PCM),
MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
- // FIXME: this cast will be removed when the flag will be
- // declared in types.hal for audio HAL V4.0 and auto imported to audio-base.h
- {"AUDIO_OUTPUT_FLAG_INCALL_MUSIC", static_cast<audio_output_flags_t>(AUDIO_OUTPUT_FLAG_INCALL_MUSIC)},
+ MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
TERMINATOR
};
@@ -131,6 +131,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_SYNC),
MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_MMAP_NOIRQ),
MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_VOIP_TX),
+ MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_HW_AV_SYNC),
TERMINATOR
};
@@ -167,6 +168,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_LD),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V2),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_ELD),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_XHE),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_VORBIS),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V1),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V2),
@@ -197,6 +199,11 @@
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_HD),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AC4),
MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LDAC),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_E_AC3_JOC),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_1_0),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_0),
+ MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_1),
TERMINATOR
};
@@ -206,10 +213,18 @@
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT0POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT0POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT1POINT2),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_BACK),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_SIDE),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_SURROUND),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_PENTA),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
TERMINATOR
@@ -222,6 +237,11 @@
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_STEREO),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_6),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT0POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT1POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT0POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT1POINT2),
+ MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_5POINT1),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
diff --git a/media/libmediaextractor/include/media/ExtractorUtils.h b/media/libmediaextractor/include/media/ExtractorUtils.h
new file mode 100644
index 0000000..22f9349
--- /dev/null
+++ b/media/libmediaextractor/include/media/ExtractorUtils.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef EXTRACTOR_UTILS_H_
+
+#define EXTRACTOR_UTILS_H_
+
+#include <memory>
+
+namespace android {
+
+template <class T>
+std::unique_ptr<T[]> heapbuffer(size_t size) {
+ return std::unique_ptr<T[]>(new (std::nothrow) T[size]);
+}
+
+} // namespace android
+
+#endif // UTILS_H_
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/ACodec.cpp b/media/libstagefright/ACodec.cpp
index b296622..86d7c08 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -553,6 +553,7 @@
mNativeWindowUsageBits(0),
mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),
mIsVideo(false),
+ mIsImage(false),
mIsEncoder(false),
mFatalError(false),
mShutdownInProgress(false),
@@ -1713,6 +1714,7 @@
mIsEncoder = encoder;
mIsVideo = !strncasecmp(mime, "video/", 6);
+ mIsImage = !strncasecmp(mime, "image/", 6);
mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
@@ -1728,10 +1730,11 @@
// FLAC encoder or video encoder in constant quality mode doesn't need a
// bitrate, other encoders do.
if (encoder) {
- if (mIsVideo && !findVideoBitrateControlInfo(
- msg, &bitrateMode, &bitrate, &quality)) {
- return INVALID_OPERATION;
- } else if (!mIsVideo && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
+ if (mIsVideo || mIsImage) {
+ if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) {
+ return INVALID_OPERATION;
+ }
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
&& !msg->findInt32("bitrate", &bitrate)) {
return INVALID_OPERATION;
}
@@ -2010,7 +2013,7 @@
(void)msg->findInt32("pcm-encoding", (int32_t*)&pcmEncoding);
// invalid encodings will default to PCM-16bit in setupRawAudioFormat.
- if (mIsVideo) {
+ if (mIsVideo || mIsImage) {
// determine need for software renderer
bool usingSwRenderer = false;
if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
@@ -2290,7 +2293,7 @@
}
// create data converters if needed
- if (!mIsVideo && err == OK) {
+ if (!mIsVideo && !mIsImage && err == OK) {
AudioEncoding codecPcmEncoding = kAudioEncodingPcm16bit;
if (encoder) {
(void)mInputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding);
@@ -3215,6 +3218,7 @@
{ MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 },
{ MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 },
{ MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, OMX_VIDEO_CodingDolbyVision },
+ { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, OMX_VIDEO_CodingImageHEIC },
};
static status_t GetVideoCodingTypeFromMime(
@@ -3872,7 +3876,8 @@
break;
case OMX_VIDEO_CodingHEVC:
- err = setupHEVCEncoderParameters(msg);
+ case OMX_VIDEO_CodingImageHEIC:
+ err = setupHEVCEncoderParameters(msg, outputFormat);
break;
case OMX_VIDEO_CodingVP8:
@@ -4378,27 +4383,63 @@
return configureBitrate(bitrateMode, bitrate);
}
-status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
- float iFrameInterval;
- if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
- return INVALID_OPERATION;
+status_t ACodec::configureImageGrid(
+ const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
+ int32_t gridWidth, gridHeight, gridRows, gridCols;
+ if (!msg->findInt32("grid-width", &gridWidth) ||
+ !msg->findInt32("grid-height", &gridHeight) ||
+ !msg->findInt32("grid-rows", &gridRows) ||
+ !msg->findInt32("grid-cols", &gridCols)) {
+ return OK;
}
+ OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE gridType;
+ InitOMXParams(&gridType);
+ gridType.nPortIndex = kPortIndexOutput;
+ gridType.bEnabled = OMX_TRUE;
+ gridType.nGridWidth = gridWidth;
+ gridType.nGridHeight = gridHeight;
+ gridType.nGridRows = gridRows;
+ gridType.nGridCols = gridCols;
+
+ status_t err = mOMXNode->setParameter(
+ (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid,
+ &gridType, sizeof(gridType));
+
+ // for video encoders, grid config is only a hint.
+ if (!mIsImage) {
+ return OK;
+ }
+
+ // image encoders must support grid config.
+ if (err != OK) {
+ return err;
+ }
+
+ // query to get the image encoder's real grid config as it might be
+ // different from the requested, and transfer that to the output.
+ err = mOMXNode->getParameter(
+ (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid,
+ &gridType, sizeof(gridType));
+
+ if (err == OK && gridType.bEnabled) {
+ outputFormat->setInt32("grid-width", gridType.nGridWidth);
+ outputFormat->setInt32("grid-height", gridType.nGridHeight);
+ outputFormat->setInt32("grid-rows", gridType.nGridRows);
+ outputFormat->setInt32("grid-cols", gridType.nGridCols);
+ }
+
+ return err;
+}
+
+status_t ACodec::setupHEVCEncoderParameters(
+ const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
OMX_VIDEO_CONTROLRATETYPE bitrateMode;
int32_t bitrate, quality;
if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) {
return INVALID_OPERATION;
}
- float frameRate;
- if (!msg->findFloat("frame-rate", &frameRate)) {
- int32_t tmp;
- if (!msg->findInt32("frame-rate", &tmp)) {
- return INVALID_OPERATION;
- }
- frameRate = (float)tmp;
- }
-
OMX_VIDEO_PARAM_HEVCTYPE hevcType;
InitOMXParams(&hevcType);
hevcType.nPortIndex = kPortIndexOutput;
@@ -4426,7 +4467,27 @@
hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
}
// TODO: finer control?
- hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
+ if (mIsImage) {
+ hevcType.nKeyFrameInterval = 1;
+ } else {
+ float iFrameInterval;
+ if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ hevcType.nKeyFrameInterval =
+ setPFramesSpacing(iFrameInterval, frameRate) + 1;
+ }
+
err = mOMXNode->setParameter(
(OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
@@ -4434,6 +4495,12 @@
return err;
}
+ err = configureImageGrid(msg, outputFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
return configureBitrate(bitrateMode, bitrate, quality);
}
@@ -4875,7 +4942,8 @@
(void)getHDRStaticInfoForVideoCodec(kPortIndexInput, notify);
}
uint32_t latency = 0;
- if (mIsEncoder && getLatency(&latency) == OK && latency > 0) {
+ if (mIsEncoder && !mIsImage &&
+ getLatency(&latency) == OK && latency > 0) {
notify->setInt32("latency", latency);
}
}
@@ -4931,7 +4999,8 @@
notify->setString("mime", mime.c_str());
}
uint32_t intraRefreshPeriod = 0;
- if (mIsEncoder && getIntraRefreshPeriod(&intraRefreshPeriod) == OK
+ if (mIsEncoder && !mIsImage &&
+ getIntraRefreshPeriod(&intraRefreshPeriod) == OK
&& intraRefreshPeriod > 0) {
notify->setInt32("intra-refresh-period", intraRefreshPeriod);
}
@@ -6346,9 +6415,6 @@
sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
- Vector<AString> matchingCodecs;
- Vector<AString> owners;
-
AString componentName;
CHECK(msg->findString("componentName", &componentName));
@@ -6424,7 +6490,7 @@
mCodec->mOMX = omx;
mCodec->mOMXNode = omxNode;
- mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
+ mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str(), info);
mCodec->changeState(mCodec->mLoadedState);
return true;
@@ -8226,8 +8292,9 @@
}
bool isVideo = strncasecmp(mime, "video/", 6) == 0;
+ bool isImage = strncasecmp(mime, "image/", 6) == 0;
- if (isVideo) {
+ if (isVideo || isImage) {
OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
InitOMXParams(¶m);
param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 65348e5..13d80f5 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -137,7 +137,6 @@
"libstagefright_omx",
"libstagefright_omx_utils",
"libstagefright_xmlparser",
- "libdl",
"libRScpp",
"libhidlallocatorutils",
"libhidlbase",
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..84962a9 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;
@@ -893,7 +1079,7 @@
GraphicBuffer::CLONE_HANDLE,
blocks.front().width(),
blocks.front().height(),
- HAL_PIXEL_FORMAT_YV12,
+ HAL_PIXEL_FORMAT_YCbCr_420_888,
// TODO
1,
(uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -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/Codec2InfoBuilder.cpp b/media/libstagefright/Codec2InfoBuilder.cpp
index 7ce2ff1..78c4e38 100644
--- a/media/libstagefright/Codec2InfoBuilder.cpp
+++ b/media/libstagefright/Codec2InfoBuilder.cpp
@@ -31,6 +31,63 @@
using ConstTraitsPtr = std::shared_ptr<const C2Component::Traits>;
+struct ProfileLevel {
+ uint32_t profile;
+ uint32_t level;
+};
+static const ProfileLevel kAvcProfileLevels[] = {
+ { 0x01, 0x0001 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1 },
+ { 0x01, 0x0002 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+ { 0x01, 0x0004 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+ { 0x01, 0x0008 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+ { 0x01, 0x0010 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+ { 0x01, 0x0020 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2 },
+ { 0x01, 0x0040 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+ { 0x01, 0x0080 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+ { 0x01, 0x0100 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3 },
+ { 0x01, 0x0200 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+ { 0x01, 0x0400 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+ { 0x01, 0x0800 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4 },
+ { 0x01, 0x1000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+ { 0x01, 0x2000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
+ { 0x01, 0x4000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5 },
+ { 0x01, 0x8000 }, // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
+
+ { 0x02, 0x0001 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1 },
+ { 0x02, 0x0002 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b },
+ { 0x02, 0x0004 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 },
+ { 0x02, 0x0008 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 },
+ { 0x02, 0x0010 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 },
+ { 0x02, 0x0020 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2 },
+ { 0x02, 0x0040 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 },
+ { 0x02, 0x0080 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 },
+ { 0x02, 0x0100 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3 },
+ { 0x02, 0x0200 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 },
+ { 0x02, 0x0400 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 },
+ { 0x02, 0x0800 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4 },
+ { 0x02, 0x1000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
+ { 0x02, 0x2000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel42 },
+ { 0x02, 0x4000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel5 },
+ { 0x02, 0x8000 }, // { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel51 },
+
+ { 0x04, 0x0001 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1 },
+ { 0x04, 0x0002 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel1b },
+ { 0x04, 0x0004 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel11 },
+ { 0x04, 0x0008 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel12 },
+ { 0x04, 0x0010 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel13 },
+ { 0x04, 0x0020 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel2 },
+ { 0x04, 0x0040 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel21 },
+ { 0x04, 0x0080 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel22 },
+ { 0x04, 0x0100 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel3 },
+ { 0x04, 0x0200 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel31 },
+ { 0x04, 0x0400 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel32 },
+ { 0x04, 0x0800 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel4 },
+ { 0x04, 0x1000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel41 },
+ { 0x04, 0x2000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel42 },
+ { 0x04, 0x4000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel5 },
+ { 0x04, 0x8000 }, // { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel51 },
+};
+
status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
// Obtain C2ComponentStore
std::shared_ptr<C2ComponentStore> store = GetCodec2PlatformComponentStore();
@@ -86,6 +143,12 @@
caps->addDetail(key.c_str(), value.c_str());
}
}
+ // TODO: get this from intf(), and apply to other codecs as well.
+ if (mediaType.find("video/avc") != std::string::npos && !encoder) {
+ for (const auto& pl : kAvcProfileLevels) {
+ caps->addProfileLevel(pl.profile, pl.level);
+ }
+ }
// TODO: get this from intf().
if (mediaType.find("video") != std::string::npos && !encoder) {
caps->addColorFormat(0x7F420888); // COLOR_FormatYUV420Flexible
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7d5c63a..e59259d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -278,7 +278,8 @@
virtual void onReleaseCompleted() override;
virtual void onFlushCompleted() override;
virtual void onError(status_t err, enum ActionCode actionCode) override;
- virtual void onComponentAllocated(const char *componentName) override;
+ virtual void onComponentAllocated(
+ const char *componentName, const sp<MediaCodecInfo> &codecInfo) override;
virtual void onComponentConfigured(
const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) override;
virtual void onInputSurfaceCreated(
@@ -338,10 +339,14 @@
notify->post();
}
-void CodecCallback::onComponentAllocated(const char *componentName) {
+void CodecCallback::onComponentAllocated(
+ const char *componentName, const sp<MediaCodecInfo> &codecInfo) {
sp<AMessage> notify(mNotify->dup());
notify->setInt32("what", kWhatComponentAllocated);
notify->setString("componentName", componentName);
+ if (codecInfo != nullptr) {
+ notify->setObject("codecInfo", codecInfo);
+ }
notify->post();
}
@@ -422,14 +427,12 @@
const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
uid_t uid) {
Vector<AString> matchingCodecs;
- Vector<AString> owners;
MediaCodecList::findMatchingCodecs(
mime.c_str(),
encoder,
0,
- &matchingCodecs,
- &owners);
+ &matchingCodecs);
if (err != NULL) {
*err = NAME_NOT_FOUND;
@@ -1205,6 +1208,22 @@
return OK;
}
+status_t MediaCodec::getCodecInfo(sp<MediaCodecInfo> *codecInfo) const {
+ sp<AMessage> msg = new AMessage(kWhatGetCodecInfo, this);
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ sp<RefBase> obj;
+ CHECK(response->findObject("codecInfo", &obj));
+ *codecInfo = static_cast<MediaCodecInfo *>(obj.get());
+
+ return OK;
+}
+
status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) {
reply = NULL;
@@ -1588,6 +1607,11 @@
CHECK(msg->findString("componentName", &mComponentName));
+ sp<RefBase> obj;
+ if (msg->findObject("codecInfo", &obj)) {
+ mCodecInfo = static_cast<MediaCodecInfo *>(obj.get());
+ }
+
if (mComponentName.c_str()) {
mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
}
@@ -2595,6 +2619,17 @@
break;
}
+ case kWhatGetCodecInfo:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ sp<AMessage> response = new AMessage;
+ response->setObject("codecInfo", mCodecInfo);
+ response->postReply(replyID);
+ break;
+ }
+
case kWhatSetParameters:
{
sp<AReplyToken> replyID;
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index f595646..9244886 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -307,11 +307,8 @@
//static
void MediaCodecList::findMatchingCodecs(
const char *mime, bool encoder, uint32_t flags,
- Vector<AString> *matches, Vector<AString> *owners) {
+ Vector<AString> *matches) {
matches->clear();
- if (owners != nullptr) {
- owners->clear();
- }
const sp<IMediaCodecList> list = getInstance();
if (list == nullptr) {
@@ -337,9 +334,6 @@
ALOGV("skipping SW codec '%s'", componentName.c_str());
} else {
matches->push(componentName);
- if (owners != nullptr) {
- owners->push(AString(info->getOwnerName()));
- }
ALOGV("matching '%s'", componentName.c_str());
}
}
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index af8f539..04f6ade 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -24,11 +24,12 @@
namespace android {
-bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit) {
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
int32_t width;
int32_t height;
int32_t sarWidth;
int32_t sarHeight;
+ sp<ABuffer> accessUnit = new ABuffer((void*)data, size);
sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
if (csd == nullptr) {
return false;
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f6fc813..83ca112 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NuMediaExtractor"
#include <utils/Log.h>
diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp
index 03226c7..6819bba 100644
--- a/media/libstagefright/VideoFrameScheduler.cpp
+++ b/media/libstagefright/VideoFrameScheduler.cpp
@@ -129,6 +129,11 @@
numSamplesToUse = mNumSamples;
}
+ if ((period >> kPrecision) == 0 ) {
+ ALOGW("Period is 0, or after including precision is 0 - would cause div0, returning");
+ return false;
+ }
+
int64_t sumX = 0;
int64_t sumXX = 0;
int64_t sumXY = 0;
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 0753b08..f3d95f6 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -51,6 +51,8 @@
mQueue.push_back({ nullptr, drainMode });
}
+////////////////////////////////////////////////////////////////////////////////
+
SimpleC2Component::SimpleC2Component(
const std::shared_ptr<C2ComponentInterface> &intf)
: mIntf(intf) {
@@ -91,13 +93,13 @@
}
c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
- (void) items;
+ (void)items;
return C2_OMITTED;
}
c2_status_t SimpleC2Component::flush_sm(
- flush_mode_t flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
- (void) flushThrough;
+ flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+ (void)flushMode;
{
Mutexed<ExecState>::Locked state(mExecState);
if (state->mState != RUNNING) {
@@ -107,6 +109,7 @@
{
Mutexed<WorkQueue>::Locked queue(mWorkQueue);
queue->incGeneration();
+ // TODO: queue->splicedBy(flushedWork, flushedWork->end());
while (!queue->empty()) {
std::unique_ptr<C2Work> work = queue->pop_front();
if (work) {
@@ -122,7 +125,7 @@
}
}
- return onFlush_sm();
+ return C2_OK;
}
c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
@@ -160,6 +163,10 @@
}
if (!state->mThread.joinable()) {
mExitRequested = false;
+ {
+ Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+ monitor->mExited = false;
+ }
state->mThread = std::thread(
[](std::weak_ptr<SimpleC2Component> wp) {
while (true) {
@@ -168,6 +175,8 @@
return;
}
if (thiz->exitRequested()) {
+ ALOGV("stop processing");
+ thiz->signalExit();
return;
}
thiz->processQueue();
@@ -179,7 +188,42 @@
return C2_OK;
}
+void SimpleC2Component::signalExit() {
+ Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+ monitor->mExited = true;
+ monitor->mCondition.broadcast();
+}
+
+void SimpleC2Component::requestExitAndWait(std::function<void()> job) {
+ {
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (!state->mThread.joinable()) {
+ return;
+ }
+ }
+ mExitRequested = true;
+ {
+ Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+ queue->mCondition.broadcast();
+ }
+ // TODO: timeout?
+ {
+ Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+ while (!monitor->mExited) {
+ monitor.waitForCondition(monitor->mCondition);
+ }
+ job();
+ }
+ Mutexed<ExecState>::Locked state(mExecState);
+ if (state->mThread.joinable()) {
+ ALOGV("joining the processing thread");
+ state->mThread.join();
+ ALOGV("joined the processing thread");
+ }
+}
+
c2_status_t SimpleC2Component::stop() {
+ ALOGV("stop");
{
Mutexed<ExecState>::Locked state(mExecState);
if (state->mState != RUNNING) {
@@ -195,7 +239,8 @@
Mutexed<PendingWork>::Locked pending(mPendingWork);
pending->clear();
}
- c2_status_t err = onStop();
+ c2_status_t err;
+ requestExitAndWait([this, &err]{ err = onStop(); });
if (err != C2_OK) {
return err;
}
@@ -203,6 +248,7 @@
}
c2_status_t SimpleC2Component::reset() {
+ ALOGV("reset");
{
Mutexed<ExecState>::Locked state(mExecState);
state->mState = UNINITIALIZED;
@@ -215,21 +261,13 @@
Mutexed<PendingWork>::Locked pending(mPendingWork);
pending->clear();
}
- onReset();
+ requestExitAndWait([this]{ onReset(); });
return C2_OK;
}
c2_status_t SimpleC2Component::release() {
- std::thread releasing;
- {
- Mutexed<ExecState>::Locked state(mExecState);
- releasing = std::move(state->mThread);
- }
- mExitRequested = true;
- if (releasing.joinable()) {
- releasing.join();
- }
- onRelease();
+ ALOGV("release");
+ requestExitAndWait([this]{ onRelease(); });
return C2_OK;
}
@@ -271,10 +309,14 @@
std::unique_ptr<C2Work> work;
uint64_t generation;
int32_t drainMode;
+ bool isFlushPending = false;
{
Mutexed<WorkQueue>::Locked queue(mWorkQueue);
nsecs_t deadline = systemTime() + ms2ns(250);
while (queue->empty()) {
+ if (exitRequested()) {
+ return;
+ }
nsecs_t now = systemTime();
if (now >= deadline) {
return;
@@ -287,8 +329,17 @@
generation = queue->generation();
drainMode = queue->drainMode();
+ isFlushPending = queue->popPendingFlush();
work = queue->pop_front();
}
+ if (isFlushPending) {
+ ALOGV("processing pending flush");
+ c2_status_t err = onFlush_sm();
+ if (err != C2_OK) {
+ ALOGD("flush err: %d", err);
+ // TODO: error
+ }
+ }
if (!mOutputBlockPool) {
c2_status_t err = [this] {
@@ -333,10 +384,12 @@
}
process(work, mOutputBlockPool);
+ ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
{
Mutexed<WorkQueue>::Locked queue(mWorkQueue);
if (queue->generation() != generation) {
- ALOGW("work form old generation: was %" PRIu64 " now %" PRIu64, queue->generation(), generation);
+ ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
+ queue->generation(), generation);
work->result = C2_NOT_FOUND;
queue.unlock();
{
@@ -361,9 +414,10 @@
unexpected = std::move(pending->at(frameIndex));
pending->erase(frameIndex);
}
- (void) pending->insert({ frameIndex, std::move(work) });
+ (void)pending->insert({ frameIndex, std::move(work) });
}
if (unexpected) {
+ ALOGD("unexpected pending work");
unexpected->result = C2_CORRUPTED;
Mutexed<ExecState>::Locked state(mExecState);
state->mListener->onWorkDone_nb(shared_from_this(), vec(unexpected));
diff --git a/media/libstagefright/codec2/include/SimpleC2Component.h b/media/libstagefright/codec2/include/SimpleC2Component.h
index a4b6ee1..168e79f 100644
--- a/media/libstagefright/codec2/include/SimpleC2Component.h
+++ b/media/libstagefright/codec2/include/SimpleC2Component.h
@@ -52,6 +52,7 @@
// for thread
inline bool exitRequested() { return mExitRequested; }
void processQueue();
+ void signalExit();
protected:
/**
@@ -157,16 +158,21 @@
class WorkQueue {
public:
- inline WorkQueue() : mGeneration(0ul) {}
+ inline WorkQueue() : mFlush(false), mGeneration(0ul) {}
inline uint64_t generation() const { return mGeneration; }
- inline void incGeneration() { ++mGeneration; }
+ inline void incGeneration() { ++mGeneration; mFlush = true; }
std::unique_ptr<C2Work> pop_front();
void push_back(std::unique_ptr<C2Work> work);
bool empty() const;
uint32_t drainMode() const;
void markDrain(uint32_t drainMode);
+ inline bool popPendingFlush() {
+ bool flush = mFlush;
+ mFlush = false;
+ return flush;
+ }
void clear();
Condition mCondition;
@@ -177,6 +183,7 @@
uint32_t drainMode;
};
+ bool mFlush;
uint64_t mGeneration;
std::list<Entry> mQueue;
};
@@ -185,9 +192,18 @@
typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
Mutexed<PendingWork> mPendingWork;
+ struct ExitMonitor {
+ inline ExitMonitor() : mExited(false) {}
+ Condition mCondition;
+ bool mExited;
+ };
+ Mutexed<ExitMonitor> mExitMonitor;
+
std::shared_ptr<C2BlockPool> mOutputBlockPool;
SimpleC2Component() = delete;
+
+ void requestExitAndWait(std::function<void()> job);
};
} // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 91b21c2..af2c20d 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -647,7 +647,8 @@
std::shared_ptr<_C2BlockPoolData> mPoolData;
};
-class C2_HIDE _C2MappingBlock2DImpl : public _C2Block2DImpl {
+class C2_HIDE _C2MappingBlock2DImpl
+ : public _C2Block2DImpl, public std::enable_shared_from_this<_C2MappingBlock2DImpl> {
public:
using _C2Block2DImpl::_C2Block2DImpl;
@@ -658,7 +659,7 @@
private:
friend class _C2MappingBlock2DImpl;
- Mapped(const _C2Block2DImpl *impl, bool writable, C2Fence *fence __unused)
+ Mapped(const std::shared_ptr<_C2Block2DImpl> &impl, bool writable, C2Fence *fence __unused)
: mImpl(impl), mWritable(writable) {
memset(mData, 0, sizeof(mData));
const C2Rect crop = mImpl->crop();
@@ -726,7 +727,7 @@
bool writable() const { return mWritable; }
private:
- const _C2Block2DImpl *mImpl;
+ const std::shared_ptr<_C2Block2DImpl> mImpl;
bool mWritable;
c2_status_t mError;
uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
@@ -744,7 +745,7 @@
std::lock_guard<std::mutex> lock(mMappedLock);
std::shared_ptr<Mapped> existing = mMapped.lock();
if (!existing) {
- existing = std::shared_ptr<Mapped>(new Mapped(this, writable, fence));
+ existing = std::shared_ptr<Mapped>(new Mapped(shared_from_this(), writable, fence));
mMapped = existing;
} else {
// if we mapped the region read-only, we cannot remap it read-write
diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
index c82ea45..6bd15a5 100644
--- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
+++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
@@ -305,37 +305,57 @@
ALOGV("getting %d from ringbuffer", numSamples);
std::shared_ptr<C2LinearBlock> block;
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- // TODO: error handling, proper usage, etc.
- c2_status_t err = pool->fetchLinearBlock(numSamples * sizeof(int16_t), usage, &block);
- if (err != C2_OK) {
- ALOGE("err = %d", err);
- }
+ std::function<void(const std::unique_ptr<C2Work>&)> fillWork =
+ [&block, numSamples, pool, this]()
+ -> std::function<void(const std::unique_ptr<C2Work>&)> {
+ auto fillEmptyWork = [](const std::unique_ptr<C2Work> &work, c2_status_t err) {
+ work->result = err;
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
- C2WriteView wView = block->map().get();
- // TODO
- INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(wView.data());
- int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
- if (ns != numSamples) {
- ALOGE("not a complete frame of samples available");
- mSignalledError = true;
- // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
- auto fillWork = [buffer = createLinearBuffer(block)](const std::unique_ptr<C2Work> &work) {
- work->worklets.front()->output.flags = work->input.flags;
- work->worklets.front()->output.buffers.clear();
- work->worklets.front()->output.buffers.push_back(buffer);
- work->worklets.front()->output.ordinal = work->input.ordinal;
- work->workletsProcessed = 1u;
- };
+ using namespace std::placeholders;
+ if (numSamples == 0) {
+ return std::bind(fillEmptyWork, _1, C2_OK);
+ }
+
+ // TODO: error handling, proper usage, etc.
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchLinearBlock(
+ numSamples * sizeof(int16_t), usage, &block);
+ if (err != C2_OK) {
+ ALOGD("failed to fetch a linear block (%d)", err);
+ mSignalledError = true;
+ return std::bind(fillEmptyWork, _1, C2_NO_MEMORY);
+ }
+ C2WriteView wView = block->map().get();
+ // TODO
+ INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(wView.data());
+ int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
+ if (ns != numSamples) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ return std::bind(fillEmptyWork, _1, C2_CORRUPTED);
+ }
+ return [buffer = createLinearBuffer(block)](const std::unique_ptr<C2Work> &work) {
+ work->result = C2_OK;
+ work->worklets.front()->output.flags = work->input.flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ }();
+
if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) {
fillWork(work);
} else {
finish(outInfo.frameIndex, fillWork);
}
- ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block->capacity());
+ ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block ? block->capacity() : 0);
mBuffersInfo.pop_front();
}
}
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
index 4dd0309..406d1ca 100644
--- a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
@@ -115,7 +115,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index b9ba251..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,138 +74,19 @@
.build();
}
-#if 0
-using SupportedValuesWithFields = C2SoftAvcDecIntf::SupportedValuesWithFields;
-
-struct ValidateParam {
- explicit ValidateParam(
- const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
- : mSupportedValues(supportedValues) {}
-
- template <class T, bool SIGNED = std::is_signed<T>::value, size_t SIZE = sizeof(T)>
- struct Getter {
- static T get(const C2Value::Primitive &) {
- static_assert(!std::is_arithmetic<T>::value, "non-arithmetic type");
- static_assert(!std::is_floating_point<T>::value || std::is_same<T, float>::value,
- "float is the only supported floating point type");
- static_assert(sizeof(T) <= 8, "type exceeds 64-bit");
+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;
}
- };
-
- template <class T>
- bool validateField(
- const C2FieldSupportedValues &supportedValues, const T &value) {
- switch (supportedValues.type) {
- case C2FieldSupportedValues::EMPTY:
- {
- return false;
- }
- case C2FieldSupportedValues::RANGE:
- {
- // TODO: handle step, num, denom
- return Getter<T>::get(supportedValues.range.min) <= value
- && value <= Getter<T>::get(supportedValues.range.max);
- }
- case C2FieldSupportedValues::VALUES:
- {
- for (const auto &val : supportedValues.values) {
- if (Getter<T>::get(val) == value) {
- return true;
- }
- }
- return false;
- }
- case C2FieldSupportedValues::FLAGS:
- // TODO
- return false;
- }
- return false;
+ dst -= plane.colInc * width;
+ dst += plane.rowInc;
}
-
-protected:
- const std::map<C2ParamField, SupportedValuesWithFields> &mSupportedValues;
-};
-
-template <>
-struct ValidateParam::Getter<float> {
- static float get(const C2Value::Primitive &value) { return value.fp; }
-};
-template <class T>
-struct ValidateParam::Getter<T, true, 8u> {
- static int64_t get(const C2Value::Primitive &value) { return value.i64; }
-};
-template <class T>
-struct ValidateParam::Getter<T, true, 4u> {
- static int32_t get(const C2Value::Primitive &value) { return value.i32; }
-};
-template <class T>
-struct ValidateParam::Getter<T, false, 8u> {
- static uint64_t get(const C2Value::Primitive &value) { return value.u64; }
-};
-template <class T>
-struct ValidateParam::Getter<T, false, 4u> {
- static uint32_t get(const C2Value::Primitive &value) { return value.u32; }
-};
-
-template <class T>
-struct ValidateSimpleParam : public ValidateParam {
- explicit ValidateSimpleParam(
- const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
- : ValidateParam(supportedValues) {}
-
- std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
- T* param = (T*)c2param;
- C2ParamField field(param, &T::value);
- const C2FieldSupportedValues &supportedValues = mSupportedValues.at(field).supported;
- if (!validateField(supportedValues, param->value)) {
- return std::unique_ptr<C2SettingResult>(
- new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
- }
- return nullptr;
- }
-};
-
-template <class T>
-struct ValidateVideoSize : public ValidateParam {
- explicit ValidateVideoSize(
- const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
- : ValidateParam(supportedValues) {}
-
- std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
- T* param = (T*)c2param;
- C2ParamField field(param, &T::width);
- const C2FieldSupportedValues &supportedWidth = mSupportedValues.at(field).supported;
- if (!validateField(supportedWidth, param->width)) {
- return std::unique_ptr<C2SettingResult>(
- new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
- }
- field = C2ParamField(param, &T::height);
- const C2FieldSupportedValues &supportedHeight = mSupportedValues.at(field).supported;
- if (!validateField(supportedHeight, param->height)) {
- return std::unique_ptr<C2SettingResult>(
- new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
- }
- return nullptr;
- }
-};
-
-template <class T>
-struct ValidateCString {
- explicit ValidateCString(const char *expected) : mExpected(expected) {}
-
- std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
- T* param = (T*)c2param;
- if (strncmp(param->m.value, mExpected, param->flexCount()) != 0) {
- return std::unique_ptr<C2SettingResult>(
- new C2SettingResult {C2SettingResult::BAD_VALUE, {C2ParamField(param, &T::m), nullptr}, {}});
- }
- return nullptr;
- }
-
-private:
- const char *mExpected;
-};
-#endif
+}
void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
uint32_t flags = 0;
@@ -229,398 +101,6 @@
} // namespace
-#if 0
-#define CASE(member) \
- case decltype(component->member)::CORE_INDEX: \
- return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor( \
- static_cast<decltype(component->member) *>(nullptr)))
-
-class C2SoftAvcDecIntf::ParamReflector : public C2ParamReflector {
-public:
- virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) override {
- constexpr C2SoftAvcDecIntf *component = nullptr;
- switch (coreIndex.coreIndex()) {
- CASE(mDomainInfo);
- CASE(mInputStreamCount);
- CASE(mInputStreamFormat);
- // Output counterparts for the above would be redundant.
- CASE(mVideoSize);
- CASE(mMaxVideoSizeHint);
-
- // port mime configs are stored as unique_ptr.
- case C2PortMimeConfig::CORE_INDEX:
- return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor(
- static_cast<C2PortMimeConfig *>(nullptr)));
- }
- return nullptr;
- }
-};
-#undef CASE
-
-// static const CodecProfileLevel kProfileLevels[] = {
-// { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
-// { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 },
-// { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 },
-// };
-C2SoftAvcDecIntf::C2SoftAvcDecIntf(const char *name, c2_node_id_t id)
- : mName(name),
- mId(id),
- mDomainInfo(C2DomainVideo),
- mInputStreamCount(1u),
- mOutputStreamCount(1u),
- mInputStreamFormat(0u, C2FormatCompressed),
- mOutputStreamFormat(0u, C2FormatVideo),
- mProfile(0u, kAvcProfileUnknown),
- mLevel(0u, kAvcLevelUnknown),
- mBlockSize(0u),
- mAlignment(0u),
- mFrameRate(0u, 0),
- mBlocksPerSecond(0u, 0),
- mParamReflector(new ParamReflector) {
- ALOGV("in %s", __func__);
- mInputPortMime = C2PortMimeConfig::input::AllocUnique(strlen(CODEC_MIME_TYPE) + 1);
- strcpy(mInputPortMime->m.value, CODEC_MIME_TYPE);
- mOutputPortMime = C2PortMimeConfig::output::AllocUnique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1);
- strcpy(mOutputPortMime->m.value, MEDIA_MIMETYPE_VIDEO_RAW);
-
- mVideoSize.width = 320;
- mVideoSize.height = 240;
- mBlockSize.width = 16;
- mBlockSize.height = 16;
- mAlignment.width = 2;
- mAlignment.height = 2;
-
- mMaxVideoSizeHint.width = H264_MAX_FRAME_WIDTH;
- mMaxVideoSizeHint.height = H264_MAX_FRAME_HEIGHT;
-
- mOutputBlockPools = C2PortBlockPoolsTuning::output::AllocUnique({});
-
- auto insertParam = [¶ms = mParams] (C2Param *param) {
- params[param->index()] = param;
- };
-
- auto markReadOnly = [&supported = mSupportedValues] (auto *param) {
- supported.emplace(
- C2ParamField(param, &std::remove_pointer<decltype(param)>::type::value),
- C2FieldSupportedValues(false /* flags */, {}));
- };
-
- auto markReadOnlyVideoSize = [&supported = mSupportedValues] (auto *param) {
- supported.emplace(
- C2ParamField(param, &std::remove_pointer<decltype(param)>::type::width),
- C2FieldSupportedValues(false /* flags */, {}));
- supported.emplace(
- C2ParamField(param, &std::remove_pointer<decltype(param)>::type::height),
- C2FieldSupportedValues(false /* flags */, {}));
- };
-
- insertParam(&mDomainInfo);
- markReadOnly(&mDomainInfo);
- mFieldVerifiers[mDomainInfo.index()] =
- ValidateSimpleParam<decltype(mDomainInfo)>(mSupportedValues);
-
- insertParam(mInputPortMime.get());
- mFieldVerifiers[mInputPortMime->index()] =
- ValidateCString<std::remove_reference<decltype(*mInputPortMime)>::type>(CODEC_MIME_TYPE);
-
- insertParam(&mInputStreamCount);
- markReadOnly(&mInputStreamCount);
- mFieldVerifiers[mInputStreamCount.index()] =
- ValidateSimpleParam<decltype(mInputStreamCount)>(mSupportedValues);
-
- insertParam(mOutputPortMime.get());
- mFieldVerifiers[mOutputPortMime->index()] =
- ValidateCString<std::remove_reference<decltype(*mOutputPortMime)>::type>(MEDIA_MIMETYPE_VIDEO_RAW);
-
- insertParam(&mOutputStreamCount);
- markReadOnly(&mOutputStreamCount);
- mFieldVerifiers[mOutputStreamCount.index()] =
- ValidateSimpleParam<decltype(mOutputStreamCount)>(mSupportedValues);
-
- insertParam(&mInputStreamFormat);
- markReadOnly(&mInputStreamFormat);
- mFieldVerifiers[mInputStreamFormat.index()] =
- ValidateSimpleParam<decltype(mInputStreamFormat)>(mSupportedValues);
-
- insertParam(&mOutputStreamFormat);
- markReadOnly(&mOutputStreamFormat);
- mFieldVerifiers[mOutputStreamFormat.index()] =
- ValidateSimpleParam<decltype(mOutputStreamFormat)>(mSupportedValues);
-
- insertParam(&mVideoSize);
- markReadOnlyVideoSize(&mVideoSize);
- mFieldVerifiers[mVideoSize.index()] =
- ValidateVideoSize<decltype(mVideoSize)>(mSupportedValues);
-
- insertParam(&mMaxVideoSizeHint);
- mSupportedValues.emplace(
- C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width),
- C2FieldSupportedValues(H264_MIN_FRAME_WIDTH, H264_MAX_FRAME_WIDTH, mAlignment.width));
- mSupportedValues.emplace(
- C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height),
- C2FieldSupportedValues(H264_MIN_FRAME_HEIGHT, H264_MAX_FRAME_HEIGHT, mAlignment.height));
- mFieldVerifiers[mMaxVideoSizeHint.index()] =
- ValidateVideoSize<decltype(mMaxVideoSizeHint)>(mSupportedValues);
-
- insertParam(&mProfile);
- mSupportedValues.emplace(
- C2ParamField(&mProfile, &C2AvcProfileInfo::value),
- C2FieldSupportedValues(false /* flags */, {
- kAvcProfileUnknown,
- kAvcProfileBaseline,
- kAvcProfileMain,
- kAvcProfileHigh,
- }));
- mFieldVerifiers[mProfile.index()] =
- ValidateSimpleParam<decltype(mProfile)>(mSupportedValues);
-
- insertParam(&mLevel);
- mSupportedValues.emplace(
- C2ParamField(&mLevel, &C2AvcLevelInfo::value),
- C2FieldSupportedValues(false /* flags */, {
- kAvcLevelUnknown,
- kAvcLevel10,
- kAvcLevel1b,
- kAvcLevel11,
- kAvcLevel12,
- kAvcLevel13,
- kAvcLevel20,
- kAvcLevel21,
- kAvcLevel22,
- kAvcLevel30,
- kAvcLevel31,
- kAvcLevel32,
- kAvcLevel40,
- kAvcLevel41,
- kAvcLevel42,
- kAvcLevel50,
- kAvcLevel51,
- kAvcLevel52,
- }));
- mFieldVerifiers[mLevel.index()] =
- ValidateSimpleParam<decltype(mLevel)>(mSupportedValues);
-
- insertParam(&mBlockSize);
- markReadOnlyVideoSize(&mBlockSize);
- mFieldVerifiers[mBlockSize.index()] =
- ValidateVideoSize<decltype(mBlockSize)>(mSupportedValues);
-
- insertParam(&mAlignment);
- markReadOnlyVideoSize(&mAlignment);
- mFieldVerifiers[mAlignment.index()] =
- ValidateVideoSize<decltype(mAlignment)>(mSupportedValues);
-
- insertParam(&mFrameRate);
- mSupportedValues.emplace(
- C2ParamField(&mFrameRate, &C2FrameRateInfo::value),
- C2FieldSupportedValues(0, 240));
- mFieldVerifiers[mFrameRate.index()] =
- ValidateSimpleParam<decltype(mFrameRate)>(mSupportedValues);
-
- insertParam(&mBlocksPerSecond);
- mSupportedValues.emplace(
- C2ParamField(&mFrameRate, &C2BlocksPerSecondInfo::value),
- C2FieldSupportedValues(0, 244800));
- mFieldVerifiers[mBlocksPerSecond.index()] =
- ValidateSimpleParam<decltype(mBlocksPerSecond)>(mSupportedValues);
-
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_domain", &mDomainInfo));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_input_port_mime", mInputPortMime.get()));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_input_stream_count", &mInputStreamCount));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_output_port_mime", mOutputPortMime.get()));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_output_stream_count", &mOutputStreamCount));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_input_stream_format", &mInputStreamFormat));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- true, "_output_stream_format", &mOutputStreamFormat));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- false, "_video_size", &mVideoSize));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- false, "_max_video_size_hint", &mMaxVideoSizeHint));
- mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
- false, "_output_block_pools", mOutputBlockPools.get()));
-}
-
-C2SoftAvcDecIntf::~C2SoftAvcDecIntf() {
- ALOGV("in %s", __func__);
-}
-
-C2String C2SoftAvcDecIntf::getName() const {
- return mName;
-}
-
-c2_node_id_t C2SoftAvcDecIntf::getId() const {
- return mId;
-}
-
-c2_status_t C2SoftAvcDecIntf::query_vb(
- const std::vector<C2Param*> & stackParams,
- const std::vector<C2Param::Index> & heapParamIndices,
- c2_blocking_t mayBlock,
- std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
- (void)mayBlock;
- for (C2Param* const param : stackParams) {
- if (!*param) {
- continue;
- }
-
- uint32_t index = param->index();
- if (!mParams.count(index)) {
- // TODO: add support for output-block-pools (this will be done when we move all
- // config to shared ptr)
- continue;
- }
-
- C2Param *myParam = mParams.find(index)->second;
- if (myParam->size() != param->size()) {
- param->invalidate();
- continue;
- }
-
- param->updateFrom(*myParam);
- }
-
- for (const C2Param::Index index : heapParamIndices) {
- if (mParams.count(index)) {
- C2Param *myParam = mParams.find(index)->second;
- heapParams->emplace_back(C2Param::Copy(*myParam));
- }
- }
-
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDecIntf::config_vb(
- const std::vector<C2Param*> ¶ms,
- c2_blocking_t mayBlock,
- std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
- (void)mayBlock;
- c2_status_t err = C2_OK;
- for (C2Param *param : params) {
- uint32_t index = param->index();
- if (param->index() == mOutputBlockPools.get()->index()) {
- // setting output block pools
- mOutputBlockPools.reset(
- (C2PortBlockPoolsTuning::output *)C2Param::Copy(*param).release());
- continue;
- }
-
- if (mParams.count(index) == 0) {
- // We can't create C2SettingResult with no field, so just skipping in this case.
- err = C2_BAD_INDEX;
- continue;
- }
- C2Param *myParam = mParams.find(index)->second;
- std::unique_ptr<C2SettingResult> result;
- if (!(result = mFieldVerifiers[index](param))) {
- myParam->updateFrom(*param);
- updateSupportedValues();
- } else {
- failures->push_back(std::move(result));
- err = C2_BAD_VALUE;
- }
- }
- return err;
-}
-
-c2_status_t C2SoftAvcDecIntf::createTunnel_sm(c2_node_id_t targetComponent) {
- // Tunneling is not supported
- (void) targetComponent;
- return C2_OMITTED;
-}
-
-c2_status_t C2SoftAvcDecIntf::releaseTunnel_sm(c2_node_id_t targetComponent) {
- // Tunneling is not supported
- (void) targetComponent;
- return C2_OMITTED;
-}
-
-std::shared_ptr<C2ParamReflector> C2SoftAvcDecIntf::getParamReflector() const {
- return mParamReflector;
-}
-
-c2_status_t C2SoftAvcDecIntf::querySupportedParams_nb(
- std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const {
- params->insert(params->begin(), mParamDescs.begin(), mParamDescs.end());
- return C2_OK;
-}
-
-c2_status_t C2SoftAvcDecIntf::querySupportedValues_vb(
- std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock) const {
- (void)mayBlock;
- c2_status_t res = C2_OK;
- for (C2FieldSupportedValuesQuery &query : fields) {
- if (mSupportedValues.count(query.field) == 0) {
- query.status = C2_BAD_INDEX;
- res = C2_BAD_INDEX;
- } else {
- query.status = C2_OK;
- query.values = mSupportedValues.at(query.field).supported;
- }
- }
- return res;
-}
-
-void C2SoftAvcDecIntf::updateSupportedValues() {
- int32_t maxWidth = H264_MAX_FRAME_WIDTH;
- int32_t maxHeight = H264_MAX_FRAME_HEIGHT;
- // cf: Rec. ITU-T H.264 A.3
- int maxFrameRate = 172;
- std::vector<C2ParamField> fields;
- if (mLevel.value != kAvcLevelUnknown) {
- // cf: Rec. ITU-T H.264 Table A-1
- constexpr int MaxFS[] = {
- // 0 1 2 3 4 5 6 7 8 9
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 99,
- 99, 396, 396, 396, 0, 0, 0, 0, 0, 0,
- 396, 792, 1620, 0, 0, 0, 0, 0, 0, 0,
- 1620, 3600, 5120, 0, 0, 0, 0, 0, 0, 0,
- 8192, 8192, 8704, 0, 0, 0, 0, 0, 0, 0,
- 22080, 36864, 36864,
- };
- constexpr int MaxMBPS[] = {
- // 0 1 2 3 4 5 6 7 8 9
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1485,
- 1485, 3000, 6000, 11880, 0, 0, 0, 0, 0, 0,
- 11880, 19800, 20250, 0, 0, 0, 0, 0, 0, 0,
- 40500, 108000, 216000, 0, 0, 0, 0, 0, 0, 0,
- 245760, 245760, 522240, 0, 0, 0, 0, 0, 0, 0,
- 589824, 983040, 2073600,
- };
-
- // cf: Rec. ITU-T H.264 A.3.1
- maxWidth = std::min(maxWidth, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE);
- maxHeight = std::min(maxHeight, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE);
- int32_t MBs = ((mVideoSize.width + 15) / 16) * ((mVideoSize.height + 15) / 16);
- maxFrameRate = std::min(maxFrameRate, MaxMBPS[mLevel.value] / MBs);
- fields.push_back(C2ParamField(&mLevel, &C2AvcLevelInfo::value));
- }
-
- SupportedValuesWithFields &maxWidthVals = mSupportedValues.at(
- C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width));
- maxWidthVals.supported.range.max = maxWidth;
- maxWidthVals.restrictingFields.clear();
- maxWidthVals.restrictingFields.insert(fields.begin(), fields.end());
-
- SupportedValuesWithFields &maxHeightVals = mSupportedValues.at(
- C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height));
- maxHeightVals.supported.range.max = maxHeight;
- maxHeightVals.restrictingFields.clear();
- maxHeightVals.restrictingFields.insert(fields.begin(), fields.end());
-
- SupportedValuesWithFields &frameRate = mSupportedValues.at(
- C2ParamField(&mFrameRate, &C2FrameRateInfo::value));
- frameRate.supported.range.max = maxFrameRate;
- frameRate.restrictingFields.clear();
- frameRate.restrictingFields.insert(fields.begin(), fields.end());
-}
-#endif
-
///////////////////////////////////////////////////////////////////////////////
C2SoftAvcDec::C2SoftAvcDec(
@@ -628,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),
@@ -654,6 +134,7 @@
}
c2_status_t C2SoftAvcDec::onStop() {
+ ALOGV("onStop");
mSignalledError = false;
resetDecoder();
resetPlugin();
@@ -662,6 +143,7 @@
}
void C2SoftAvcDec::onReset() {
+ ALOGV("onReset");
(void)onStop();
}
@@ -672,17 +154,6 @@
c2_status_t C2SoftAvcDec::onFlush_sm() {
setFlushMode();
- /* 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;
- }
-
while (true) {
ivd_video_decode_ip_t s_dec_ip;
ivd_video_decode_op_t s_dec_op;
@@ -698,9 +169,9 @@
}
}
- if (mFlushOutBuffer) {
- free(mFlushOutBuffer);
- mFlushOutBuffer = NULL;
+ if (mOutBuffer) {
+ free(mOutBuffer);
+ mOutBuffer = NULL;
}
return C2_OK;
}
@@ -778,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;
}
@@ -854,6 +335,7 @@
s_video_flush_op.u4_error_code);
return UNKNOWN_ERROR;
}
+
return OK;
}
@@ -993,6 +475,7 @@
ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+ ps_dec_op->u4_output_present = 0;
ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
@@ -1020,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;
@@ -1065,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) {
@@ -1093,9 +592,9 @@
work->result = C2_OK;
work->workletsProcessed = 0u;
- const C2ConstLinearBlock &buffer =
+ const C2ConstLinearBlock buffer =
work->input.buffers[0]->data().linearBlocks().front();
- if (buffer.capacity() == 0) {
+ if (buffer.size() == 0) {
ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull());
// TODO: result?
fillEmptyWork(work);
@@ -1109,6 +608,13 @@
}
C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
+ if (input.error() != C2_OK) {
+ work->result = input.error();
+ fillEmptyWork(work);
+ ALOGD("map error: %d", input.error());
+ return;
+ }
+ ALOGV("buffer.size() = %u, input.capacity() = %u", buffer.size(), input.capacity());
uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
size_t inOffset = 0u;
@@ -1117,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/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
index 8fb8122..d75e4c1 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -909,6 +909,9 @@
}
ALOGV("width = %d, height = %d", input->width(), input->height());
+ if (mWidth != input->width() || mHeight != input->height()) {
+ return C2_BAD_VALUE;
+ }
const C2PlanarLayout &layout = input->layout();
uint8_t *yPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
uint8_t *uPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 5558bcf..295a5b0 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -248,7 +248,7 @@
ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
if (output) {
- const C2ConstGraphicBlock &block = output->data().graphicBlocks().front();
+ const C2ConstGraphicBlock block = output->data().graphicBlocks().front();
native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(block.handle());
sp<GraphicBuffer> buffer(new GraphicBuffer(
grallocHandle,
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
index ce40d6b..87138af 100644
--- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
@@ -48,6 +48,7 @@
}
C2SoftFlacDecoder::~C2SoftFlacDecoder() {
+ delete mFLACDecoder;
}
c2_status_t C2SoftFlacDecoder::onInit() {
@@ -77,6 +78,9 @@
}
status_t C2SoftFlacDecoder::initDecoder() {
+ if (mFLACDecoder) {
+ delete mFLACDecoder;
+ }
mFLACDecoder = FLACDecoder::Create();
if (!mFLACDecoder) {
ALOGE("initDecoder: failed to create FLACDecoder");
@@ -111,7 +115,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
index a5c01a9..43d913b 100644
--- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
@@ -46,7 +46,7 @@
kMaxBlockSize = 4096
};
- sp<FLACDecoder> mFLACDecoder;
+ FLACDecoder *mFLACDecoder;
FLAC__StreamMetadata_StreamInfo mStreamInfo;
bool mSignalledError;
bool mSignalledOutputEos;
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
index 4ab1ab2..d0b72b7 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -57,6 +57,7 @@
SoftFlacDecoder::~SoftFlacDecoder() {
ALOGV("dtor:");
+ delete mFLACDecoder;
}
void SoftFlacDecoder::initPorts() {
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
index 4a21c34..0f17ed8 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
@@ -50,7 +50,7 @@
kNumOutputBuffers = 4,
};
- sp<FLACDecoder> mFLACDecoder;
+ FLACDecoder *mFLACDecoder;
FLAC__StreamMetadata_StreamInfo mStreamInfo;
bool mHasStreamInfo;
size_t mInputBufferCount;
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
index 6c147ad..fa93fe5 100644
--- a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
@@ -140,7 +140,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
index d296a3d..1049420 100644
--- a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
@@ -92,8 +92,8 @@
return;
}
- const C2ConstLinearBlock &inBuffer =
- work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer =
+ work->input.buffers[0]->data().linearBlocks().front();
C2ReadView rView = inBuffer.map().get();
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
index 2a3239f..641c342 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
@@ -304,7 +304,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
index 9cac87e..0a8891e 100644
--- a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
@@ -288,7 +288,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index d443b7c..bc5fd79 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -184,7 +184,7 @@
info->emphasis = (temp << 30) >> 30; /* 2 */
- if (!info->bitrate_index || info->sampling_frequency == 3)
+ if (!info->bitrate_index || info->bitrate_index == 15 || info->sampling_frequency == 3)
{
err = UNSUPPORTED_FREE_BITRATE;
}
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
index 0ebe7d6..74ea340 100644
--- a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
@@ -614,7 +614,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
index 96b303c..8528f26 100644
--- a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
@@ -209,7 +209,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
index 4eec362..47fb6de 100644
--- a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
@@ -251,7 +251,7 @@
return;
}
- const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+ const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
size_t inOffset = inBuffer.offset();
size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp
index 8c7137c..e0e9211 100644
--- a/media/libstagefright/flac/dec/FLACDecoder.cpp
+++ b/media/libstagefright/flac/dec/FLACDecoder.cpp
@@ -220,9 +220,10 @@
}
// static
-sp<FLACDecoder> FLACDecoder::Create() {
- sp<FLACDecoder> decoder = new FLACDecoder();
- if (decoder->init() != OK) {
+FLACDecoder *FLACDecoder::Create() {
+ FLACDecoder *decoder = new (std::nothrow) FLACDecoder();
+ if (decoder == NULL || decoder->init() != OK) {
+ delete decoder;
return NULL;
}
return decoder;
diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h
index 36282a8..1a33cae 100644
--- a/media/libstagefright/flac/dec/FLACDecoder.h
+++ b/media/libstagefright/flac/dec/FLACDecoder.h
@@ -26,14 +26,14 @@
namespace android {
// packet based FLAC decoder, wrapps libFLAC stream decoder.
-class FLACDecoder : public RefBase {
+class FLACDecoder {
public:
enum {
kMaxChannels = 8,
};
- static sp<FLACDecoder> Create();
+ static FLACDecoder *Create();
FLAC__StreamMetadata_StreamInfo getStreamInfo() const {
return mStreamInfo;
@@ -43,10 +43,10 @@
status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
short *outBuffer, size_t *outBufferLen);
void flush();
+ virtual ~FLACDecoder();
protected:
FLACDecoder();
- virtual ~FLACDecoder() override;
private:
// stream properties
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/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index fa22003..1a5304b 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -253,6 +253,7 @@
sp<AMessage> mLastOutputFormat;
bool mIsVideo;
+ bool mIsImage;
bool mIsEncoder;
bool mFatalError;
bool mShutdownInProgress;
@@ -489,11 +490,12 @@
status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
status_t setupH263EncoderParameters(const sp<AMessage> &msg);
status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
- status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
+ status_t setupHEVCEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
status_t setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
+ status_t configureImageGrid(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
status_t configureBitrate(
OMX_VIDEO_CONTROLRATETYPE bitrateMode, int32_t bitrate, int32_t quality = 0);
void configureEncoderLatency(const sp<AMessage> &msg);
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/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 268662f..9606fd9 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -95,8 +95,11 @@
*
* @param componentName the unique name of the component specified in
* MediaCodecList.
+ * @param codecInfo the associated codec info if available.
*/
- virtual void onComponentAllocated(const char *componentName) = 0;
+ virtual void onComponentAllocated(
+ const char *componentName,
+ const sp<MediaCodecInfo> &codecInfo = nullptr) = 0;
/**
* Notify MediaCodec that the underlying component is configured.
*
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index e7faea5..ef8de1f 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -184,6 +184,8 @@
status_t getName(AString *componentName) const;
+ status_t getCodecInfo(sp<MediaCodecInfo> *codecInfo) const;
+
status_t getMetrics(MediaAnalyticsItem * &reply);
status_t setParameters(const sp<AMessage> ¶ms);
@@ -248,6 +250,7 @@
kWhatRequestIDRFrame = 'ridr',
kWhatRequestActivityNotification = 'racN',
kWhatGetName = 'getN',
+ kWhatGetCodecInfo = 'gCoI',
kWhatSetParameters = 'setP',
kWhatSetCallback = 'setC',
kWhatSetNotification = 'setN',
@@ -308,6 +311,7 @@
sp<ALooper> mCodecLooper;
sp<CodecBase> mCodec;
AString mComponentName;
+ sp<MediaCodecInfo> mCodecInfo;
sp<AReplyToken> mReplyID;
uint32_t mFlags;
status_t mStickyError;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index d46fe85..bb4da09 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -74,8 +74,7 @@
const char *mime,
bool createEncoder,
uint32_t flags,
- Vector<AString> *matchingCodecs,
- Vector<AString> *owners = nullptr);
+ Vector<AString> *matchingCodecs);
static bool isSoftwareCodec(const AString &componentName);
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
index 3af2218..d5a8080 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -23,7 +23,7 @@
namespace android {
struct ABuffer;
-bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit);
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 0b7bd26..0fa9fcb 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -634,7 +634,7 @@
if (mFormat == NULL) {
mFormat = new MetaData;
- if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+ if (!MakeAVCCodecSpecificData(*mFormat, accessUnit->data(), accessUnit->size())) {
mFormat.clear();
}
}
@@ -1010,7 +1010,7 @@
}
if (mFormat == NULL) {
mFormat = new MetaData;
- if (!MakeAVCCodecSpecificData(*mFormat, mBuffer)) {
+ if (!MakeAVCCodecSpecificData(*mFormat, mBuffer->data(), mBuffer->size())) {
ALOGW("Creating dummy AVC format for scrambled content");
mFormat = new MetaData;
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -1172,7 +1172,9 @@
if (mFormat == NULL) {
mFormat = new MetaData;
- if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+ if (!MakeAVCCodecSpecificData(*mFormat,
+ accessUnit->data(),
+ accessUnit->size())) {
mFormat.clear();
}
}
@@ -1493,7 +1495,9 @@
const uint8_t *data, size_t size) {
static const char kStartCode[] = "\x00\x00\x01";
- if (size < 3) {
+ // per ISO/IEC 14496-2 6.2.1, a chunk has a 3-byte prefix + 1-byte start code
+ // we need at least <prefix><start><next prefix> to successfully scan
+ if (size < 3 + 1 + 3) {
return -EAGAIN;
}
@@ -1501,7 +1505,7 @@
return -EAGAIN;
}
- size_t offset = 3;
+ size_t offset = 4;
while (offset + 2 < size) {
if (!memcmp(&data[offset], kStartCode, 3)) {
return offset;
@@ -1552,6 +1556,9 @@
state = EXPECT_VISUAL_OBJECT_START;
} else {
discard = true;
+ offset += chunkSize;
+ ALOGW("b/74114680, advance to next chunk");
+ android_errorWriteLog(0x534e4554, "74114680");
}
break;
}
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index f597e02..f7b569d 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -163,6 +163,8 @@
"audio_decoder.ac3", "audio_encoder.ac3" },
{ MEDIA_MIMETYPE_AUDIO_EAC3,
"audio_decoder.eac3", "audio_encoder.eac3" },
+ { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
+ "image_decoder.heic", "image_encoder.heic" },
};
static const size_t kNumMimeToRole =
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 22eea25..19e4b24 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -32,6 +32,7 @@
# TODO: Use System SDK once public APIs are approved
# LOCAL_SDK_VERSION := system_current
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index aafe8b0..8488c84 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -104,7 +104,7 @@
<view class="com.android.support.mediarouter.app.MediaRouteButton"
android:id="@+id/cast"
android:layout_centerVertical="true"
- android:visibility="visible"
+ android:visibility="gone"
style="@style/TitleBarButton" />
</LinearLayout>
diff --git a/packages/MediaComponents/runcts.sh b/packages/MediaComponents/runcts.sh
new file mode 100644
index 0000000..0cf0e44
--- /dev/null
+++ b/packages/MediaComponents/runcts.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+# 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.
+
+# Usage '. runcts.sh'
+
+function _runtest_cts_mediacomponent_usage() {
+ echo 'runtest-cts-MediaComponents [option]: Build, flash device,'
+ echo ' and run subset of CtsMediaTestCases that MediaComponents covers.'
+ echo ' *Warning* This bypasses CTS setup (e.g. download media contents from server)'
+ echo ' For running CTS in official way, use atest or cts-tradefed '
+ echo ' -h|--help: This help'
+ echo ' --skip: Skip build and flash. Just rerun-tests'
+ echo ' --min: Only rebuild tests and updatable library.'
+ echo ' -s [device_id]: Specify a device name to run test against.'
+ echo ' You can define ${ADBHOST} instead.'
+ echo ' -r [count]: Repeat tests for given count. It will stop when fails.'
+ echo ' --ignore: Keep repeating tests even when it fails.'
+ echo ' -t [test]: Only run the specific test. Can be either a class or a method.'
+}
+
+function runtest-cts-MediaComponents() {
+ # Edit here if you want to support other tests.
+ # List up libs and apks in the media_api needed for tests, and place test target at the last.
+ local TEST_PACKAGE_DIR=("frameworks/av/packages/MediaComponents/test")
+ local TEST_PACKAGE=("android.media.cts")
+ local BUILD_TARGETS=("MediaComponents" "CtsMediaTestCases")
+ # Don't include MediaComponents -- if we simply install it, system server
+ # wouldn't use the installed one.
+ local INSTALL_TARGETS=("CtsMediaTestCases")
+ local TEST_RUNNER="android.support.test.runner.AndroidJUnitRunner"
+ local DEPENDENCIES=("mockito-target-minus-junit4" "android-support-test" "compatibility-device-util")
+ local DEFAULT_TEST_TARGET=""
+ DEFAULT_TEST_TARGET+="android.media.cts.MediaBrowser2Test"
+ DEFAULT_TEST_TARGET+=",android.media.cts.MediaController2Test"
+ DEFAULT_TEST_TARGET+=",android.media.cts.MediaMetadata2Test"
+ DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2Test"
+ DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2_PermissionTest"
+ DEFAULT_TEST_TARGET+=",android.media.cts.MediaSessionManager_MediaSession2Test"
+ DEFAULT_TEST_TARGET+=",android.media.cts.SessionToken2Test"
+ if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+ echo "Needs to lunch a target first"
+ return
+ fi
+
+ local old_path=${OLDPWD}
+ while true; do
+ local OPTION_SKIP="false"
+ local OPTION_MIN="false"
+ local OPTION_REPEAT_COUNT="1"
+ local OPTION_IGNORE="false"
+ local OPTION_TEST_TARGET="${DEFAULT_TEST_TARGET}"
+ local adbhost_local
+ while (( "$#" )); do
+ case "${1}" in
+ -h|--help)
+ _runtest_cts_mediacomponent_usage
+ return
+ ;;
+ --skip)
+ OPTION_SKIP="true"
+ ;;
+ --min)
+ OPTION_MIN="true"
+ ;;
+ -s)
+ shift
+ adbhost_local=${1}
+ ;;
+ -r)
+ shift
+ OPTION_REPEAT_COUNT="${1}"
+ ;;
+ --ignore)
+ OPTION_IGNORE="true"
+ ;;
+ -t)
+ shift
+ OPTION_TEST_TARGET="${1}"
+ esac
+ shift
+ done
+
+ # Build adb command.
+ local adb
+ if [[ -z "${adbhost_local}" ]]; then
+ adbhost_local=${ADBHOST}
+ fi
+ if [[ -z "${adbhost_local}" ]]; then
+ local device_count=$(adb devices | sed '/^[[:space:]]*$/d' | wc -l)
+ if [[ "${device_count}" != "2" ]]; then
+ echo "Too many devices. Specify a device." && break
+ fi
+ adb="adb"
+ else
+ adb="adb -s ${adbhost_local}"
+ fi
+
+ local target_dir="${ANDROID_BUILD_TOP}/${TEST_PACKAGE_DIR}"
+ #local TEST_PACKAGE=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\.]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
+
+ if [[ "${OPTION_SKIP}" != "true" ]]; then
+ # Build dependencies if needed.
+ local dependency
+ local build_dependency=""
+ for dependency in ${DEPENDENCIES[@]}; do
+ if [[ "${dependency}" == "out/"* ]]; then
+ if [[ ! -f ${ANDROID_BUILD_TOP}/${dependency} ]]; then
+ build_dependency="true"
+ break
+ fi
+ else
+ if [[ "$(find ${OUT} -name ${dependency}_intermediates | wc -l)" == "0" ]]; then
+ build_dependency="true"
+ break
+ fi
+ fi
+ done
+ if [[ "${build_dependency}" == "true" ]]; then
+ echo "Building dependencies. Will only print stderr."
+ m ${DEPENDENCIES[@]} -j > /dev/null
+ fi
+
+ # Build test apk and required apk.
+ local build_targets="${BUILD_TARGETS[@]}"
+ if [[ "${OPTION_MIN}" != "true" ]]; then
+ build_targets="${build_targets} droid"
+ fi
+ m ${build_targets} -j || break
+
+ local device_build_type="$(${adb} shell getprop ro.build.type)"
+ if [[ "${device_build_type}" == "user" ]]; then
+ # User build. Cannot adb sync
+ ${adb} reboot bootloader
+ fastboot flashall
+ else
+ ${adb} root
+ local device_verity_mode="$(${adb} shell getprop ro.boot.veritymode)"
+ if [[ "${device_verity_mode}" != "disabled" ]]; then
+ ${adb} disable-verity
+ ${adb} reboot
+ ${adb} wait-for-device || break
+ ${adb} root
+ fi
+ ${adb} remount
+ ${adb} shell stop
+ ${adb} shell setprop log.tag.MediaSessionService DEBUG
+ ${adb} sync
+ ${adb} shell start
+ fi
+ ${adb} wait-for-device || break
+ # Ensure package manager is loaded.
+ # TODO(jaewan): Find better way to wait
+ sleep 15
+
+ # Install apks
+ local install_failed="false"
+ for target in ${INSTALL_TARGETS[@]}; do
+ local apk_path=$(find ${OUT}/system ${OUT}/data -name ${target}.apk)
+ local apk_num=$(find ${OUT}/system ${OUT}/data -name ${target}.apk | wc -l)
+ if [[ "${apk_num}" != "1" ]]; then
+ echo "Cannot locate a ${target}.apk. Found ${apk_num} apks" && break
+ fi
+ echo "Installing ${target}.apk. path=${apk_path}"
+ ${adb} install -r ${apk_path}
+ if [[ "${?}" != "0" ]]; then
+ install_failed="true"
+ break
+ fi
+ done
+ if [[ "${install_failed}" == "true" ]]; then
+ echo "Failed to install. Test wouldn't run."
+ break
+ fi
+ fi
+
+ local test_target=""
+ if [[ -n "${OPTION_TEST_TARGET}" ]]; then
+ test_target="-e class ${OPTION_TEST_TARGET}"
+ fi
+
+ local i
+ local tmpfile=$(tempfile)
+ for ((i=1; i <= ${OPTION_REPEAT_COUNT}; i++)); do
+ echo "Run test ${i}/${OPTION_REPEAT_COUNT}"
+ ${adb} shell am instrument ${test_target} -w ${TEST_PACKAGE}/${TEST_RUNNER} >& ${tmpfile}
+ cat ${tmpfile}
+ if [[ "${OPTION_IGNORE}" != "true" ]]; then
+ if [[ -n "$(grep ${tmpfile} -e 'FAILURE\|crashed')" ]]; then
+ # am instrument doesn't return error code so need to grep result message instead
+ break
+ fi
+ fi
+ done
+ rm ${tmpfile}
+ break
+ done
+}
+
+echo "Following functions are added to your environment:"
+_runtest_cts_mediacomponent_usage
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index 9ee4928..3d812f8 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -41,6 +41,7 @@
void onDisconnected();
void onCustomLayoutChanged(in List<Bundle> commandButtonlist);
+ void onAllowedCommandsChanged(in Bundle commands);
void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver);
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 4a4f250..96fdda0 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -314,12 +314,12 @@
}
@Override
- public void skipToPrevious_impl() {
+ public void skipToPreviousItem_impl() {
sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
}
@Override
- public void skipToNext_impl() {
+ public void skipToNextItem_impl() {
sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
}
@@ -351,7 +351,7 @@
@Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
- final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+ final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (binder != null) {
try {
binder.setVolumeTo(mSessionCallbackStub, value, flags);
@@ -366,7 +366,7 @@
@Override
public void adjustVolume_impl(int direction, int flags) {
// TODO(hdmoon): sanity check
- final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+ final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (binder != null) {
try {
binder.adjustVolume(mSessionCallbackStub, direction, flags);
@@ -561,7 +561,7 @@
Bundle args = new Bundle();
args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, item);
sendTransportControlCommand(
- MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM, args);
+ MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, args);
*/
}
@@ -651,7 +651,7 @@
}
@Override
- public MediaItem2 getCurrentPlaylistItem_impl() {
+ public MediaItem2 getCurrentMediaItem_impl() {
// TODO(jaewan): Implement
return null;
}
@@ -776,6 +776,12 @@
});
}
+ void onAllowedCommandsChanged(final CommandGroup commands) {
+ mCallbackExecutor.execute(() -> {
+ mCallback.onAllowedCommandsChanged(mInstance, commands);
+ });
+ }
+
void onCustomLayoutChanged(final List<CommandButton> layout) {
mCallbackExecutor.execute(() -> {
mCallback.onCustomLayoutChanged(mInstance, layout);
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index 252512c..039ff8f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -44,14 +44,11 @@
private DataSourceDesc mDataSourceDesc;
// From the public API
- public MediaItem2Impl(Context context, String mediaId,
- DataSourceDesc dsd, MediaMetadata2 metadata, @Flags int flags) {
+ public MediaItem2Impl(@NonNull Context context, @NonNull String mediaId,
+ @Nullable DataSourceDesc dsd, @Nullable MediaMetadata2 metadata, @Flags int flags) {
if (mediaId == null) {
throw new IllegalArgumentException("mediaId shouldn't be null");
}
- if (dsd == null) {
- throw new IllegalArgumentException("dsd shouldn't be null");
- }
if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
}
@@ -186,10 +183,7 @@
}
@Override
- public Builder setDataSourceDesc_impl(@NonNull DataSourceDesc dataSourceDesc) {
- if (dataSourceDesc == null) {
- throw new IllegalArgumentException("dataSourceDesc shouldn't be null");
- }
+ public Builder setDataSourceDesc_impl(@Nullable DataSourceDesc dataSourceDesc) {
mDataSourceDesc = dataSourceDesc;
return mInstance;
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 1cc9a77..5351394 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -24,7 +24,7 @@
import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaPlayerBase;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSessionService2;
@@ -68,11 +68,11 @@
public static class MediaLibrarySessionImpl extends MediaSession2Impl
implements MediaLibrarySessionProvider {
public MediaLibrarySessionImpl(Context context,
- MediaPlayerBase player, String id, MediaPlaylistController mplc,
+ MediaPlayerBase player, String id, MediaPlaylistAgent playlistAgent,
VolumeProvider2 volumeProvider,
PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, mplc, volumeProvider, sessionActivity, callbackExecutor,
+ super(context, player, id, playlistAgent, volumeProvider, sessionActivity, callbackExecutor,
callback);
// Don't put any extra initialization here. Here's the reason.
// System service will recognize this session inside of the super constructor and would
@@ -141,7 +141,7 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mMplc,
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mPlaylistAgent,
mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
new file mode 100644
index 0000000..b3c17f9
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package com.android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaPlaylistAgent.PlaylistEventCallback;
+import android.media.update.MediaPlaylistAgentProvider;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class MediaPlaylistAgentImpl implements MediaPlaylistAgentProvider {
+ private final Context mContext;
+ private final MediaPlaylistAgent mInstance;
+
+ public MediaPlaylistAgentImpl(Context context, MediaPlaylistAgent instance) {
+ mContext = context;
+ mInstance = instance;
+ }
+
+ final public void registerPlaylistEventCallback_impl(
+ @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor shouldn't be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ final public void unregisterPlaylistEventCallback_impl(
+ @NonNull PlaylistEventCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ final public void notifyPlaylistChanged_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ final public void notifyPlaylistMetadataChanged_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ final public void notifyShuffleModeChanged_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ final public void notifyRepeatModeChanged_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public @Nullable List<MediaItem2> getPlaylist_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ return null;
+ }
+
+ public void setPlaylist_impl(@NonNull List<MediaItem2> list,
+ @Nullable MediaMetadata2 metadata) {
+ if (list == null) {
+ throw new IllegalArgumentException("list shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public @Nullable MediaMetadata2 getPlaylistMetadata_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ return null;
+ }
+
+ public void updatePlaylistMetadata_impl(@Nullable MediaMetadata2 metadata) {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void removePlaylistItem_impl(@NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index can not have a negative value");
+ }
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) {
+ if (item == null) {
+ throw new IllegalArgumentException("item shouldn't be null");
+ }
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void skipToPreviousItem_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public void skipToNextItem_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public int getRepeatMode_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ return MediaPlaylistAgent.REPEAT_MODE_NONE;
+ }
+
+ public void setRepeatMode_impl(int repeatMode) {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+
+ public int getShuffleMode_impl() {
+ // TODO(jaewan): implement this (b/74090741)
+ return MediaPlaylistAgent.SHUFFLE_MODE_NONE;
+ }
+
+ public void setShuffleMode_impl(int shuffleMode) {
+ // TODO(jaewan): implement this (b/74090741)
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index b8e651e..451368f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -215,6 +215,27 @@
}
@Override
+ public void onAllowedCommandsChanged(Bundle commandsBundle) {
+ final MediaController2Impl controller;
+ try {
+ controller = getController();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (controller == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ CommandGroup commands = CommandGroup.fromBundle(controller.getContext(), commandsBundle);
+ if (commands == null) {
+ Log.w(TAG, "onAllowedCommandsChanged(): Ignoring null commands");
+ return;
+ }
+ controller.onAllowedCommandsChanged(commands);
+ }
+
+ @Override
public void onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
final MediaController2Impl controller;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 6aab84a..4c6b0a4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -21,14 +21,12 @@
import static android.media.SessionToken2.TYPE_SESSION;
import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
-import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -39,7 +37,7 @@
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
import android.media.MediaPlayerBase.PlayerEventCallback;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -107,7 +105,7 @@
@GuardedBy("mLock")
private MediaPlayerBase mPlayer;
@GuardedBy("mLock")
- private MediaPlaylistController mMplc;
+ private MediaPlaylistAgent mPlaylistAgent;
@GuardedBy("mLock")
private VolumeProvider2 mVolumeProvider;
@GuardedBy("mLock")
@@ -120,14 +118,14 @@
* @param context
* @param player
* @param id
- * @param mplc
+ * @param playlistAgent
* @param volumeProvider
* @param sessionActivity
* @param callbackExecutor
* @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerBase player, String id,
- MediaPlaylistController mplc, VolumeProvider2 volumeProvider,
+ MediaPlaylistAgent playlistAgent, VolumeProvider2 volumeProvider,
PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
@@ -142,7 +140,7 @@
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mMplc = mplc;
+ mPlaylistAgent = playlistAgent;
// Infer type from the id and package name.
String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
@@ -206,13 +204,13 @@
}
@Override
- public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+ public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
VolumeProvider2 volumeProvider) throws IllegalArgumentException {
ensureCallingThread();
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
}
- mMplc = mplc;
+ mPlaylistAgent = playlistAgent;
setPlayer(player, volumeProvider);
}
@@ -344,18 +342,16 @@
ensureCallingThread();
final MediaPlayerBase player = mPlayer;
if (player != null) {
- // TODO: Uncomment or remove
- //player.stop();
- player.pause();
+ player.reset();
} else if (DEBUG) {
Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
}
@Override
- public void skipToPrevious_impl() {
+ public void skipToPreviousItem_impl() {
ensureCallingThread();
- // TODO: Uncomment or remove
+ // TODO(jaewan): Implement this (b/74175632)
/*
final MediaPlayerBase player = mPlayer;
if (player != null) {
@@ -368,14 +364,17 @@
}
@Override
- public void skipToNext_impl() {
+ public void skipToNextItem_impl() {
ensureCallingThread();
+ // TODO(jaewan): Implement this (b/74175632)
+ /*
final MediaPlayerBase player = mPlayer;
if (player != null) {
player.skipToNext();
} else if (DEBUG) {
Log.d(TAG, "API calls after the close()", new IllegalStateException());
}
+ */
}
@Override
@@ -431,7 +430,13 @@
@Override
public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
- // TODO(jaewan): Implement
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (commands == null) {
+ throw new IllegalArgumentException("commands shouldn't be null");
+ }
+ mSessionStub.setAllowedCommands(controller, commands);
}
@Override
@@ -455,7 +460,7 @@
/*
final MediaPlayerBase player = mPlayer;
if (player != null) {
- // TODO implement, use the SessionPlaylistController itf
+ // TODO implement and use SessionPlaylistAgent
//player.setPlaylist(playlist);
mSessionStub.notifyPlaylistChanged(playlist);
} else if (DEBUG) {
@@ -885,8 +890,7 @@
@Override
public void addAllPredefinedCommands_impl() {
- final int COMMAND_CODE_MAX = 22;
- for (int i = 1; i <= COMMAND_CODE_MAX; i++) {
+ for (int i = 1; i <= MediaSession2.COMMAND_CODE_MAX; i++) {
mCommands.add(new Command(mContext, i));
}
}
@@ -974,31 +978,13 @@
mInstance = instance;
mUid = uid;
mPackageName = packageName;
-
- // TODO(jaewan): Remove this workaround
- if ("com.android.server.media".equals(packageName)) {
- mIsTrusted = true;
- } else if (context.checkPermission(permission.MEDIA_CONTENT_CONTROL, pid, uid) ==
- PackageManager.PERMISSION_GRANTED) {
- mIsTrusted = true;
- } else {
- // TODO(jaewan): Also consider enabled notification listener.
- mIsTrusted = false;
- // System apps may bind across the user so uid can be differ.
- // Skip sanity check for the system app.
- try {
- int uidForPackage = context.getPackageManager().getPackageUid(packageName, 0);
- if (uid != uidForPackage) {
- throw new IllegalArgumentException("Illegal call from uid=" + uid +
- ", pkg=" + packageName + ". Expected uid" + uidForPackage);
- }
- } catch (NameNotFoundException e) {
- // Rethrow exception with different name because binder methods only accept
- // RemoteException.
- throw new IllegalArgumentException(e);
- }
- }
mControllerBinder = callback;
+ MediaSessionManager manager =
+ (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ // Ask server whether the controller is trusted.
+ // App cannot know this because apps cannot query enabled notification listener for
+ // another package, but system server can do.
+ mIsTrusted = manager.isTrusted(uid, packageName);
}
@Override
@@ -1290,7 +1276,7 @@
String mId;
Executor mCallbackExecutor;
C mCallback;
- MediaPlaylistController mMplc;
+ MediaPlaylistAgent mPlaylistAgent;
VolumeProvider2 mVolumeProvider;
PendingIntent mSessionActivity;
@@ -1319,11 +1305,11 @@
}
@Override
- public void setPlaylistController_impl(MediaPlaylistController mplc) {
- if (mplc == null) {
- throw new IllegalArgumentException("mplc shouldn't be null");
+ public void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent) {
+ if (playlistAgent == null) {
+ throw new IllegalArgumentException("playlistAgent shouldn't be null");
}
- mMplc = mplc;
+ mPlaylistAgent = playlistAgent;
}
public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) {
@@ -1372,7 +1358,7 @@
mCallback = new SessionCallback(mContext) {};
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mMplc,
+ return new MediaSession2Impl(mContext, mPlayer, mId, mPlaylistAgent,
mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 785248c..d4164f6 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -267,7 +267,7 @@
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist =
- allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET)
+ allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST)
? session.getInstance().getPlaylist() : null;
final List<Bundle> playlistBundle;
if (playlist != null) {
@@ -332,23 +332,23 @@
throws RuntimeException {
final MediaSession2Impl session = getSession();
final ControllerInfo controller = getControllerIfAble(
- caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+ caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (session == null || controller == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+ if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
return;
}
// TODO(jaewan): Sanity check.
Command command = new Command(
- session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+ session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
controller, command);
if (!accepted) {
// Don't run rejected command.
if (DEBUG) {
- Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+ Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
+ controller + " was rejected by " + session);
}
return;
@@ -368,23 +368,23 @@
throws RuntimeException {
final MediaSession2Impl session = getSession();
final ControllerInfo controller = getControllerIfAble(
- caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+ caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
if (session == null || controller == null) {
return;
}
session.getCallbackExecutor().execute(() -> {
- if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+ if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
return;
}
// TODO(jaewan): Sanity check.
Command command = new Command(
- session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+ session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
controller, command);
if (!accepted) {
// Don't run rejected command.
if (DEBUG) {
- Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+ Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
+ controller + " was rejected by " + session);
}
return;
@@ -435,10 +435,10 @@
session.getInstance().stop();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
- session.getInstance().skipToPrevious();
+ session.getInstance().skipToPreviousItem();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
- session.getInstance().skipToNext();
+ session.getInstance().skipToNextItem();
break;
case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
session.getInstance().prepare();
@@ -452,18 +452,21 @@
case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM:
+ case MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM:
// TODO(jaewan): Implement
/*
session.getInstance().skipToPlaylistItem(
args.getInt(ARGUMENT_KEY_ITEM_INDEX));
*/
break;
+ // TODO(jaewan): Remove (b/74116823)
+ /*
case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
session.getInstance().setPlaylistParams(
PlaylistParams.fromBundle(session.getContext(),
args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
break;
+ */
default:
// TODO(jaewan): Resend unknown (new) commands through the custom command.
}
@@ -910,7 +913,7 @@
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(
- list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET);
+ list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST);
if (controllerBinder != null) {
try {
controllerBinder.onPlaylistChanged(bundleList);
@@ -955,6 +958,22 @@
}
}
+ public void setAllowedCommands(ControllerInfo controller, CommandGroup commands) {
+ synchronized (mLock) {
+ mAllowedCommandGroupMap.put(controller, commands);
+ }
+ final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+ if (controllerBinder == null) {
+ return;
+ }
+ try {
+ controllerBinder.onAllowedCommandsChanged(commands.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
if (receiver != null && controller == null) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 4537d5f..6bd2b2a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -138,7 +138,7 @@
private class SessionServiceEventCallback extends PlayerEventCallback {
@Override
- public void onPlayerStateChanged(MediaPlayerBase mpb, int state) {
+ public void onPlayerStateChanged(MediaPlayerBase player, int state) {
// TODO: Implement this
return;
}
diff --git a/packages/MediaComponents/src/com/android/media/RoutePlayer.java b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
new file mode 100644
index 0000000..9450d34
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+
+import com.android.support.mediarouter.media.MediaItemStatus;
+import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaSessionStatus;
+import com.android.support.mediarouter.media.RemotePlaybackClient;
+import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback;
+import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback;
+import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback;
+
+import java.util.Map;
+
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class RoutePlayer extends MediaSession.Callback {
+ public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE
+ | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO
+ | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
+
+ private RemotePlaybackClient mClient;
+ private String mSessionId;
+ private String mItemId;
+ private PlayerEventCallback mCallback;
+
+ private StatusCallback mStatusCallback = new StatusCallback() {
+ @Override
+ public void onItemStatusChanged(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus,
+ String itemId, MediaItemStatus itemStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ updateItemStatus(itemId, itemStatus);
+ }
+ };
+
+ public RoutePlayer(Context context, MediaRouter.RouteInfo route) {
+ mClient = new RemotePlaybackClient(context, route);
+ mClient.setStatusCallback(mStatusCallback);
+ mClient.startSession(null, new SessionActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ }
+ });
+ }
+
+ @Override
+ public void onPlay() {
+ mClient.resume(null, new SessionActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ mClient.pause(null, new SessionActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ }
+ });
+ }
+
+ @Override
+ public void onSeekTo(long pos) {
+ mClient.seek(mItemId, pos, null, new ItemActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus,
+ String itemId, MediaItemStatus itemStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ updateItemStatus(itemId, itemStatus);
+ }
+ });
+ }
+
+ @Override
+ public void onStop() {
+ mClient.stop(null, new SessionActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ }
+ });
+ }
+
+ public void setPlayerEventCallback(PlayerEventCallback callback) {
+ mCallback = callback;
+ }
+
+ public void openVideo(DataSourceDesc dsd) {
+ mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus,
+ String itemId, MediaItemStatus itemStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ updateItemStatus(itemId, itemStatus);
+ playInternal(dsd.getUri());
+ }
+ });
+ }
+
+ public void release() {
+ if (mClient != null) {
+ mClient.release();
+ mClient = null;
+ }
+ if (mCallback != null) {
+ mCallback = null;
+ }
+ }
+
+ private void playInternal(Uri uri) {
+ mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() {
+ @Override
+ public void onResult(Bundle data,
+ String sessionId, MediaSessionStatus sessionStatus,
+ String itemId, MediaItemStatus itemStatus) {
+ updateSessionStatus(sessionId, sessionStatus);
+ updateItemStatus(itemId, itemStatus);
+ }
+ });
+ }
+
+ private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) {
+ mSessionId = sessionId;
+ }
+
+ private void updateItemStatus(String itemId, MediaItemStatus itemStatus) {
+ mItemId = itemId;
+ if (itemStatus == null || mCallback == null) return;
+ mCallback.onPlayerStateChanged(itemStatus);
+ }
+
+ public static abstract class PlayerEventCallback {
+ public void onPlayerStateChanged(MediaItemStatus itemStatus) { }
+ }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 9a01ade..07b8788 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -32,6 +32,7 @@
import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import android.media.MediaMetadata2;
import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
@@ -50,6 +51,7 @@
import android.media.update.MediaItem2Provider;
import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
import android.media.update.MediaMetadata2Provider;
+import android.media.update.MediaPlaylistAgentProvider;
import android.media.update.MediaSession2Provider;
import android.media.update.MediaSession2Provider.BuilderBaseProvider;
import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
@@ -76,6 +78,7 @@
import com.android.media.MediaLibraryService2Impl;
import com.android.media.MediaLibraryService2Impl.LibraryRootImpl;
import com.android.media.MediaMetadata2Impl;
+import com.android.media.MediaPlaylistAgentImpl;
import com.android.media.MediaSession2Impl;
import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
import com.android.media.MediaSessionService2Impl;
@@ -301,4 +304,10 @@
public PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle) {
return PlaybackState2Impl.fromBundle(context, bundle);
}
+
+ @Override
+ public MediaPlaylistAgentProvider createMediaPlaylistAgent(Context context,
+ MediaPlaylistAgent instance) {
+ return new MediaPlaylistAgentImpl(context, instance);
+ }
}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
index 8577123..fc92e85 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.graphics.Rect;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
@@ -33,7 +33,7 @@
private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
private SurfaceHolder mSurfaceHolder = null;
private SurfaceListener mSurfaceListener = null;
- private MediaPlayer mMediaPlayer;
+ private MediaPlayer2 mMediaPlayer;
// A flag to indicate taking over other view should be proceed.
private boolean mIsTakingOverOldView;
private VideoViewInterface mOldView;
@@ -62,7 +62,7 @@
////////////////////////////////////////////////////
@Override
- public boolean assignSurfaceToMediaPlayer(MediaPlayer mp) {
+ public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceHolder: " + mSurfaceHolder);
if (mp == null || !hasAvailableSurface()) {
return false;
@@ -82,7 +82,7 @@
}
@Override
- public void setMediaPlayer(MediaPlayer mp) {
+ public void setMediaPlayer(MediaPlayer2 mp) {
mMediaPlayer = mp;
if (mIsTakingOverOldView) {
takeOver(mOldView);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
index 69a4ebe..024a3aa 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
@@ -38,7 +38,7 @@
private SurfaceTexture mSurfaceTexture;
private Surface mSurface;
private SurfaceListener mSurfaceListener;
- private MediaPlayer mMediaPlayer;
+ private MediaPlayer2 mMediaPlayer;
// A flag to indicate taking over other view should be proceed.
private boolean mIsTakingOverOldView;
private VideoViewInterface mOldView;
@@ -66,7 +66,7 @@
////////////////////////////////////////////////////
@Override
- public boolean assignSurfaceToMediaPlayer(MediaPlayer mp) {
+ public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture);
if (mp == null || !hasAvailableSurface()) {
// Surface is not ready.
@@ -87,7 +87,7 @@
}
@Override
- public void setMediaPlayer(MediaPlayer mp) {
+ public void setMediaPlayer(MediaPlayer2 mp) {
mMediaPlayer = mp;
if (mIsTakingOverOldView) {
takeOver(mOldView);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index c3ca057..0bb659d 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -16,16 +16,15 @@
package com.android.widget;
-import android.annotation.NonNull;
import android.content.Context;
-import android.content.res.Resources;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.DataSourceDesc;
import android.media.MediaMetadata;
-import android.media.MediaPlayer;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayer2;
+import android.media.MediaPlayer2.MediaPlayer2EventCallback;
+import android.media.MediaPlayer2Impl;
import android.media.Cea708CaptionRenderer;
import android.media.ClosedCaptionRenderer;
import android.media.MediaItem2;
@@ -45,6 +44,7 @@
import android.media.update.VideoView2Provider;
import android.media.update.ViewGroupProvider;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.support.annotation.Nullable;
@@ -58,15 +58,12 @@
import android.widget.MediaControlView2;
import android.widget.VideoView2;
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
+import com.android.media.RoutePlayer;
+import com.android.support.mediarouter.media.MediaItemStatus;
+import com.android.support.mediarouter.media.MediaControlIntent;
import com.android.support.mediarouter.media.MediaRouter;
import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.RemotePlaybackClient;
-import com.android.support.mediarouter.media.MediaItemStatus;
-import com.android.support.mediarouter.media.MediaSessionStatus;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -103,13 +100,11 @@
private VideoTextureView mTextureView;
private VideoSurfaceView mSurfaceView;
- private MediaPlayer mMediaPlayer;
+ private MediaPlayer2 mMediaPlayer;
+ private DataSourceDesc mDsd;
private MediaControlView2 mMediaControlView;
private MediaSession mMediaSession;
private MediaController mMediaController;
- private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback();
- private MediaRouter mMediaRouter;
- private MediaRouteSelector mRouteSelector;
private Metadata mMetadata;
private MediaMetadata2 mMediaMetadata;
private boolean mNeedUpdateMediaType;
@@ -142,9 +137,80 @@
// TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams().
// Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit
private float mFallbackSpeed; // keep the original speed before 'pause' is called.
-
private long mShowControllerIntervalMs;
+ private MediaRouter mMediaRouter;
+ private MediaRouteSelector mRouteSelector;
+ private MediaRouter.RouteInfo mRoute;
+ private RoutePlayer mRoutePlayer;
+
+ private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() {
+ @Override
+ public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
+ if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
+ // Stop local playback (if necessary)
+ resetPlayer();
+ mRoute = route;
+ mRoutePlayer = new RoutePlayer(mInstance.getContext(), route);
+ mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() {
+ @Override
+ public void onPlayerStateChanged(MediaItemStatus itemStatus) {
+ PlaybackState.Builder psBuilder = new PlaybackState.Builder();
+ psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS);
+ long position = itemStatus.getContentPosition();
+ switch (itemStatus.getPlaybackState()) {
+ case MediaItemStatus.PLAYBACK_STATE_PENDING:
+ psBuilder.setState(PlaybackState.STATE_NONE, position, 0);
+ mCurrentState = STATE_IDLE;
+ break;
+ case MediaItemStatus.PLAYBACK_STATE_PLAYING:
+ psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1);
+ mCurrentState = STATE_PLAYING;
+ break;
+ case MediaItemStatus.PLAYBACK_STATE_PAUSED:
+ psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0);
+ mCurrentState = STATE_PAUSED;
+ break;
+ case MediaItemStatus.PLAYBACK_STATE_BUFFERING:
+ psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0);
+ mCurrentState = STATE_PAUSED;
+ break;
+ case MediaItemStatus.PLAYBACK_STATE_FINISHED:
+ psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0);
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ break;
+ }
+
+ PlaybackState pbState = psBuilder.build();
+ mMediaSession.setPlaybackState(pbState);
+
+ MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder();
+ mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION,
+ itemStatus.getContentDuration());
+ mMediaSession.setMetadata(mmBuilder.build());
+ }
+ });
+ // Start remote playback (if necessary)
+ mRoutePlayer.openVideo(mDsd);
+ }
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) {
+ if (mRoute != null && mRoutePlayer != null) {
+ mRoutePlayer.release();
+ mRoutePlayer = null;
+ }
+ if (mRoute == route) {
+ mRoute = null;
+ }
+ if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
+ // TODO: Resume local playback (if necessary)
+ openVideo(mDsd);
+ }
+ }
+ };
+
public VideoView2Impl(VideoView2 instance,
ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
super(instance, superProvider, privateProvider);
@@ -196,7 +262,6 @@
mSubtitleView.setBackgroundColor(0);
mInstance.addView(mSubtitleView);
- // TODO: Need a common namespace for attributes those are defined in updatable library.
boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
"http://schemas.android.com/apk/res/android",
"enableControlView", true);
@@ -219,16 +284,20 @@
Log.d(TAG, "viewType attribute is textureView.");
// TODO: implement
}
+
+ MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
+ builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+ builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
+ builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
+ mRouteSelector = builder.build();
}
@Override
public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) {
mMediaControlView = mediaControlView;
mShowControllerIntervalMs = intervalMs;
- if (mRouteSelector != null) {
- ((MediaControlView2Impl) mMediaControlView.getProvider())
- .setRouteSelector(mRouteSelector);
- }
+ // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe.
+ ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector);
if (mInstance.isAttachedToWindow()) {
attachMediaControlView();
@@ -328,30 +397,6 @@
}
@Override
- public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) {
- // TODO: implement this.
- }
-
- @Override
- public void setRouteAttributes_impl(@NonNull List<String> routeCategories,
- MediaSession.Callback sessionPlayer) {
- MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
- for (String category : routeCategories) {
- builder.addControlCategory(category);
- }
- mRouteSelector = builder.build();
- if (mMediaControlView != null) {
- ((MediaControlView2Impl) mMediaControlView.getProvider())
- .setRouteSelector(mRouteSelector);
- }
- mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
- mRouteSessionCallback = sessionPlayer;
- if (mMediaSession != null) {
- mMediaRouter.setMediaSession(mMediaSession);
- }
- }
-
- @Override
public void setVideoPath_impl(String path) {
mInstance.setVideoUri(Uri.parse(path));
}
@@ -363,8 +408,9 @@
@Override
public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
- mSeekWhenPrepared = 0;
- openVideo(uri, headers);
+ DataSourceDesc.Builder builder = new DataSourceDesc.Builder();
+ builder.setDataSource(mInstance.getContext(), uri, headers, null);
+ mInstance.setDataSource(builder.build());
}
@Override
@@ -374,7 +420,9 @@
@Override
public void setDataSource_impl(DataSourceDesc dsd) {
- // TODO: implement this
+ mDsd = dsd;
+ mSeekWhenPrepared = 0;
+ openVideo(dsd);
}
@Override
@@ -431,10 +479,11 @@
// Create MediaSession
mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
mMediaSession.setCallback(new MediaSessionCallback());
+ mMediaSession.setActive(true);
mMediaController = mMediaSession.getController();
- if (mMediaRouter != null) {
- mMediaRouter.setMediaSession(mMediaSession);
- }
+ mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
+ mMediaRouter.setMediaSession(mMediaSession);
+ mMediaRouter.addCallback(mRouteSelector, mRouterCallback);
attachMediaControlView();
// TODO: remove this after moving MediaSession creating code inside initializing VideoView2
if (mCurrentState == STATE_PREPARED) {
@@ -545,21 +594,27 @@
}
private boolean isInPlaybackState() {
- return (mMediaPlayer != null
+ return (mMediaPlayer != null || mRoutePlayer != null)
&& mCurrentState != STATE_ERROR
&& mCurrentState != STATE_IDLE
- && mCurrentState != STATE_PREPARING);
+ && mCurrentState != STATE_PREPARING;
}
private boolean needToStart() {
- return (mMediaPlayer != null
+ return (mMediaPlayer != null || mRoutePlayer != null)
&& mCurrentState != STATE_PLAYING
- && mTargetState == STATE_PLAYING);
+ && mTargetState == STATE_PLAYING;
}
- // Creates a MediaPlayer instance and prepare playback.
- private void openVideo(Uri uri, Map<String, String> headers) {
+ // Creates a MediaPlayer2 instance and prepare playback.
+ private void openVideo(DataSourceDesc dsd) {
+ Uri uri = dsd.getUri();
+ Map<String, String> headers = dsd.getUriHeaders();
resetPlayer();
+ if (isRemotePlayback()) {
+ mRoutePlayer.openVideo(dsd);
+ return;
+ }
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
// TODO this should have a focus listener
AudioFocusRequest focusRequest;
@@ -570,13 +625,13 @@
}
try {
- Log.d(TAG, "openVideo(): creating new MediaPlayer instance.");
- mMediaPlayer = new MediaPlayer();
+ Log.d(TAG, "openVideo(): creating new MediaPlayer2 instance.");
+ mMediaPlayer = new MediaPlayer2Impl();
mSurfaceView.setMediaPlayer(mMediaPlayer);
mTextureView.setMediaPlayer(mMediaPlayer);
mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer);
- // TODO: create SubtitleController in MediaPlayer, but we need
+ // TODO: create SubtitleController in MediaPlayer2, but we need
// a context for the subtitle renderers
final Context context = mInstance.getContext();
final SubtitleController controller = new SubtitleController(
@@ -588,23 +643,21 @@
controller.registerRenderer(new SRTRenderer(context));
mMediaPlayer.setSubtitleAnchor(
controller, (SubtitleController.Anchor) mSubtitleView);
- // TODO: Remove timed text related code later once relevant Renderer is defined.
- // This is just for debugging purpose.
- mMediaPlayer.setOnTimedTextListener(mTimedTextListener);
+ Executor executor = new Executor() {
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+ };
+ mMediaPlayer.setMediaPlayer2EventCallback(executor, mMediaPlayer2Callback);
- mMediaPlayer.setOnPreparedListener(mPreparedListener);
- mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
- mMediaPlayer.setOnCompletionListener(mCompletionListener);
- mMediaPlayer.setOnErrorListener(mErrorListener);
- mMediaPlayer.setOnInfoListener(mInfoListener);
- mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
- mMediaPlayer.setDataSource(mInstance.getContext(), uri, headers);
+ mMediaPlayer.setDataSource(dsd);
mMediaPlayer.setAudioAttributes(mAudioAttributes);
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
- mMediaPlayer.prepareAsync();
+ mMediaPlayer.prepare();
// Save file name as title since the file may not have a title Metadata.
mTitle = uri.getPath();
@@ -617,24 +670,12 @@
Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
+ ", mTargetState=" + mTargetState);
}
- /*
- for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
- try {
- mMediaPlayer.addSubtitleSource(pending.first, pending.second);
- } catch (IllegalStateException e) {
- mInfoListener.onInfo(
- mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
- }
- }
- */
- } catch (IOException | IllegalArgumentException ex) {
+ } catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + uri, ex);
mCurrentState = STATE_ERROR;
mTargetState = STATE_ERROR;
- mErrorListener.onError(mMediaPlayer,
- MediaPlayer.MEDIA_ERROR_UNKNOWN, MediaPlayer.MEDIA_ERROR_IO);
- } finally {
- //mPendingSubtitleTracks.clear();
+ mMediaPlayer2Callback.onError(mMediaPlayer, dsd,
+ MediaPlayer2.MEDIA_ERROR_UNKNOWN, MediaPlayer2.MEDIA_ERROR_IO);
}
}
@@ -643,9 +684,18 @@
*/
private void resetPlayer() {
if (mMediaPlayer != null) {
- mMediaPlayer.reset();
- mMediaPlayer.release();
+ final MediaPlayer2 player = mMediaPlayer;
+ new AsyncTask<MediaPlayer2, Void, Void>() {
+ @Override
+ protected Void doInBackground(MediaPlayer2... players) {
+ // TODO: Fix NPE while MediaPlayer2.close()
+ //players[0].close();
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player);
mMediaPlayer = null;
+ mTextureView.setMediaPlayer(null);
+ mSurfaceView.setMediaPlayer(null);
//mPendingSubtitleTracks.clear();
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
@@ -660,8 +710,8 @@
private void updatePlaybackState() {
if (mStateBuilder == null) {
// Get the capabilities of the player for this stream
- mMetadata = mMediaPlayer.getMetadata(MediaPlayer.METADATA_ALL,
- MediaPlayer.BYPASS_METADATA_FILTER);
+ mMetadata = mMediaPlayer.getMetadata(MediaPlayer2.METADATA_ALL,
+ MediaPlayer2.BYPASS_METADATA_FILTER);
// Add Play action as default
long playbackActions = PlaybackState.ACTION_PLAY;
@@ -701,6 +751,8 @@
if (mCurrentState != STATE_ERROR
&& mCurrentState != STATE_IDLE
&& mCurrentState != STATE_PREPARING) {
+ // TODO: this should be replaced with MediaPlayer2.getBufferedPosition() once it is
+ // implemented.
mStateBuilder.setBufferedPosition(
(long) (mCurrentBufferPercentage / 100.0) * mMediaPlayer.getDuration());
}
@@ -792,8 +844,8 @@
return false;
}
PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
- return (playbackInfo != null)
- && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+ return playbackInfo != null
+ && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
}
private void selectOrDeselectSubtitle(boolean select) {
@@ -817,18 +869,18 @@
}
private void extractTracks() {
- MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+ List<MediaPlayer2.TrackInfo> trackInfos = mMediaPlayer.getTrackInfo();
mVideoTrackIndices = new ArrayList<>();
mAudioTrackIndices = new ArrayList<>();
mSubtitleTrackIndices = new ArrayList<>();
- for (int i = 0; i < trackInfos.length; ++i) {
- int trackType = trackInfos[i].getTrackType();
- if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
+ for (int i = 0; i < trackInfos.size(); ++i) {
+ int trackType = trackInfos.get(i).getTrackType();
+ if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
mVideoTrackIndices.add(i);
- } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
+ } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
mAudioTrackIndices.add(i);
- } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
- || trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+ } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
+ || trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
mSubtitleTrackIndices.add(i);
}
}
@@ -842,127 +894,50 @@
mMediaSession.sendSessionEvent(MediaControlView2Impl.EVENT_UPDATE_TRACK_STATUS, data);
}
- MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
- new MediaPlayer.OnVideoSizeChangedListener() {
- public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ MediaPlayer2EventCallback mMediaPlayer2Callback =
+ new MediaPlayer2EventCallback() {
+ @Override
+ public void onVideoSizeChanged(
+ MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) {
if (DEBUG) {
- Log.d(TAG, "OnVideoSizeChanged(): size: " + width + "/" + height);
+ Log.d(TAG, "onVideoSizeChanged(): size: " + width + "/" + height);
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
if (DEBUG) {
- Log.d(TAG, "OnVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
+ Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
+ mVideoHeight);
}
-
if (mVideoWidth != 0 && mVideoHeight != 0) {
mInstance.requestLayout();
}
}
- };
- MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mp) {
- if (DEBUG) {
- Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
- + ", mTargetState=" + mTargetState);
- }
- mCurrentState = STATE_PREPARED;
- // Create and set playback state for MediaControlView2
- updatePlaybackState();
-
- // TODO: change this to send TrackInfos to MediaControlView2
- // TODO: create MediaSession when initializing VideoView2
- if (mMediaSession != null) {
- extractTracks();
- }
-
- if (mMediaControlView != null) {
- mMediaControlView.setEnabled(true);
- }
- int videoWidth = mp.getVideoWidth();
- int videoHeight = mp.getVideoHeight();
-
- // mSeekWhenPrepared may be changed after seekTo() call
- long seekToPosition = mSeekWhenPrepared;
- if (seekToPosition != 0) {
- mMediaController.getTransportControls().seekTo(seekToPosition);
- }
-
- if (videoWidth != 0 && videoHeight != 0) {
- if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
- if (DEBUG) {
- Log.i(TAG, "OnPreparedListener() : ");
- Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
- Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
- + mInstance.getMeasuredHeight());
- Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
- + mInstance.getHeight());
- }
-
- mVideoWidth = videoWidth;
- mVideoHeight = videoHeight;
- mInstance.requestLayout();
+ // TODO: Remove timed text related code later once relevant Renderer is defined.
+ // This is just for debugging purpose.
+ @Override
+ public void onTimedText(
+ MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
+ Log.d(TAG, "TimedText: " + text.getText());
}
- if (needToStart()) {
- mMediaController.getTransportControls().play();
- }
- } else {
- // We don't know the video size yet, but should start anyway.
- // The video size might be reported to us later.
- if (needToStart()) {
- mMediaController.getTransportControls().play();
- }
- }
-
- // Get and set duration and title values as MediaMetadata for MediaControlView2
- MediaMetadata.Builder builder = new MediaMetadata.Builder();
- if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
- mTitle = mMetadata.getString(Metadata.TITLE);
- }
- builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
- builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
-
- if (mMediaSession != null) {
- mMediaSession.setMetadata(builder.build());
-
- // TODO: merge this code with the above code when integrating with MediaSession2.
- if (mNeedUpdateMediaType) {
- mMediaSession.sendSessionEvent(
- MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
- mNeedUpdateMediaType = false;
- }
- }
- }
- };
-
- private MediaPlayer.OnCompletionListener mCompletionListener =
- new MediaPlayer.OnCompletionListener() {
- public void onCompletion(MediaPlayer mp) {
- mCurrentState = STATE_PLAYBACK_COMPLETED;
- mTargetState = STATE_PLAYBACK_COMPLETED;
- updatePlaybackState();
-
- if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
- mAudioManager.abandonAudioFocus(null);
- }
- }
- };
-
- private MediaPlayer.OnInfoListener mInfoListener =
- new MediaPlayer.OnInfoListener() {
- public boolean onInfo(MediaPlayer mp, int what, int extra) {
- if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
+ @Override
+ public void onInfo(
+ MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+ if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
extractTracks();
+ } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+ this.onPrepared(mp, dsd);
+ } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+ this.onCompletion(mp, dsd);
+ } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
+ this.onBufferingUpdate(mp, dsd, extra);
}
- return true;
}
- };
- private MediaPlayer.OnErrorListener mErrorListener =
- new MediaPlayer.OnErrorListener() {
- public boolean onError(MediaPlayer mp, int frameworkErr, int implErr) {
+ @Override
+ public void onError(
+ MediaPlayer2 mp, DataSourceDesc dsd, int frameworkErr, int implErr) {
if (DEBUG) {
Log.d(TAG, "Error: " + frameworkErr + "," + implErr);
}
@@ -973,32 +948,103 @@
if (mMediaControlView != null) {
mMediaControlView.setVisibility(View.GONE);
}
- return true;
}
- };
- private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
- new MediaPlayer.OnBufferingUpdateListener() {
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ private void onPrepared(MediaPlayer2 mp, DataSourceDesc dsd) {
+ if (DEBUG) {
+ Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
+ + ", mTargetState=" + mTargetState);
+ }
+ mCurrentState = STATE_PREPARED;
+ // Create and set playback state for MediaControlView2
+ updatePlaybackState();
+
+ // TODO: change this to send TrackInfos to MediaControlView2
+ // TODO: create MediaSession when initializing VideoView2
+ if (mMediaSession != null) {
+ extractTracks();
+ }
+
+ if (mMediaControlView != null) {
+ mMediaControlView.setEnabled(true);
+ }
+ int videoWidth = mp.getVideoWidth();
+ int videoHeight = mp.getVideoHeight();
+
+ // mSeekWhenPrepared may be changed after seekTo() call
+ long seekToPosition = mSeekWhenPrepared;
+ if (seekToPosition != 0) {
+ mMediaController.getTransportControls().seekTo(seekToPosition);
+ }
+
+ if (videoWidth != 0 && videoHeight != 0) {
+ if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
+ if (DEBUG) {
+ Log.i(TAG, "OnPreparedListener() : ");
+ Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
+ Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
+ + mInstance.getMeasuredHeight());
+ Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
+ + mInstance.getHeight());
+ }
+ mVideoWidth = videoWidth;
+ mVideoHeight = videoHeight;
+ mInstance.requestLayout();
+ }
+
+ if (needToStart()) {
+ mMediaController.getTransportControls().play();
+ }
+ } else {
+ // We don't know the video size yet, but should start anyway.
+ // The video size might be reported to us later.
+ if (needToStart()) {
+ mMediaController.getTransportControls().play();
+ }
+ }
+ // Get and set duration and title values as MediaMetadata for MediaControlView2
+ MediaMetadata.Builder builder = new MediaMetadata.Builder();
+ if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
+ mTitle = mMetadata.getString(Metadata.TITLE);
+ }
+ builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
+ builder.putLong(
+ MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
+
+ if (mMediaSession != null) {
+ mMediaSession.setMetadata(builder.build());
+
+ // TODO: merge this code with the above code when integrating with
+ // MediaSession2.
+ if (mNeedUpdateMediaType) {
+ mMediaSession.sendSessionEvent(
+ MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS,
+ mMediaTypeData);
+ mNeedUpdateMediaType = false;
+ }
+ }
+ }
+
+ private void onCompletion(MediaPlayer2 mp, DataSourceDesc dsd) {
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ mTargetState = STATE_PLAYBACK_COMPLETED;
+ updatePlaybackState();
+ if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
+ mAudioManager.abandonAudioFocus(null);
+ }
+ }
+
+ private void onBufferingUpdate(MediaPlayer2 mp, DataSourceDesc dsd, int percent) {
mCurrentBufferPercentage = percent;
updatePlaybackState();
}
};
- // TODO: Remove timed text related code later once relevant Renderer is defined.
- // This is just for debugging purpose.
- private MediaPlayer.OnTimedTextListener mTimedTextListener =
- new MediaPlayer.OnTimedTextListener() {
- public void onTimedText(MediaPlayer mp, TimedText text) {
- Log.d(TAG, "TimedText: " + text.getText());
- }
- };
-
private class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onCommand(String command, Bundle args, ResultReceiver receiver) {
if (isRemotePlayback()) {
- mRouteSessionCallback.onCommand(command, args, receiver);
+ mRoutePlayer.onCommand(command, args, receiver);
} else {
switch (command) {
case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
@@ -1028,57 +1074,57 @@
@Override
public void onPlay() {
- if (isRemotePlayback()) {
- mRouteSessionCallback.onPlay();
- } else {
- if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+ if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+ if (isRemotePlayback()) {
+ mRoutePlayer.onPlay();
+ } else {
applySpeed();
- mMediaPlayer.start();
+ mMediaPlayer.play();
mCurrentState = STATE_PLAYING;
updatePlaybackState();
}
- mTargetState = STATE_PLAYING;
- if (DEBUG) {
- Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
- + ", mTargetState=" + mTargetState);
- }
+ mCurrentState = STATE_PLAYING;
+ }
+ mTargetState = STATE_PLAYING;
+ if (DEBUG) {
+ Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
+ + ", mTargetState=" + mTargetState);
}
showController();
}
@Override
public void onPause() {
- if (isRemotePlayback()) {
- mRouteSessionCallback.onPause();
- } else {
- if (isInPlaybackState()) {
- if (mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- mCurrentState = STATE_PAUSED;
- updatePlaybackState();
- }
+ if (isInPlaybackState()) {
+ if (isRemotePlayback()) {
+ mRoutePlayer.onPause();
+ mCurrentState = STATE_PAUSED;
+ } else if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ mCurrentState = STATE_PAUSED;
+ updatePlaybackState();
}
- mTargetState = STATE_PAUSED;
- if (DEBUG) {
- Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
- + ", mTargetState=" + mTargetState);
- }
+ }
+ mTargetState = STATE_PAUSED;
+ if (DEBUG) {
+ Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
+ + ", mTargetState=" + mTargetState);
}
showController();
}
@Override
public void onSeekTo(long pos) {
- if (isRemotePlayback()) {
- mRouteSessionCallback.onSeekTo(pos);
- } else {
- if (isInPlaybackState()) {
- mMediaPlayer.seekTo(pos, MediaPlayer.SEEK_PREVIOUS_SYNC);
+ if (isInPlaybackState()) {
+ if (isRemotePlayback()) {
+ mRoutePlayer.onSeekTo(pos);
+ } else {
+ mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mSeekWhenPrepared = 0;
updatePlaybackState();
- } else {
- mSeekWhenPrepared = pos;
}
+ } else {
+ mSeekWhenPrepared = pos;
}
showController();
}
@@ -1086,74 +1132,11 @@
@Override
public void onStop() {
if (isRemotePlayback()) {
- mRouteSessionCallback.onStop();
+ mRoutePlayer.onStop();
} else {
resetPlayer();
}
showController();
}
}
-
- private class RouteSessionCallback extends MediaSession.Callback {
- RemotePlaybackClient mClient;
-
- RemotePlaybackClient.StatusCallback mStatusCallback =
- new RemotePlaybackClient.StatusCallback() {
- @Override
- public void onItemStatusChanged(Bundle data,
- String sessionId, MediaSessionStatus sessionStatus,
- String itemId, MediaItemStatus itemStatus) {
- // TODO: implement this
- }
-
- @Override
- public void onSessionStatusChanged(Bundle data,
- String sessionId, MediaSessionStatus sessionStatus) {
- // TODO: implement this
- }
-
- @Override
- public void onSessionChanged(String sessionId) {
- // TODO: implement this
- }
- };
-
- @Override
- public void onCommand(String command, Bundle args, ResultReceiver receiver) {
- ensureClient();
- // TODO: implement this
- }
-
- @Override
- public void onPlay() {
- ensureClient();
- // TODO: implement this
- }
-
- @Override
- public void onPause() {
- ensureClient();
- // TODO: implement this
- }
-
- @Override
- public void onSeekTo(long pos) {
- ensureClient();
- // TODO: implement this
- }
-
- @Override
- public void onStop() {
- ensureClient();
- // TODO: implement this
- }
-
- private void ensureClient() {
- if (mClient == null) {
- mClient = new RemotePlaybackClient(
- mInstance.getContext(), mMediaRouter.getSelectedRoute());
- mClient.setStatusCallback(mStatusCallback);
- }
- }
- }
}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java b/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
index 2a5eb94..854d47e 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
@@ -17,31 +17,31 @@
package com.android.widget;
import android.annotation.NonNull;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
import android.view.View;
interface VideoViewInterface {
/**
- * Assigns the view's surface to the given MediaPlayer instance.
+ * Assigns the view's surface to the given MediaPlayer2 instance.
*
- * @param mp MediaPlayer
+ * @param mp MediaPlayer2
* @return true if the surface is successfully assigned, false if not. It will fail to assign
- * if any of MediaPlayer or surface is unavailable.
+ * if any of MediaPlayer2 or surface is unavailable.
*/
- boolean assignSurfaceToMediaPlayer(MediaPlayer mp);
+ boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp);
void setSurfaceListener(SurfaceListener l);
int getViewType();
- void setMediaPlayer(MediaPlayer mp);
+ void setMediaPlayer(MediaPlayer2 mp);
/**
- * Takes over oldView. It means that the MediaPlayer will start rendering on this view.
+ * Takes over oldView. It means that the MediaPlayer2 will start rendering on this view.
* The visibility of oldView will be set as {@link View.GONE}. If the view doesn't have a
- * MediaPlayer instance or its surface is not available, the actual execution is deferred until
- * a MediaPlayer instance is set by {@link #setMediaPlayer} or its surface becomes available.
+ * MediaPlayer2 instance or its surface is not available, the actual execution is deferred until
+ * a MediaPlayer2 instance is set by {@link #setMediaPlayer} or its surface becomes available.
* {@link SurfaceListener.onSurfaceTakeOverDone} will be called when the actual execution is
* done.
*
- * @param oldView The view that MediaPlayer is currently rendering on.
+ * @param oldView The view that MediaPlayer2 is currently rendering on.
*/
void takeOver(@NonNull VideoViewInterface oldView);
diff --git a/packages/MediaComponents/test/Android.mk b/packages/MediaComponents/test/Android.mk
index 8703b9f..ea053e0 100644
--- a/packages/MediaComponents/test/Android.mk
+++ b/packages/MediaComponents/test/Android.mk
@@ -25,4 +25,5 @@
compatibility-device-util
LOCAL_PACKAGE_NAME := MediaComponentsTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index d1c7717..e58bd02 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -339,7 +339,9 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
if (Process.myUid() == info.getUid()) {
assertEquals(testParentId, parentId);
assertTrue(TestUtils.equals(testExtras, extras));
@@ -361,7 +363,8 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public void onUnsubscribe(ControllerInfo info, String parentId) {
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId) {
if (Process.myUid() == info.getUid()) {
assertEquals(testParentId, parentId);
latch.countDown();
@@ -387,21 +390,29 @@
final CountDownLatch latch = new CountDownLatch(3);
final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
@Override
- public CommandGroup onConnect(ControllerInfo controller) {
- final MockMediaLibraryService2 service = (MockMediaLibraryService2)
- TestServiceRegistry.getInstance().getServiceInstance();
- final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
- // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
- // subscribed.
- session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
- session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
- return super.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ if (Process.myUid() == controller.getUid()) {
+ assertTrue(session instanceof MediaLibrarySession);
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ testParentId1, testChildrenCount, null);
+ ((MediaLibrarySession) session).notifyChildrenChanged(
+ controller, testParentId1, testChildrenCount, null);
+ }
+ return super.onConnect(session, controller);
}
@Override
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId,
+ @Nullable Bundle extras) {
if (Process.myUid() == info.getUid()) {
- final MediaLibrarySession session = (MediaLibrarySession) mSession;
session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
testExtras);
@@ -435,12 +446,6 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
final MediaBrowser2 browser = (MediaBrowser2) createController(
token, true, controllerCallbackProxy);
- final MockMediaLibraryService2 service =
- (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
- if (mSession != null) {
- mSession.close();
- }
- mSession = service.getSession();
assertTrue(mSession instanceof MediaLibrarySession);
browser.subscribe(testParentId2, null);
// This ensures that onChildrenChanged() is only called for the expected reasons.
@@ -494,13 +499,17 @@
mCallbackProxy.onCustomCommand(command, args, receiver);
}
-
@Override
public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
mCallbackProxy.onCustomLayoutChanged(layout);
}
@Override
+ public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+ mCallbackProxy.onAllowedCommandsChanged(commands);
+ }
+
+ @Override
public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints,
String rootMediaId, Bundle rootExtra) {
super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index e6ad098..2237d18 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -32,6 +32,7 @@
import android.os.HandlerThread;
import android.os.Process;
import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -118,8 +119,8 @@
@Ignore
@Test
- public void testSkipToPrevious() throws InterruptedException {
- mController.skipToPrevious();
+ public void testSkipToPreviousItem() throws InterruptedException {
+ mController.skipToPreviousItem();
try {
assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
@@ -128,9 +129,10 @@
assertTrue(mPlayer.mSkipToPreviousCalled);
}
+ @Ignore
@Test
- public void testSkipToNext() throws InterruptedException {
- mController.skipToNext();
+ public void testSkipToNextItem() throws InterruptedException {
+ mController.skipToNextItem();
try {
assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
@@ -211,7 +213,7 @@
} catch (InterruptedException e) {
fail(e.getMessage());
}
- assertTrue(mPlayer.mSetCurrentPlaylistItemCalled);
+ assertTrue(mPlayer.mSetCurrentPlaylistCalled);
assertEquals(itemIndex, mPlayer.mCurrentItem);
}
*/
@@ -622,9 +624,9 @@
player.notifyPlaybackState(state);
controller.stop();
player.notifyPlaybackState(state);
- controller.skipToNext();
+ controller.skipToNextItem();
player.notifyPlaybackState(state);
- controller.skipToPrevious();
+ controller.skipToPreviousItem();
}
// This may hang if deadlock happens.
latch.countDown();
@@ -656,15 +658,6 @@
assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
}
- private void connectToService(SessionToken2 token) throws InterruptedException {
- if (mSession != null) {
- mSession.close();
- }
- mController = createController(token);
- mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
- mPlayer = (MockPlayer) mSession.getPlayer();
- }
-
@Test
public void testConnectToService_sessionService() throws InterruptedException {
testConnectToService(MockMediaSessionService2.ID);
@@ -680,17 +673,24 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
@Override
- public CommandGroup onConnect(ControllerInfo controller) {
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
if (Process.myUid() == controller.getUid()) {
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = session;
+ mPlayer = (MockPlayer) session.getPlayer();
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
latch.countDown();
}
- return super.onConnect(controller);
+ return super.onConnect(session, controller);
}
};
TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
- connectToService(TestUtils.getServiceToken(mContext, id));
+
+ mController = createController(TestUtils.getServiceToken(mContext, id));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Test command from controller to session service
@@ -718,11 +718,14 @@
testControllerAfterSessionIsGone(mSession.getToken().getId());
}
+ // TODO(jaewan): Re-enable this test
@Ignore
@Test
public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+ /*
connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+ */
}
@Test
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 8c1a749..a0b1f18 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -42,7 +42,6 @@
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
@@ -188,19 +187,20 @@
});
}
+ @Ignore
@Test
- public void testSkipToNext() throws Exception {
+ public void testSkipToNextItem() throws Exception {
sHandler.postAndSync(() -> {
- mSession.skipToNext();
+ mSession.skipToNextItem();
assertTrue(mPlayer.mSkipToNextCalled);
});
}
@Ignore
@Test
- public void testSkipToPrevious() throws Exception {
+ public void testSkipToPreviousItem() throws Exception {
sHandler.postAndSync(() -> {
- mSession.skipToPrevious();
+ mSession.skipToPreviousItem();
assertTrue(mPlayer.mSkipToPreviousCalled);
});
}
@@ -356,13 +356,14 @@
assertEquals(1, callback.commands.size());
assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
(long) callback.commands.get(0).getCommandCode());
- controller.skipToNext();
- assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertTrue(mPlayer.mSkipToNextCalled);
- assertFalse(mPlayer.mPauseCalled);
- assertEquals(2, callback.commands.size());
- assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
- (long) callback.commands.get(1).getCommandCode());
+ // TODO(jaewan): uncomment followings once skipToNextItem is implemented (b/74090741)
+// controller.skipToNextItem();
+// assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+// assertTrue(mPlayer.mSkipToNextCalled);
+// assertFalse(mPlayer.mPauseCalled);
+// assertEquals(2, callback.commands.size());
+// assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+// (long) callback.commands.get(1).getCommandCode());
}
@Test
@@ -426,6 +427,38 @@
}
@Test
+ public void testSetAllowedCommands() throws InterruptedException {
+ final CommandGroup commands = new CommandGroup(mContext);
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY));
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE));
+ commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_STOP));
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+ @Override
+ public void onAllowedCommandsChanged(CommandGroup commandsOut) {
+ assertNotNull(commandsOut);
+ List<Command> expected = commands.getCommands();
+ List<Command> actual = commandsOut.getCommands();
+
+ assertNotNull(actual);
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), actual.get(i));
+ }
+ latch.countDown();
+ }
+ };
+
+ final MediaController2 controller = createController(mSession.getToken(), true, callback);
+ ControllerInfo controllerInfo = getTestControllerInfo();
+ assertNotNull(controllerInfo);
+
+ mSession.setAllowedCommands(controllerInfo, commands);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
public void testSendCustomAction() throws InterruptedException {
final Command testCommand =
new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
@@ -457,13 +490,12 @@
private ControllerInfo getTestControllerInfo() {
List<ControllerInfo> controllers = mSession.getConnectedControllers();
assertNotNull(controllers);
- final String packageName = mContext.getPackageName();
for (int i = 0; i < controllers.size(); i++) {
- if (TextUtils.equals(packageName, controllers.get(i).getPackageName())) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
return controllers.get(i);
}
}
- fail("Fails to get custom command");
+ fail("Failed to get test controller info");
return null;
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index b32400f..83706c0 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -69,6 +69,7 @@
default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
default void onPlaybackStateChanged(PlaybackState2 state) {}
default void onCustomLayoutChanged(List<CommandButton> layout) {}
+ default void onAllowedCommandsChanged(CommandGroup commands) {}
default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
}
@@ -247,6 +248,11 @@
public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
mCallbackProxy.onCustomLayoutChanged(layout);
}
+
+ @Override
+ public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+ mCallbackProxy.onAllowedCommandsChanged(commands);
+ }
}
public class TestMediaController extends MediaController2 implements TestControllerInterface {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
index d89cecd..ca36513 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
@@ -16,19 +16,38 @@
package android.media;
-import static android.media.MediaSession2.*;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PLAY;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_REWIND;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_STOP;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI;
+import static android.media.MediaSession2.ControllerInfo;
+import static android.media.MediaSession2.PlaylistParams;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.SessionCallback;
@@ -41,8 +60,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
+
+import java.util.List;
/**
* Tests whether {@link MediaSession2} receives commands that hasn't allowed.
@@ -154,13 +173,13 @@
public void testSkipToNext() throws InterruptedException {
createSessionWithAllowedActions(
createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
- createController(mSession.getToken()).skipToNext();
+ createController(mSession.getToken()).skipToNextItem();
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
createSessionWithAllowedActions(
createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
- createController(mSession.getToken()).skipToNext();
+ createController(mSession.getToken()).skipToNextItem();
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -168,13 +187,13 @@
public void testSkipToPrevious() throws InterruptedException {
createSessionWithAllowedActions(
createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
- createController(mSession.getToken()).skipToPrevious();
+ createController(mSession.getToken()).skipToPreviousItem();
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
createSessionWithAllowedActions(
createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
- createController(mSession.getToken()).skipToPrevious();
+ createController(mSession.getToken()).skipToPreviousItem();
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -260,12 +279,13 @@
@Test
public void testSetVolume() throws InterruptedException {
- createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_SET_VOLUME));
+ createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_VOLUME));
createController(mSession.getToken()).setVolumeTo(0, 0);
verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
- matchesSession(), matchesCaller(), matches(COMMAND_CODE_SET_VOLUME));
+ matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SET_VOLUME));
- createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_SET_VOLUME));
+ createSessionWithAllowedActions(
+ createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_VOLUME));
createController(mSession.getToken()).setVolumeTo(0, 0);
verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
}
@@ -362,4 +382,43 @@
verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
any(), any(), any(), any());
}
+
+ @Test
+ public void testChangingPermissionWithSetAllowedCommands() throws InterruptedException {
+ final String query = "testChangingPermissionWithSetAllowedCommands";
+ createSessionWithAllowedActions(
+ createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_SEARCH));
+
+ TestControllerCallbackInterface controllerCallback =
+ mock(TestControllerCallbackInterface.class);
+ MediaController2 controller =
+ createController(mSession.getToken(), true, controllerCallback);
+
+ controller.prepareFromSearch(query, null);
+ verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromSearch(
+ matchesSession(), matchesCaller(), eq(query), isNull());
+ clearInvocations(mCallback);
+
+ // Change allowed commands.
+ mSession.setAllowedCommands(getTestControllerInfo(),
+ createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_SEARCH));
+ verify(controllerCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onAllowedCommandsChanged(any());
+
+ controller.prepareFromSearch(query, null);
+ verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
+ any(), any(), any(), any());
+ }
+
+ private ControllerInfo getTestControllerInfo() {
+ List<ControllerInfo> controllers = mSession.getConnectedControllers();
+ assertNotNull(controllers);
+ for (int i = 0; i < controllers.size(); i++) {
+ if (Process.myUid() == controllers.get(i).getUid()) {
+ return controllers.get(i);
+ }
+ }
+ fail("Failed to get test controller info");
+ return null;
+ }
}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index fb02f7a..df516c5 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -27,6 +27,8 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import java.io.FileDescriptor;
@@ -96,8 +98,8 @@
@Override
public void onCreate() {
- super.onCreate();
TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
}
@Override
@@ -144,9 +146,9 @@
}
@Override
- public CommandGroup onConnect(MediaSession2 session,
- ControllerInfo controller) {
- return mCallbackProxy.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ return mCallbackProxy.onConnect(session, controller);
}
@Override
@@ -211,15 +213,16 @@
}
@Override
- public void onSubscribe(MediaLibrarySession session, ControllerInfo controller,
- String parentId, Bundle extras) {
- mCallbackProxy.onSubscribe(controller, parentId, extras);
+ public void onSubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo controller, @NonNull String parentId,
+ @Nullable Bundle extras) {
+ mCallbackProxy.onSubscribe(session, controller, parentId, extras);
}
@Override
- public void onUnsubscribe(MediaLibrarySession session, ControllerInfo controller,
- String parentId) {
- mCallbackProxy.onUnsubscribe(controller, parentId);
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribe(session, controller, parentId);
}
}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index ce7ce8b..64b1acd 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -27,6 +27,7 @@
import android.media.MediaSession2.SessionCallback;
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
+import android.support.annotation.NonNull;
import java.util.concurrent.Executor;
@@ -46,8 +47,8 @@
@Override
public void onCreate() {
- super.onCreate();
TestServiceRegistry.getInstance().setServiceInstance(this);
+ super.onCreate();
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
@@ -103,9 +104,9 @@
}
@Override
- public CommandGroup onConnect(MediaSession2 session,
- ControllerInfo controller) {
- return mCallbackProxy.onConnect(controller);
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
+ return mCallbackProxy.onConnect(session, controller);
}
}
}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 08e0cf0..27b6f89 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
@@ -26,6 +27,8 @@
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
/**
* Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
@@ -36,7 +39,7 @@
public class TestServiceRegistry {
/**
* Proxy for both {@link MediaSession2.SessionCallback} and
- * {@link MediaLibraryService2.MediaLibrarySessionCallback}.
+ * {@link MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback}.
*/
public static abstract class SessionCallbackProxy {
private final Context mContext;
@@ -56,7 +59,8 @@
* @param controller
* @return
*/
- public CommandGroup onConnect(ControllerInfo controller) {
+ public CommandGroup onConnect(@NonNull MediaSession2 session,
+ @NonNull ControllerInfo controller) {
if (Process.myUid() == controller.getUid()) {
CommandGroup commands = new CommandGroup(mContext);
commands.addAllPredefinedCommands();
@@ -75,8 +79,10 @@
*/
public void onServiceDestroyed() { }
- public void onSubscribe(ControllerInfo info, String parentId, Bundle extra) { }
- public void onUnsubscribe(ControllerInfo info, String parentId) { }
+ public void onSubscribe(@NonNull MediaLibrarySession session, @NonNull ControllerInfo info,
+ @NonNull String parentId, @Nullable Bundle extra) { }
+ public void onUnsubscribe(@NonNull MediaLibrarySession session,
+ @NonNull ControllerInfo info, @NonNull String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index bc12f39..e3bb5dc 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -364,7 +364,7 @@
// queue, no onBufferReleased is called by the buffer queue.
// Proactively trigger the callback to avoid buffer loss.
if (queueOutput.bufferReplaced) {
- onBufferReleasedByOutputLocked(output, surfaceId);
+ onBufferReplacedLocked(output, surfaceId);
}
return res;
@@ -582,7 +582,16 @@
void Camera3StreamSplitter::onBufferReleasedByOutput(
const sp<IGraphicBufferProducer>& from) {
ATRACE_CALL();
+ sp<Fence> fence;
+
+ int slot = BufferItem::INVALID_BUFFER_SLOT;
+ auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage,
+ nullptr, nullptr);
Mutex::Autolock lock(mMutex);
+ handleOutputDequeueStatusLocked(res, slot);
+ if (res != OK) {
+ return;
+ }
size_t surfaceId = 0;
bool found = false;
@@ -598,61 +607,47 @@
return;
}
- onBufferReleasedByOutputLocked(from, surfaceId);
+ returnOutputBufferLocked(fence, from, surfaceId, slot);
}
-void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
+void Camera3StreamSplitter::onBufferReplacedLocked(
const sp<IGraphicBufferProducer>& from, size_t surfaceId) {
ATRACE_CALL();
- sp<GraphicBuffer> buffer;
sp<Fence> fence;
- if (mOutputSlots[from] == nullptr) {
- //Output surface got likely removed by client.
- return;
- }
- auto outputSlots = *mOutputSlots[from];
int slot = BufferItem::INVALID_BUFFER_SLOT;
auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage,
nullptr, nullptr);
- if (res == NO_INIT) {
- // If we just discovered that this output has been abandoned, note that,
- // but we can't do anything else, since buffer is invalid
- onAbandonedLocked();
- return;
- } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
- SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
- SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
- return;
- } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
- SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
- SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
- return;
- } else if (res == NO_MEMORY) {
- SP_LOGE("%s: No free buffers", __FUNCTION__);
- return;
- } else if (res == WOULD_BLOCK) {
- SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
- return;
- } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
- SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
+ handleOutputDequeueStatusLocked(res, slot);
+ if (res != OK) {
return;
}
- buffer = outputSlots[slot];
+ returnOutputBufferLocked(fence, from, surfaceId, slot);
+}
+
+void Camera3StreamSplitter::returnOutputBufferLocked(const sp<Fence>& fence,
+ const sp<IGraphicBufferProducer>& from, size_t surfaceId, int slot) {
+ sp<GraphicBuffer> buffer;
+
+ if (mOutputSlots[from] == nullptr) {
+ //Output surface got likely removed by client.
+ return;
+ }
+
+ auto outputSlots = *mOutputSlots[from];
+ buffer = outputSlots[slot];
BufferTracker& tracker = *(mBuffers[buffer->getId()]);
// Merge the release fence of the incoming buffer so that the fence we send
// back to the input includes all of the outputs' fences
if (fence != nullptr && fence->isValid()) {
tracker.mergeFence(fence);
}
- SP_LOGV("%s: dequeued buffer %" PRId64 " %p from output %p", __FUNCTION__,
- buffer->getId(), buffer.get(), from.get());
auto detachBuffer = mDetachedBuffers.find(buffer->getId());
bool detach = (detachBuffer != mDetachedBuffers.end());
if (detach) {
- res = from->detachBuffer(slot);
+ auto res = from->detachBuffer(slot);
if (res == NO_ERROR) {
outputSlots[slot] = nullptr;
} else {
@@ -664,6 +659,26 @@
decrementBufRefCountLocked(buffer->getId(), surfaceId);
}
+void Camera3StreamSplitter::handleOutputDequeueStatusLocked(status_t res, int slot) {
+ if (res == NO_INIT) {
+ // If we just discovered that this output has been abandoned, note that,
+ // but we can't do anything else, since buffer is invalid
+ onAbandonedLocked();
+ } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+ SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+ } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+ SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
+ SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+ } else if (res == NO_MEMORY) {
+ SP_LOGE("%s: No free buffers", __FUNCTION__);
+ } else if (res == WOULD_BLOCK) {
+ SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
+ } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
+ SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
+ }
+}
+
void Camera3StreamSplitter::onAbandonedLocked() {
// If this is called from binderDied callback, it means the app process
// holding the binder has died. CameraService will be notified of the binder
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 76a5b7d..fea1bdb 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -122,10 +122,8 @@
// onFrameAvailable call to proceed.
void onBufferReleasedByOutput(const sp<IGraphicBufferProducer>& from);
- // This is the implementation of onBufferReleasedByOutput without the mutex locked.
- // It could either be called from onBufferReleasedByOutput or from
- // onFrameAvailable when a buffer in the async buffer queue is overwritten.
- void onBufferReleasedByOutputLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
+ // Called by outputBufferLocked when a buffer in the async buffer queue got replaced.
+ void onBufferReplacedLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
// When this is called, the splitter disconnects from (i.e., abandons) its
// input queue and signals any waiting onFrameAvailable calls to wake up.
@@ -138,6 +136,13 @@
// 0, return the buffer back to the input BufferQueue.
void decrementBufRefCountLocked(uint64_t id, size_t surfaceId);
+ // Check for and handle any output surface dequeue errors.
+ void handleOutputDequeueStatusLocked(status_t res, int slot);
+
+ // Handles released output surface buffers.
+ void returnOutputBufferLocked(const sp<Fence>& fence, const sp<IGraphicBufferProducer>& from,
+ size_t surfaceId, int slot);
+
// This is a thin wrapper class that lets us determine which BufferQueue
// the IProducerListener::onBufferReleased callback is associated with. We
// create one of these per output BufferQueue, and then pass the producer
diff --git a/services/codec2/Android.bp b/services/codec2/Android.bp
deleted file mode 100644
index 1d5d205..0000000
--- a/services/codec2/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-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"],
- },
- },
-
- compile_multilib: "32",
-}
-
diff --git a/services/codec2/Android.mk b/services/codec2/Android.mk
deleted file mode 100644
index fa49875..0000000
--- a/services/codec2/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-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
deleted file mode 100644
index d5871d1..0000000
--- a/services/codec2/seccomp_policy/codec2.system.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# 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
deleted file mode 100644
index 20c7625..0000000
--- a/services/codec2/seccomp_policy/codec2.system.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# 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
deleted file mode 100644
index d5871d1..0000000
--- a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# 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
deleted file mode 100644
index 20c7625..0000000
--- a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# 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
deleted file mode 100644
index d6ec644..0000000
--- a/services/codec2/system.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 3993eba..0000000
--- a/services/codec2/vendor.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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"
-
-// TODO: Replace this with vendor's implementation
-#include <C2PlatformSupport.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.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";
-
-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(
- android::GetCodec2PlatformComponentStore());
- 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
deleted file mode 100644
index 0577a1d..0000000
--- a/services/codec2/vendor.google.media.c2@1.0-service-system.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-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
deleted file mode 100644
index 3e7e0a6..0000000
--- a/services/codec2/vendor.google.media.c2@1.0-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-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
-