diff options
42 files changed, 3138 insertions, 215 deletions
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index fdd0610..6c3e233 100644 --- a/camera/CameraMetadata.cpp +++ b/camera/CameraMetadata.cpp @@ -23,19 +23,22 @@ namespace android { CameraMetadata::CameraMetadata() : - mBuffer(NULL) { + mBuffer(NULL), mLocked(false) { } -CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) +CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) : + mLocked(false) { mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity); } -CameraMetadata::CameraMetadata(const CameraMetadata &other) { +CameraMetadata::CameraMetadata(const CameraMetadata &other) : + mLocked(false) { mBuffer = clone_camera_metadata(other.mBuffer); } -CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : mBuffer(NULL) { +CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : + mBuffer(NULL), mLocked(false) { acquire(buffer); } @@ -44,6 +47,11 @@ CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) { } CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: Assignment to a locked CameraMetadata!", __FUNCTION__); + return *this; + } + if (CC_LIKELY(buffer != mBuffer)) { camera_metadata_t *newBuffer = clone_camera_metadata(buffer); clear(); @@ -53,16 +61,44 @@ CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { } CameraMetadata::~CameraMetadata() { + mLocked = false; clear(); } +const camera_metadata_t* CameraMetadata::getAndLock() { + mLocked = true; + return mBuffer; +} + +status_t CameraMetadata::unlock(const camera_metadata_t *buffer) { + if (!mLocked) { + ALOGE("%s: Can't unlock a non-locked CameraMetadata!", __FUNCTION__); + return INVALID_OPERATION; + } + if (buffer != mBuffer) { + ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!", + __FUNCTION__); + return BAD_VALUE; + } + mLocked = false; + return OK; +} + camera_metadata_t* CameraMetadata::release() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return NULL; + } camera_metadata_t *released = mBuffer; mBuffer = NULL; return released; } void CameraMetadata::clear() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } if (mBuffer) { free_camera_metadata(mBuffer); mBuffer = NULL; @@ -70,15 +106,27 @@ void CameraMetadata::clear() { } void CameraMetadata::acquire(camera_metadata_t *buffer) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } clear(); mBuffer = buffer; } void CameraMetadata::acquire(CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return; + } acquire(other.release()); } status_t CameraMetadata::append(const CameraMetadata &other) { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } return append_camera_metadata(mBuffer, other.mBuffer); } @@ -92,6 +140,10 @@ bool CameraMetadata::isEmpty() const { } status_t CameraMetadata::sort() { + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } return sort_camera_metadata(mBuffer); } @@ -115,69 +167,101 @@ status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) { status_t CameraMetadata::update(uint32_t tag, const int32_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_INT32)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const uint8_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_BYTE)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const float *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_FLOAT)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const int64_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_INT64)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const double *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const camera_metadata_rational_t *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) { return res; } - return update(tag, (const void*)data, data_count); + return updateImpl(tag, (const void*)data, data_count); } status_t CameraMetadata::update(uint32_t tag, const String8 &string) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } if ( (res = checkType(tag, TYPE_BYTE)) != OK) { return res; } - return update(tag, (const void*)string.string(), string.size()); + return updateImpl(tag, (const void*)string.string(), string.size()); } -status_t CameraMetadata::update(uint32_t tag, const void *data, +status_t CameraMetadata::updateImpl(uint32_t tag, const void *data, size_t data_count) { status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } int type = get_camera_metadata_tag_type(tag); if (type == -1) { ALOGE("%s: Tag %d not found", __FUNCTION__, tag); @@ -216,6 +300,11 @@ bool CameraMetadata::exists(uint32_t tag) const { camera_metadata_entry_t CameraMetadata::find(uint32_t tag) { status_t res; camera_metadata_entry entry; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + entry.count = 0; + return entry; + } res = find_camera_metadata_entry(mBuffer, tag, &entry); if (CC_UNLIKELY( res != OK )) { entry.count = 0; @@ -238,6 +327,10 @@ camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const { status_t CameraMetadata::erase(uint32_t tag) { camera_metadata_entry_t entry; status_t res; + if (mLocked) { + ALOGE("%s: CameraMetadata is locked", __FUNCTION__); + return INVALID_OPERATION; + } res = find_camera_metadata_entry(mBuffer, tag, &entry); if (res == NAME_NOT_FOUND) { return OK; diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index 396b009..fec5461 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -103,7 +103,7 @@ void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) { { Mutex::Autolock al(mWaitMutex); mMetadataReady = true; - mLatestMetadata = tmp; + mLatestMetadata = tmp; // make copy mWaitCondition.broadcast(); } @@ -312,8 +312,6 @@ void ProCamera::onFrameAvailable(int streamId) { sp<ProCameraListener> listener = mListener; StreamInfo& stream = getStreamInfo(streamId); - CpuConsumer::LockedBuffer buf; - if (listener.get() != NULL) { listener->onFrameAvailable(streamId, stream.cpuConsumer); } @@ -421,7 +419,7 @@ CameraMetadata ProCamera::consumeFrameMetadata() { // Destructive: Subsequent calls return empty metadatas CameraMetadata tmp = mLatestMetadata; - mLatestMetadata.release(); + mLatestMetadata.clear(); return tmp; } diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index ecc0854..2b5f3ad 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -587,14 +587,19 @@ TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) { sp<ServiceListener> listener = new ServiceListener(); EXPECT_OK(ProCamera::addServiceListener(listener)); - ServiceListener::Status currentStatus = ServiceListener::STATUS_AVAILABLE; + ServiceListener::Status currentStatus; + + // when subscribing a new listener, + // we immediately get a callback to the current status + while (listener->waitForStatusChange(/*out*/currentStatus) != OK); + EXPECT_EQ(ServiceListener::STATUS_PRESENT, currentStatus); dout << "Will now stream and resume infinitely..." << std::endl; while (true) { - if (currentStatus == ServiceListener::STATUS_AVAILABLE) { + if (currentStatus == ServiceListener::STATUS_PRESENT) { - EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, + ASSERT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, surface, &depthStreamId)); EXPECT_NE(-1, depthStreamId); @@ -613,12 +618,15 @@ TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) { while (listener->waitForStatusChange(/*out*/stat) != OK); if (currentStatus != stat) { - if (stat == ServiceListener::STATUS_AVAILABLE) { + if (stat == ServiceListener::STATUS_PRESENT) { dout << "Reconnecting to camera" << std::endl; mCamera = ProCamera::connect(CAMERA_ID); } else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) { dout << "Disconnecting from camera" << std::endl; mCamera->disconnect(); + } else if (stat == ServiceListener::STATUS_NOT_PRESENT) { + dout << "Camera unplugged" << std::endl; + mCamera = NULL; } else { dout << "Unknown status change " << std::hex << stat << std::endl; @@ -1021,6 +1029,9 @@ TEST_F(ProCameraTest, WaitForDualStreamBuffer) { // Consume two frames simultaneously. Unsynchronized by timestamps. for (int i = 0; i < REQUEST_COUNT; ++i) { + // Exhaust event queue so it doesn't keep growing + while (mListener->ReadEvent() != UNKNOWN); + // Get the metadata EXPECT_OK(mCamera->waitForFrameMetadata()); CameraMetadata meta = mCamera->consumeFrameMetadata(); @@ -1216,7 +1227,7 @@ TEST_F(ProCameraTest, ServiceListenersFunctional) { } EXPECT_OK(listener->waitForStatusChange(/*out*/stat)); - EXPECT_EQ(ServiceListener::STATUS_AVAILABLE, stat); + EXPECT_EQ(ServiceListener::STATUS_PRESENT, stat); EXPECT_OK(ProCamera::removeServiceListener(listener)); } diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h index 4289126..8eeb2e7 100644 --- a/include/camera/CameraMetadata.h +++ b/include/camera/CameraMetadata.h @@ -49,6 +49,23 @@ class CameraMetadata { CameraMetadata &operator=(const camera_metadata_t *buffer); /** + * Get reference to the underlying metadata buffer. Ownership remains with + * the CameraMetadata object, but non-const CameraMetadata methods will not + * work until unlock() is called. Note that the lock has nothing to do with + * thread-safety, it simply prevents the camera_metadata_t pointer returned + * here from being accidentally invalidated by CameraMetadata operations. + */ + const camera_metadata_t* getAndLock(); + + /** + * Unlock the CameraMetadata for use again. After this unlock, the pointer + * given from getAndLock() may no longer be used. The pointer passed out + * from getAndLock must be provided to guarantee that the right object is + * being unlocked. + */ + status_t unlock(const camera_metadata_t *buffer); + + /** * Release a raw metadata buffer to the caller. After this call, * CameraMetadata no longer references the buffer, and the caller takes * responsibility for freeing the raw metadata buffer (using @@ -154,6 +171,7 @@ class CameraMetadata { private: camera_metadata_t *mBuffer; + bool mLocked; /** * Check if tag has a given type @@ -163,7 +181,7 @@ class CameraMetadata { /** * Base update entry method */ - status_t update(uint32_t tag, const void *data, size_t data_count); + status_t updateImpl(uint32_t tag, const void *data, size_t data_count); /** * Resize metadata buffer if needed by reallocating it and copying it over. diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h index 88860dd..f2a11c2 100644 --- a/include/camera/ICameraServiceListener.h +++ b/include/camera/ICameraServiceListener.h @@ -38,9 +38,8 @@ public: * NOT_PRESENT -> PRESENT * NOT_PRESENT -> ENUMERATING * ENUMERATING -> PRESENT - * PRESENT -> AVAILABLE - * AVAILABLE -> NOT_AVAILABLE - * NOT_AVAILABLE -> AVAILABLE + * PRESENT -> NOT_AVAILABLE + * NOT_AVAILABLE -> PRESENT * * A state will never immediately transition back to itself. */ @@ -48,15 +47,17 @@ public: // Device physically unplugged STATUS_NOT_PRESENT = CAMERA_DEVICE_STATUS_NOT_PRESENT, // Device physically has been plugged in + // and the camera can be used exlusively STATUS_PRESENT = CAMERA_DEVICE_STATUS_PRESENT, // Device physically has been plugged in // but it will not be connect-able until enumeration is complete STATUS_ENUMERATING = CAMERA_DEVICE_STATUS_ENUMERATING, // Camera can be used exclusively - STATUS_AVAILABLE = 0x80000000, + STATUS_AVAILABLE = STATUS_PRESENT, // deprecated, will be removed + // Camera is in use by another app and cannot be used exclusively - STATUS_NOT_AVAILABLE, + STATUS_NOT_AVAILABLE = 0x80000000, // Use to initialize variables only STATUS_UNKNOWN = 0xFFFFFFFF, diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 0529bcd..2183fbe 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -271,6 +271,7 @@ private: float mVolume; // Volume applied to audio track audio_stream_type_t mStreamType; // Audio stream used for output unsigned int mProcessSize; // Size of audio blocks generated at a time by audioCallback() (in PCM frames). + struct timespec mStartTime; // tone start time: needed to guaranty actual tone duration bool initAudioTrack(); static void audioCallback(int event, void* user, void *info); diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 58d495e..f09ce75 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -922,6 +922,9 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { ALOGV("Immediate start, time %d", (unsigned int)(systemTime()/1000000)); lResult = true; mState = TONE_STARTING; + if (clock_gettime(CLOCK_MONOTONIC, &mStartTime) != 0) { + mStartTime.tv_sec = 0; + } mLock.unlock(); mpAudioTrack->start(); mLock.lock(); @@ -940,6 +943,7 @@ bool ToneGenerator::startTone(tone_type toneType, int durationMs) { } else { ALOGV("Delayed start"); mState = TONE_RESTARTING; + mStartTime.tv_sec = 0; lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3)); if (lStatus == NO_ERROR) { if (mState != TONE_IDLE) { @@ -976,21 +980,49 @@ void ToneGenerator::stopTone() { ALOGV("stopTone"); mLock.lock(); - if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { - mState = TONE_STOPPING; + if (mState != TONE_IDLE && mState != TONE_INIT) { + if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) { + struct timespec stopTime; + // If the start time is valid, make sure that the number of audio samples produced + // corresponds at least to the time between the start and stop commands. + // This is needed in case of cold start of the output stream. + if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) { + time_t sec = stopTime.tv_sec - mStartTime.tv_sec; + long nsec = stopTime.tv_nsec - mStartTime.tv_nsec; + long durationMs; + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + + if ((sec + 1) > ((long)(INT_MAX / mSamplingRate))) { + mMaxSmp = sec * mSamplingRate; + } else { + // mSamplingRate is always > 1000 + sec = sec * 1000 + nsec / 1000000; // duration in milliseconds + mMaxSmp = (unsigned int)(((int64_t)sec * mSamplingRate) / 1000); + } + ALOGV("stopTone() forcing mMaxSmp to %d, total for far %d", mMaxSmp, mTotalSmp); + } else { + mState = TONE_STOPPING; + } + } ALOGV("waiting cond"); status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3)); if (lStatus == NO_ERROR) { + // If the tone was restarted exit now before calling clearWaveGens(); + if (mState != TONE_INIT) { + return; + } ALOGV("track stop complete, time %d", (unsigned int)(systemTime()/1000000)); } else { ALOGE("--- Stop timed out"); mState = TONE_IDLE; mpAudioTrack->stop(); } + clearWaveGens(); } - clearWaveGens(); - mLock.unlock(); } @@ -1258,6 +1290,9 @@ audioCallback_EndLoop: ALOGV("Cbk restarting track"); if (lpToneGen->prepareWave()) { lpToneGen->mState = TONE_STARTING; + if (clock_gettime(CLOCK_MONOTONIC, &lpToneGen->mStartTime) != 0) { + lpToneGen->mStartTime.tv_sec = 0; + } // must reload lpToneDesc as prepareWave() may change mpToneDesc lpToneDesc = lpToneGen->mpToneDesc; } else { @@ -1299,7 +1334,7 @@ audioCallback_EndLoop: } if (lSignal) - lpToneGen->mWaitCbkCond.signal(); + lpToneGen->mWaitCbkCond.broadcast(); lpToneGen->mLock.unlock(); } } diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp index 1fb8b1a..90aed39 100644 --- a/media/libmediaplayerservice/MediaPlayerFactory.cpp +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -206,7 +206,8 @@ class NuPlayerFactory : public MediaPlayerFactory::IFactory { return 0.0; if (!strncasecmp("http://", url, 7) - || !strncasecmp("https://", url, 8)) { + || !strncasecmp("https://", url, 8) + || !strncasecmp("file://", url, 7)) { size_t len = strlen(url); if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { return kOurScore; diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index c2c9985..095d5ca 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -70,7 +70,8 @@ StagefrightRecorder::StagefrightRecorder() mOutputFd(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false), mSurfaceMediaSource(NULL) { + mStarted(false), mSurfaceMediaSource(NULL), + mCaptureTimeLapse(false) { ALOGV("Constructor"); reset(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 5387e1a..46d0a5a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -161,7 +161,8 @@ void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) { static bool IsHTTPLiveURL(const char *url) { if (!strncasecmp("http://", url, 7) - || !strncasecmp("https://", url, 8)) { + || !strncasecmp("https://", url, 8) + || !strncasecmp("file://", url, 7)) { size_t len = strlen(url); if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { return true; @@ -833,14 +834,6 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { (*decoder)->configure(format); - int64_t durationUs; - if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { - sp<NuPlayerDriver> driver = mDriver.promote(); - if (driver != NULL) { - driver->notifyDuration(durationUs); - } - } - return OK; } @@ -1271,6 +1264,14 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { if (driver != NULL) { driver->notifyPrepareCompleted(err); } + + int64_t durationUs; + if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { + sp<NuPlayerDriver> driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyDuration(durationUs); + } + } break; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 1a2eeb1..ff72b71 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -3416,6 +3416,21 @@ bool ACodec::LoadedToIdleState::onMessageReceived(const sp<AMessage> &msg) { return true; } + case kWhatResume: + { + // We'll be active soon enough. + return true; + } + + case kWhatFlush: + { + // We haven't even started yet, so we're flushed alright... + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatFlushCompleted); + notify->post(); + return true; + } + default: return BaseState::onMessageReceived(msg); } @@ -3461,6 +3476,22 @@ bool ACodec::IdleToExecutingState::onMessageReceived(const sp<AMessage> &msg) { return true; } + case kWhatResume: + { + // We'll be active soon enough. + return true; + } + + case kWhatFlush: + { + // We haven't even started yet, so we're flushed alright... + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatFlushCompleted); + notify->post(); + + return true; + } + case kWhatSignalEndOfInputStream: { mCodec->onSignalEndOfInputStream(); diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp index 7719435..5749733 100644 --- a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp @@ -481,7 +481,7 @@ void SoftAACEncoder2::onQueueFilled(OMX_U32 portIndex) { void* inBuffer[] = { (unsigned char *)mInputFrame }; INT inBufferIds[] = { IN_AUDIO_DATA }; - INT inBufferSize[] = { numBytesPerInputFrame }; + INT inBufferSize[] = { (INT)numBytesPerInputFrame }; INT inBufferElSize[] = { sizeof(int16_t) }; AACENC_BufDesc inBufDesc; diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp index 07f8b4f..50b739c 100644 --- a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp +++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp @@ -257,7 +257,7 @@ OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter( } if (pcmParams->nChannels != 1 - || pcmParams->nSamplingRate != kSampleRate) { + || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) { return OMX_ErrorUndefined; } diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp index cc38dc3..e25637a 100644 --- a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -655,7 +655,8 @@ void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) { vpx_codec_iter_t encoded_packet_iterator = NULL; const vpx_codec_cx_pkt_t* encoded_packet; - while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) { + while ((encoded_packet = vpx_codec_get_cx_data( + mCodecContext, &encoded_packet_iterator))) { if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) { outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts; outputBufferHeader->nFlags = 0; diff --git a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c index 53b2fd8..cc838fd 100755 --- a/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c +++ b/media/libstagefright/codecs/on2/h264dec/source/h264bsd_util.c @@ -220,7 +220,7 @@ u32 h264bsdNextMbAddress(u32 *pSliceGroupMap, u32 picSizeInMbs, u32 currMbAddr) /* Variables */ - u32 i, sliceGroup, tmp; + u32 i, sliceGroup; /* Code */ @@ -231,11 +231,9 @@ u32 h264bsdNextMbAddress(u32 *pSliceGroupMap, u32 picSizeInMbs, u32 currMbAddr) sliceGroup = pSliceGroupMap[currMbAddr]; i = currMbAddr + 1; - tmp = pSliceGroupMap[i]; - while ((i < picSizeInMbs) && (tmp != sliceGroup)) + while ((i < picSizeInMbs) && (pSliceGroupMap[i] != sliceGroup)) { i++; - tmp = pSliceGroupMap[i]; } if (i == picSizeInMbs) diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 962b01c..505bdb3 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -631,22 +631,20 @@ rinse_repeat: if (index < mPlaylist->size()) { int32_t newSeqNumber = firstSeqNumberInPlaylist + index; - if (newSeqNumber != mSeqNumber) { - ALOGI("seeking to seq no %d", newSeqNumber); + ALOGI("seeking to seq no %d", newSeqNumber); - mSeqNumber = newSeqNumber; + mSeqNumber = newSeqNumber; - mDataSource->reset(); + mDataSource->reset(); - // reseting the data source will have had the - // side effect of discarding any previously queued - // bandwidth change discontinuity. - // Therefore we'll need to treat these seek - // discontinuities as involving a bandwidth change - // even if they aren't directly. - seekDiscontinuity = true; - bandwidthChanged = true; - } + // reseting the data source will have had the + // side effect of discarding any previously queued + // bandwidth change discontinuity. + // Therefore we'll need to treat these seek + // discontinuities as involving a bandwidth change + // even if they aren't directly. + seekDiscontinuity = true; + bandwidthChanged = true; } } diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 7fc7037..b304749 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -263,8 +263,8 @@ void BlockIterator::advance_l() { mCluster, nextCluster, pos, len); ALOGV("ParseNext returned %ld", res); - if (res > 0) { - // EOF + if (res != 0) { + // EOF or error mCluster = NULL; break; diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 88ca1cc..f074438 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -451,24 +451,6 @@ status_t ANetworkSession::Session::writeMore() { const Fragment &frag = *mOutFragments.begin(); const sp<ABuffer> &datagram = frag.mBuffer; - uint8_t *data = datagram->data(); - if (data[0] == 0x80 && (data[1] & 0x7f) == 33) { - int64_t nowUs = ALooper::GetNowUs(); - - uint32_t prevRtpTime = U32_AT(&data[4]); - - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime; - - ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0); - - data[4] = rtpTime >> 24; - data[5] = (rtpTime >> 16) & 0xff; - data[6] = (rtpTime >> 8) & 0xff; - data[7] = rtpTime & 0xff; - } - int n; do { n = send(mSocket, datagram->data(), datagram->size(), 0); @@ -874,6 +856,14 @@ status_t ANetworkSession::createClientOrServer( err = -errno; goto bail2; } + + int tos = 224; // VOICE + res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + + if (res < 0) { + err = -errno; + goto bail2; + } } err = MakeSocketNonBlocking(s); diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index f1f9f45..1578c21 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -109,3 +109,25 @@ LOCAL_MODULE:= rtptest LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + nettest.cpp \ + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libgui \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libstagefright_wfd \ + libutils \ + +LOCAL_MODULE:= nettest + +LOCAL_MODULE_TAGS := debug + +include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h index e3fa845..6178f00 100644 --- a/media/libstagefright/wifi-display/rtp/RTPBase.h +++ b/media/libstagefright/wifi-display/rtp/RTPBase.h @@ -25,6 +25,7 @@ struct RTPBase { PACKETIZATION_TRANSPORT_STREAM, PACKETIZATION_H264, PACKETIZATION_AAC, + PACKETIZATION_NONE, }; enum TransportMode { diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 9eeeabd..6bbe650 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -187,6 +187,10 @@ status_t RTPSender::queueBuffer( status_t err; switch (mode) { + case PACKETIZATION_NONE: + err = queueRawPacket(buffer, packetType); + break; + case PACKETIZATION_TRANSPORT_STREAM: err = queueTSPackets(buffer, packetType); break; @@ -202,6 +206,46 @@ status_t RTPSender::queueBuffer( return err; } +status_t RTPSender::queueRawPacket( + const sp<ABuffer> &packet, uint8_t packetType) { + CHECK_LE(packet->size(), kMaxUDPPacketSize - 12); + + int64_t timeUs; + CHECK(packet->meta()->findInt64("timeUs", &timeUs)); + + sp<ABuffer> udpPacket = new ABuffer(12 + packet->size()); + + udpPacket->setInt32Data(mRTPSeqNo); + + uint8_t *rtp = udpPacket->data(); + rtp[0] = 0x80; + rtp[1] = packetType; + + rtp[2] = (mRTPSeqNo >> 8) & 0xff; + rtp[3] = mRTPSeqNo & 0xff; + ++mRTPSeqNo; + + uint32_t rtpTime = (timeUs * 9) / 100ll; + + rtp[4] = rtpTime >> 24; + rtp[5] = (rtpTime >> 16) & 0xff; + rtp[6] = (rtpTime >> 8) & 0xff; + rtp[7] = rtpTime & 0xff; + + rtp[8] = kSourceID >> 24; + rtp[9] = (kSourceID >> 16) & 0xff; + rtp[10] = (kSourceID >> 8) & 0xff; + rtp[11] = kSourceID & 0xff; + + memcpy(&rtp[12], packet->data(), packet->size()); + + return sendRTPPacket( + udpPacket, + true /* storeInHistory */, + true /* timeValid */, + ALooper::GetNowUs()); +} + status_t RTPSender::queueTSPackets( const sp<ABuffer> &tsPackets, uint8_t packetType) { CHECK_EQ(0, tsPackets->size() % 188); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 3a926ea..fefcab7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -94,6 +94,7 @@ private: static uint64_t GetNowNTP(); + status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType); status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType); status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 715d0b5..cacfcca 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -709,8 +709,11 @@ void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp<AMessage> &msg) Converter::GetInt32Property( "media.wfd.video-framerate", -1); - if (rateHz < 0.0) { - rateHz = repeaterSource->getFrameRate(); + char val[PROPERTY_VALUE_MAX]; + if (rateHz < 0.0 + && property_get("media.wfd.video-framerate", val, NULL) + && !strcasecmp("adaptive", val)) { + rateHz = repeaterSource->getFrameRate(); if (avgLatencyUs > 300000ll) { rateHz *= 0.9; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 792a9c5..4a49811 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -273,7 +273,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (!strcasecmp(val, "pause") && mState == PLAYING) { mState = PLAYING_TO_PAUSED; sendTrigger(mClientSessionID, TRIGGER_PAUSE); - } else if (!strcasecmp(val, "play") && mState == PAUSED) { + } else if (!strcasecmp(val, "play") + && mState == PAUSED) { mState = PAUSED_TO_PLAYING; sendTrigger(mClientSessionID, TRIGGER_PLAY); } @@ -422,7 +423,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { NULL /* interlaced */)); mClient->onDisplayConnected( - mClientInfo.mPlaybackSession->getSurfaceTexture(), + mClientInfo.mPlaybackSession + ->getSurfaceTexture(), width, height, mUsingHDCP @@ -1351,6 +1353,15 @@ status_t WifiDisplaySource::onPlayRequest( return ERROR_MALFORMED; } + if (mState != AWAITING_CLIENT_PLAY) { + ALOGW("Received PLAY request but we're in state %d", mState); + + sendErrorResponse( + sessionID, "455 Method Not Valid in This State", cseq); + + return INVALID_OPERATION; + } + ALOGI("Received PLAY request."); if (mPlaybackSessionEstablished) { finishPlay(); @@ -1673,7 +1684,10 @@ void WifiDisplaySource::HDCPObserver::notify( status_t WifiDisplaySource::makeHDCP() { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.player")); - sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + + sp<IMediaPlayerService> service = + interface_cast<IMediaPlayerService>(binder); + CHECK(service != NULL); mHDCP = service->makeHDCP(true /* createEncryptionModule */); diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 8568dfc..df87db4 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -704,7 +704,8 @@ MtpResponseCode MtpServer::doGetObjectInfo() { mData.putUInt32(info.mAssociationDesc); mData.putUInt32(info.mSequenceNumber); mData.putString(info.mName); - mData.putEmptyString(); // date created + formatDateTime(info.mDateCreated, date, sizeof(date)); + mData.putString(date); // date created formatDateTime(info.mDateModified, date, sizeof(date)); mData.putString(date); // date modified mData.putEmptyString(); // keywords diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 74ba59e..d66294c 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -225,12 +225,18 @@ void AudioFlinger::EffectModule::updateState() { 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t)); } - start_l(); - mState = ACTIVE; + if (start_l() == NO_ERROR) { + mState = ACTIVE; + } else { + mState = IDLE; + } break; case STOPPING: - stop_l(); - mDisableWaitCnt = mMaxDisableWaitCnt; + if (stop_l() == NO_ERROR) { + mDisableWaitCnt = mMaxDisableWaitCnt; + } else { + mDisableWaitCnt = 1; // will cause immediate transition to IDLE + } mState = STOPPED; break; case STOPPED: @@ -297,7 +303,7 @@ void AudioFlinger::EffectModule::process() void AudioFlinger::EffectModule::reset_l() { - if (mEffectInterface == NULL) { + if (mStatus != NO_ERROR || mEffectInterface == NULL) { return; } (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL); @@ -305,17 +311,24 @@ void AudioFlinger::EffectModule::reset_l() status_t AudioFlinger::EffectModule::configure() { + status_t status; + sp<ThreadBase> thread; + uint32_t size; + audio_channel_mask_t channelMask; + if (mEffectInterface == NULL) { - return NO_INIT; + status = NO_INIT; + goto exit; } - sp<ThreadBase> thread = mThread.promote(); + thread = mThread.promote(); if (thread == 0) { - return DEAD_OBJECT; + status = DEAD_OBJECT; + goto exit; } // TODO: handle configuration of effects replacing track process - audio_channel_mask_t channelMask = thread->channelMask(); + channelMask = thread->channelMask(); if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO; @@ -357,8 +370,8 @@ status_t AudioFlinger::EffectModule::configure() this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount); status_t cmdStatus; - uint32_t size = sizeof(int); - status_t status = (*mEffectInterface)->command(mEffectInterface, + size = sizeof(int); + status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), &mConfig, @@ -396,6 +409,8 @@ status_t AudioFlinger::EffectModule::configure() mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) / (1000 * mConfig.outputCfg.buffer.frameCount); +exit: + mStatus = status; return status; } @@ -430,6 +445,9 @@ status_t AudioFlinger::EffectModule::start_l() if (mEffectInterface == NULL) { return NO_INIT; } + if (mStatus != NO_ERROR) { + return mStatus; + } status_t cmdStatus; uint32_t size = sizeof(status_t); status_t status = (*mEffectInterface)->command(mEffectInterface, @@ -466,6 +484,9 @@ status_t AudioFlinger::EffectModule::stop_l() if (mEffectInterface == NULL) { return NO_INIT; } + if (mStatus != NO_ERROR) { + return mStatus; + } status_t cmdStatus; uint32_t size = sizeof(status_t); status_t status = (*mEffectInterface)->command(mEffectInterface, @@ -503,6 +524,9 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode, if (mState == DESTROYED || mEffectInterface == NULL) { return NO_INIT; } + if (mStatus != NO_ERROR) { + return mStatus; + } status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, @@ -592,6 +616,10 @@ bool AudioFlinger::EffectModule::isEnabled() const bool AudioFlinger::EffectModule::isProcessEnabled() const { + if (mStatus != NO_ERROR) { + return false; + } + switch (mState) { case RESTART: case ACTIVE: @@ -609,8 +637,10 @@ bool AudioFlinger::EffectModule::isProcessEnabled() const status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller) { Mutex::Autolock _l(mLock); + if (mStatus != NO_ERROR) { + return mStatus; + } status_t status = NO_ERROR; - // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set) if (isProcessEnabled() && @@ -646,6 +676,9 @@ status_t AudioFlinger::EffectModule::setDevice(audio_devices_t device) } Mutex::Autolock _l(mLock); + if (mStatus != NO_ERROR) { + return mStatus; + } status_t status = NO_ERROR; if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) { status_t cmdStatus; @@ -665,6 +698,9 @@ status_t AudioFlinger::EffectModule::setDevice(audio_devices_t device) status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode) { Mutex::Autolock _l(mLock); + if (mStatus != NO_ERROR) { + return mStatus; + } status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) { status_t cmdStatus; @@ -685,6 +721,9 @@ status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode) status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source) { Mutex::Autolock _l(mLock); + if (mStatus != NO_ERROR) { + return mStatus; + } status_t status = NO_ERROR; if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) { uint32_t size = 0; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9d98f0b..47ca100 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -3122,16 +3122,15 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep Vector< sp<Track> > *tracksToRemove ) { - sp<Track> trackToRemove; - + size_t count = mActiveTracks.size(); mixer_state mixerStatus = MIXER_IDLE; // find out which tracks need to be processed - if (mActiveTracks.size() != 0) { - sp<Track> t = mActiveTracks[0].promote(); + for (size_t i = 0; i < count; i++) { + sp<Track> t = mActiveTracks[i].promote(); // The track died recently if (t == 0) { - return MIXER_IDLE; + continue; } Track* const track = t.get(); @@ -3180,35 +3179,40 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } right = v_clamped/MAX_GAIN; } - - if (left != mLeftVolFloat || right != mRightVolFloat) { - mLeftVolFloat = left; - mRightVolFloat = right; - - // Convert volumes from float to 8.24 - uint32_t vl = (uint32_t)(left * (1 << 24)); - uint32_t vr = (uint32_t)(right * (1 << 24)); - - // Delegate volume control to effect in track effect chain if needed - // only one effect chain can be present on DirectOutputThread, so if - // there is one, the track is connected to it - if (!mEffectChains.isEmpty()) { - // Do not ramp volume if volume is controlled by effect - mEffectChains[0]->setVolume_l(&vl, &vr); - left = (float)vl / (1 << 24); - right = (float)vr / (1 << 24); + // Only consider last track started for volume and mixer state control. + // This is the last entry in mActiveTracks unless a track underruns. + // As we only care about the transition phase between two tracks on a + // direct output, it is not a problem to ignore the underrun case. + if (i == (count - 1)) { + if (left != mLeftVolFloat || right != mRightVolFloat) { + mLeftVolFloat = left; + mRightVolFloat = right; + + // Convert volumes from float to 8.24 + uint32_t vl = (uint32_t)(left * (1 << 24)); + uint32_t vr = (uint32_t)(right * (1 << 24)); + + // Delegate volume control to effect in track effect chain if needed + // only one effect chain can be present on DirectOutputThread, so if + // there is one, the track is connected to it + if (!mEffectChains.isEmpty()) { + // Do not ramp volume if volume is controlled by effect + mEffectChains[0]->setVolume_l(&vl, &vr); + left = (float)vl / (1 << 24); + right = (float)vr / (1 << 24); + } + mOutput->stream->set_volume(mOutput->stream, left, right); } - mOutput->stream->set_volume(mOutput->stream, left, right); - } - // reset retry count - track->mRetryCount = kMaxTrackRetriesDirect; - mActiveTrack = t; - mixerStatus = MIXER_TRACKS_READY; + // reset retry count + track->mRetryCount = kMaxTrackRetriesDirect; + mActiveTrack = t; + mixerStatus = MIXER_TRACKS_READY; + } } else { - // clear effect chain input buffer if an active track underruns to avoid sending - // previous audio buffer again to effects - if (!mEffectChains.isEmpty()) { + // clear effect chain input buffer if the last active track started underruns + // to avoid sending previous audio buffer again to effects + if (!mEffectChains.isEmpty() && (i == (count -1))) { mEffectChains[0]->clearInputBuffer(); } @@ -3224,33 +3228,36 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep if (track->isStopped()) { track->reset(); } - trackToRemove = track; + tracksToRemove->add(track); } } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. + // Only consider last track started for mixer state control if (--(track->mRetryCount) <= 0) { ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); - trackToRemove = track; - } else { + tracksToRemove->add(track); + } else if (i == (count -1)){ mixerStatus = MIXER_TRACKS_ENABLED; } } } } - // FIXME merge this with similar code for removing multiple tracks // remove all the tracks that need to be... - if (CC_UNLIKELY(trackToRemove != 0)) { - tracksToRemove->add(trackToRemove); - mActiveTracks.remove(trackToRemove); - if (!mEffectChains.isEmpty()) { - ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(), - trackToRemove->sessionId()); - mEffectChains[0]->decActiveTrackCnt(); - } - if (trackToRemove->isTerminated()) { - removeTrack_l(trackToRemove); + count = tracksToRemove->size(); + if (CC_UNLIKELY(count)) { + for (size_t i = 0 ; i < count ; i++) { + const sp<Track>& track = tracksToRemove->itemAt(i); + mActiveTracks.remove(track); + if (!mEffectChains.isEmpty()) { + ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(), + track->sessionId()); + mEffectChains[0]->decActiveTrackCnt(); + } + if (track->isTerminated()) { + removeTrack_l(track); + } } } diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 8600735..6847bf8 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -25,6 +25,10 @@ LOCAL_SRC_FILES:= \ camera2/JpegCompressor.cpp \ camera2/CaptureSequencer.cpp \ camera2/ProFrameProcessor.cpp \ + camera3/Camera3Stream.cpp \ + camera3/Camera3InputStream.cpp \ + camera3/Camera3OutputStream.cpp \ + camera3/Camera3ZslStream.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp index d3adbdc..9421a77 100644 --- a/services/camera/libcameraservice/Camera2Client.cpp +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -145,9 +145,10 @@ Camera2Client::~Camera2Client() { status_t Camera2Client::dump(int fd, const Vector<String16>& args) { String8 result; - result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", + result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n", mCameraId, getRemoteCallback()->asBinder().get(), + String8(mClientPackageName).string(), mClientPid); result.append(" State: "); #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp index 04a6e6a..f2c8c04 100644 --- a/services/camera/libcameraservice/Camera3Device.cpp +++ b/services/camera/libcameraservice/Camera3Device.cpp @@ -29,13 +29,17 @@ #include <utils/Trace.h> #include <utils/Timers.h> #include "Camera3Device.h" +#include "camera3/Camera3OutputStream.h" -namespace android { +using namespace android::camera3; +namespace android { Camera3Device::Camera3Device(int id): mId(id), - mHal3Device(NULL) + mHal3Device(NULL), + mStatus(STATUS_UNINITIALIZED), + mListener(NULL) { ATRACE_CALL(); camera3_callback_ops::notify = &sNotify; @@ -54,11 +58,17 @@ int Camera3Device::getId() const { return mId; } +/** + * CameraDeviceBase interface + */ + status_t Camera3Device::initialize(camera_module_t *module) { ATRACE_CALL(); + Mutex::Autolock l(mLock); + ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId); - if (mHal3Device != NULL) { + if (mStatus != STATUS_UNINITIALIZED) { ALOGE("%s: Already initialized!", __FUNCTION__); return INVALID_OPERATION; } @@ -76,6 +86,7 @@ status_t Camera3Device::initialize(camera_module_t *module) if (res != OK) { ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, mId, strerror(-res), res); + mStatus = STATUS_ERROR; return res; } @@ -87,6 +98,7 @@ status_t Camera3Device::initialize(camera_module_t *module) __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_3_0, device->common.version); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -99,6 +111,7 @@ status_t Camera3Device::initialize(camera_module_t *module) " and device version (%x).", __FUNCTION__, device->common.version, info.device_version); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -109,6 +122,7 @@ status_t Camera3Device::initialize(camera_module_t *module) ALOGE("%s: Camera %d: Unable to initialize HAL device: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mStatus = STATUS_ERROR; return BAD_VALUE; } @@ -124,18 +138,21 @@ status_t Camera3Device::initialize(camera_module_t *module) ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mStatus = STATUS_ERROR; return res; } } /** Start up request queue thread */ - requestThread = new RequestThread(this); - res = requestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string()); + mRequestThread = new RequestThread(this, device); + res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string()); if (res != OK) { ALOGE("%s: Camera %d: Unable to start request queue thread: %s (%d)", __FUNCTION__, mId, strerror(-res), res); device->common.close(&device->common); + mRequestThread.clear(); + mStatus = STATUS_ERROR; return res; } @@ -143,54 +160,205 @@ status_t Camera3Device::initialize(camera_module_t *module) mDeviceInfo = info.static_camera_characteristics; mHal3Device = device; + mStatus = STATUS_IDLE; + mNextStreamId = 0; return OK; } status_t Camera3Device::disconnect() { ATRACE_CALL(); + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ALOGV("%s: E", __FUNCTION__); + + status_t res; + if (mStatus == STATUS_UNINITIALIZED) return OK; + + if (mStatus == STATUS_ACTIVE || + (mStatus == STATUS_ERROR && mRequestThread != NULL)) { + res = mRequestThread->clearRepeatingRequests(); + if (res != OK) { + ALOGE("%s: Can't stop streaming", __FUNCTION__); + return res; + } + res = waitUntilDrainedLocked(); + if (res != OK) { + ALOGE("%s: Timeout waiting for HAL to drain", __FUNCTION__); + return res; + } + } + assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR); + + if (mRequestThread != NULL) { + mRequestThread->requestExit(); + } + + mOutputStreams.clear(); + mInputStream.clear(); + + if (mRequestThread != NULL) { + mRequestThread->join(); + mRequestThread.clear(); + } + + if (mHal3Device != NULL) { + mHal3Device->common.close(&mHal3Device->common); + mHal3Device = NULL; + } + + mStatus = STATUS_UNINITIALIZED; + + ALOGV("%s: X", __FUNCTION__); + return OK; } status_t Camera3Device::dump(int fd, const Vector<String16> &args) { ATRACE_CALL(); (void)args; + String8 lines; + + const char *status = + mStatus == STATUS_ERROR ? "ERROR" : + mStatus == STATUS_UNINITIALIZED ? "UNINITIALIZED" : + mStatus == STATUS_IDLE ? "IDLE" : + mStatus == STATUS_ACTIVE ? "ACTIVE" : + "Unknown"; + lines.appendFormat(" Device status: %s\n", status); + lines.appendFormat(" Stream configuration:\n"); + + if (mInputStream != NULL) { + write(fd, lines.string(), lines.size()); + mInputStream->dump(fd, args); + } else { + lines.appendFormat(" No input stream.\n"); + write(fd, lines.string(), lines.size()); + } + for (size_t i = 0; i < mOutputStreams.size(); i++) { + mOutputStreams[i]->dump(fd,args); + } - mHal3Device->ops->dump(mHal3Device, fd); + if (mHal3Device != NULL) { + lines = String8(" HAL device dump:\n"); + write(fd, lines.string(), lines.size()); + mHal3Device->ops->dump(mHal3Device, fd); + } return OK; } const CameraMetadata& Camera3Device::info() const { ALOGVV("%s: E", __FUNCTION__); - + if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED || + mStatus == STATUS_ERROR)) { + ALOGE("%s: Access to static info %s!", __FUNCTION__, + mStatus == STATUS_ERROR ? + "when in error state" : "before init"); + } return mDeviceInfo; } status_t Camera3Device::capture(CameraMetadata &request) { ATRACE_CALL(); - (void)request; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + sp<CaptureRequest> newRequest = setUpRequestLocked(request); + if (newRequest == NULL) { + ALOGE("%s: Can't create capture request", __FUNCTION__); + return BAD_VALUE; + } + + return mRequestThread->queueRequest(newRequest); } status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { ATRACE_CALL(); - (void)request; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + sp<CaptureRequest> newRepeatingRequest = setUpRequestLocked(request); + if (newRepeatingRequest == NULL) { + ALOGE("%s: Can't create repeating request", __FUNCTION__); + return BAD_VALUE; + } + + RequestList newRepeatingRequests; + newRepeatingRequests.push_back(newRepeatingRequest); + + return mRequestThread->setRepeatingRequests(newRepeatingRequests); +} + + +sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked( + const CameraMetadata &request) { + status_t res; + + if (mStatus == STATUS_IDLE) { + res = configureStreamsLocked(); + if (res != OK) { + ALOGE("%s: Can't set up streams: %s (%d)", + __FUNCTION__, strerror(-res), res); + return NULL; + } + } + + sp<CaptureRequest> newRequest = createCaptureRequest(request); + return newRequest; } status_t Camera3Device::clearStreamingRequest() { ATRACE_CALL(); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + return mRequestThread->clearRepeatingRequests(); } status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) { @@ -204,11 +372,70 @@ status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time status_t Camera3Device::createStream(sp<ANativeWindow> consumer, uint32_t width, uint32_t height, int format, size_t size, int *id) { ATRACE_CALL(); - (void)consumer; (void)width; (void)height; (void)format; - (void)size; (void)id; + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + status_t res; + bool wasActive = false; + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + // OK + break; + case STATUS_ACTIVE: + ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__); + mRequestThread->setPaused(true); + res = waitUntilDrainedLocked(); + if (res != OK) { + ALOGE("%s: Can't pause captures to reconfigure streams!", + __FUNCTION__); + mStatus = STATUS_ERROR; + return res; + } + wasActive = true; + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } + assert(mStatus == STATUS_IDLE); + + sp<Camera3OutputStream> newStream; + if (format == HAL_PIXEL_FORMAT_BLOB) { + newStream = new Camera3OutputStream(mNextStreamId, consumer, + width, height, size, format); + } else { + newStream = new Camera3OutputStream(mNextStreamId, consumer, + width, height, format); + } + + res = mOutputStreams.add(mNextStreamId, newStream); + if (res < 0) { + ALOGE("%s: Can't add new stream to set: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + *id = mNextStreamId++; + + // Continue captures if active at start + if (wasActive) { + ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__); + res = configureStreamsLocked(); + if (res != OK) { + ALOGE("%s: Can't reconfigure device for new stream %d: %s (%d)", + __FUNCTION__, mNextStreamId, strerror(-res), res); + return res; + } + mRequestThread->setPaused(false); + } + + return OK; } status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) { @@ -223,27 +450,104 @@ status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) { status_t Camera3Device::getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format) { ATRACE_CALL(); - (void)id; (void)width; (void)height; (void)format; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized!", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d is unknown", __FUNCTION__, id); + return idx; + } + + if (width) *width = mOutputStreams[idx]->getWidth(); + if (height) *height = mOutputStreams[idx]->getHeight(); + if (format) *format = mOutputStreams[idx]->getFormat(); + + return OK; } status_t Camera3Device::setStreamTransform(int id, int transform) { ATRACE_CALL(); - (void)id; (void)transform; + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d does not exist", + __FUNCTION__, id); + return BAD_VALUE; + } + + return mOutputStreams.editValueAt(idx)->setTransform(transform); } status_t Camera3Device::deleteStream(int id) { ATRACE_CALL(); - (void)id; + Mutex::Autolock l(mLock); + status_t res; - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + // CameraDevice semantics require device to already be idle before + // deleteStream is called, unlike for createStream. + if (mStatus != STATUS_IDLE) { + ALOGE("%s: Device not idle", __FUNCTION__); + return INVALID_OPERATION; + } + + sp<Camera3Stream> deletedStream; + if (mInputStream != NULL && id == mInputStream->getId()) { + deletedStream = mInputStream; + mInputStream.clear(); + } else { + ssize_t idx = mOutputStreams.indexOfKey(id); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Stream %d does not exist", + __FUNCTION__, id); + return BAD_VALUE; + } + deletedStream = mOutputStreams.editValueAt(idx); + mOutputStreams.removeItem(id); + } + + // Free up the stream endpoint so that it can be used by some other stream + res = deletedStream->disconnect(); + if (res != OK) { + ALOGE("%s: Can't disconnect deleted stream", __FUNCTION__); + // fall through since we want to still list the stream as deleted. + } + mDeletedStreams.add(deletedStream); + + return res; } status_t Camera3Device::deleteReprocessStream(int id) { @@ -259,6 +563,23 @@ status_t Camera3Device::createDefaultRequest(int templateId, CameraMetadata *request) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock l(mLock); + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device is not initialized!", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + case STATUS_ACTIVE: + // OK + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } const camera_metadata_t *rawRequest; rawRequest = mHal3Device->ops->construct_default_request_settings( @@ -271,39 +592,114 @@ status_t Camera3Device::createDefaultRequest(int templateId, status_t Camera3Device::waitUntilDrained() { ATRACE_CALL(); + Mutex::Autolock l(mLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + return waitUntilDrainedLocked(); +} + +status_t Camera3Device::waitUntilDrainedLocked() { + ATRACE_CALL(); + status_t res; + + switch (mStatus) { + case STATUS_UNINITIALIZED: + case STATUS_IDLE: + ALOGV("%s: Already idle", __FUNCTION__); + return OK; + case STATUS_ERROR: + case STATUS_ACTIVE: + // Need to shut down + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } + + if (mRequestThread != NULL) { + res = mRequestThread->waitUntilPaused(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't stop request thread in %f seconds!", + __FUNCTION__, kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + if (mInputStream != NULL) { + res = mInputStream->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't idle input stream %d in %f seconds!", + __FUNCTION__, mInputStream->getId(), kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + for (size_t i = 0; i < mOutputStreams.size(); i++) { + res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout); + if (res != OK) { + ALOGE("%s: Can't idle output stream %d in %f seconds!", + __FUNCTION__, mOutputStreams.keyAt(i), + kShutdownTimeout/1e9); + mStatus = STATUS_ERROR; + return res; + } + } + + if (mStatus != STATUS_ERROR) { + mStatus = STATUS_IDLE; + } + + return OK; } status_t Camera3Device::setNotifyCallback(NotificationListener *listener) { ATRACE_CALL(); - (void)listener; + Mutex::Autolock l(mOutputLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + if (listener != NULL && mListener != NULL) { + ALOGW("%s: Replacing old callback listener", __FUNCTION__); + } + mListener = listener; + + return OK; } status_t Camera3Device::waitForNextFrame(nsecs_t timeout) { - (void)timeout; + ATRACE_CALL(); + status_t res; + Mutex::Autolock l(mOutputLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + while (mResultQueue.empty()) { + res = mResultSignal.waitRelative(mOutputLock, timeout); + if (res == TIMED_OUT) { + return res; + } else if (res != OK) { + ALOGE("%s: Camera %d: Error waiting for frame: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + } + return OK; } status_t Camera3Device::getNextFrame(CameraMetadata *frame) { ATRACE_CALL(); - (void)frame; + Mutex::Autolock l(mOutputLock); - ALOGE("%s: Unimplemented", __FUNCTION__); - return INVALID_OPERATION; + if (mResultQueue.empty()) { + return NOT_ENOUGH_DATA; + } + + CameraMetadata &result = *(mResultQueue.begin()); + frame->acquire(result); + mResultQueue.erase(mResultQueue.begin()); + + return OK; } status_t Camera3Device::triggerAutofocus(uint32_t id) { ATRACE_CALL(); (void)id; - ALOGE("%s: Unimplemented", __FUNCTION__); return INVALID_OPERATION; } @@ -335,27 +731,558 @@ status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId, return INVALID_OPERATION; } -Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent) : - Thread(false), - mParent(parent) { +/** + * Camera3Device private methods + */ + +sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest( + const CameraMetadata &request) { + ATRACE_CALL(); + status_t res; + + sp<CaptureRequest> newRequest = new CaptureRequest; + newRequest->mSettings = request; + + camera_metadata_entry_t inputStreams = + newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS); + if (inputStreams.count > 0) { + if (mInputStream == NULL || + mInputStream->getId() != inputStreams.data.u8[0]) { + ALOGE("%s: Request references unknown input stream %d", + __FUNCTION__, inputStreams.data.u8[0]); + return NULL; + } + // Lazy completion of stream configuration (allocation/registration) + // on first use + if (mInputStream->isConfiguring()) { + res = mInputStream->finishConfiguration(mHal3Device); + if (res != OK) { + ALOGE("%s: Unable to finish configuring input stream %d:" + " %s (%d)", + __FUNCTION__, mInputStream->getId(), + strerror(-res), res); + return NULL; + } + } + + newRequest->mInputStream = mInputStream; + newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS); + } + + camera_metadata_entry_t streams = + newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS); + if (streams.count == 0) { + ALOGE("%s: Zero output streams specified!", __FUNCTION__); + return NULL; + } + + for (size_t i = 0; i < streams.count; i++) { + int idx = mOutputStreams.indexOfKey(streams.data.u8[i]); + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Request references unknown stream %d", + __FUNCTION__, streams.data.u8[i]); + return NULL; + } + sp<Camera3OutputStream> stream = mOutputStreams.editValueAt(idx); + + // Lazy completion of stream configuration (allocation/registration) + // on first use + if (stream->isConfiguring()) { + res = stream->finishConfiguration(mHal3Device); + if (res != OK) { + ALOGE("%s: Unable to finish configuring stream %d: %s (%d)", + __FUNCTION__, stream->getId(), strerror(-res), res); + return NULL; + } + } + + newRequest->mOutputStreams.push(stream); + } + newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS); + + return newRequest; } -bool Camera3Device::RequestThread::threadLoop() { - ALOGE("%s: Unimplemented", __FUNCTION__); +status_t Camera3Device::configureStreamsLocked() { + ATRACE_CALL(); + status_t res; - return false; + if (mStatus != STATUS_IDLE) { + ALOGE("%s: Not idle", __FUNCTION__); + return INVALID_OPERATION; + } + + // Start configuring the streams + + camera3_stream_configuration config; + + config.num_streams = (mInputStream != NULL) + mOutputStreams.size(); + + Vector<camera3_stream_t*> streams; + streams.setCapacity(config.num_streams); + + if (mInputStream != NULL) { + camera3_stream_t *inputStream; + inputStream = mInputStream->startConfiguration(); + if (inputStream == NULL) { + ALOGE("%s: Can't start input stream configuration", + __FUNCTION__); + // TODO: Make sure the error flow here is correct + return INVALID_OPERATION; + } + streams.add(inputStream); + } + + for (size_t i = 0; i < mOutputStreams.size(); i++) { + camera3_stream_t *outputStream; + outputStream = mOutputStreams.editValueAt(i)->startConfiguration(); + if (outputStream == NULL) { + ALOGE("%s: Can't start output stream configuration", + __FUNCTION__); + // TODO: Make sure the error flow here is correct + return INVALID_OPERATION; + } + streams.add(outputStream); + } + + config.streams = streams.editArray(); + + // Do the HAL configuration; will potentially touch stream + // max_buffers, usage, priv fields. + + res = mHal3Device->ops->configure_streams(mHal3Device, &config); + + if (res != OK) { + ALOGE("%s: Unable to configure streams with HAL: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + // Request thread needs to know to avoid using repeat-last-settings protocol + // across configure_streams() calls + mRequestThread->configurationComplete(); + + // Finish configuring the streams lazily on first reference + + mStatus = STATUS_ACTIVE; + + return OK; } + +/** + * Camera HAL device callback methods + */ + void Camera3Device::processCaptureResult(const camera3_capture_result *result) { - (void)result; + ATRACE_CALL(); + + status_t res; + + if (result->result == NULL) { + // TODO: Report error upstream + ALOGW("%s: No metadata for frame %d", __FUNCTION__, + result->frame_number); + return; + } + + nsecs_t timestamp = 0; + AlgState cur3aState; + AlgState new3aState; + int32_t aeTriggerId = 0; + int32_t afTriggerId = 0; + + NotificationListener *listener; + + { + Mutex::Autolock l(mOutputLock); + + // Push result metadata into queue + mResultQueue.push_back(CameraMetadata()); + // Lets avoid copies! Too bad there's not a #back method + CameraMetadata &captureResult = *(--mResultQueue.end()); + + captureResult = result->result; + if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT, + (int32_t*)&result->frame_number, 1) != OK) { + ALOGE("%s: Camera %d: Failed to set frame# in metadata (%d)", + __FUNCTION__, mId, result->frame_number); + // TODO: Report error upstream + } else { + ALOGVV("%s: Camera %d: Set frame# in metadata (%d)", + __FUNCTION__, mId, result->frame_number); + } + + // Get timestamp from result metadata + + camera_metadata_entry entry = + captureResult.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No timestamp provided by HAL for frame %d!", + __FUNCTION__, mId, result->frame_number); + // TODO: Report error upstream + } else { + timestamp = entry.data.i64[0]; + } + + // Get 3A states from result metadata + + entry = captureResult.find(ANDROID_CONTROL_AE_STATE); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No AE state provided by HAL for frame %d!", + __FUNCTION__, mId, result->frame_number); + } else { + new3aState.aeState = + static_cast<camera_metadata_enum_android_control_ae_state>( + entry.data.u8[0]); + } + + entry = captureResult.find(ANDROID_CONTROL_AF_STATE); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No AF state provided by HAL for frame %d!", + __FUNCTION__, mId, result->frame_number); + } else { + new3aState.afState = + static_cast<camera_metadata_enum_android_control_af_state>( + entry.data.u8[0]); + } + + entry = captureResult.find(ANDROID_CONTROL_AWB_STATE); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No AWB state provided by HAL for frame %d!", + __FUNCTION__, mId, result->frame_number); + } else { + new3aState.awbState = + static_cast<camera_metadata_enum_android_control_awb_state>( + entry.data.u8[0]); + } + + entry = captureResult.find(ANDROID_CONTROL_AF_TRIGGER_ID); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No AF trigger ID provided by HAL for frame %d!", + __FUNCTION__, mId, result->frame_number); + } else { + afTriggerId = entry.data.i32[0]; + } + + entry = captureResult.find(ANDROID_CONTROL_AE_PRECAPTURE_ID); + if (entry.count == 0) { + ALOGE("%s: Camera %d: No AE precapture trigger ID provided by HAL" + " for frame %d!", __FUNCTION__, mId, result->frame_number); + } else { + aeTriggerId = entry.data.i32[0]; + } + + listener = mListener; + cur3aState = m3AState; + + m3AState = new3aState; + } // scope for mOutputLock + + // Return completed buffers to their streams + for (size_t i = 0; i < result->num_output_buffers; i++) { + Camera3Stream *stream = + Camera3Stream::cast(result->output_buffers[i].stream); + res = stream->returnBuffer(result->output_buffers[i], timestamp); + // Note: stream may be deallocated at this point, if this buffer was the + // last reference to it. + if (res != OK) { + ALOGE("%s: Camera %d: Can't return buffer %d for frame %d to its" + " stream:%s (%d)", __FUNCTION__, mId, i, + result->frame_number, strerror(-res), res); + // TODO: Report error upstream + } + } + + // Dispatch any 3A change events to listeners + if (listener != NULL) { + if (new3aState.aeState != cur3aState.aeState) { + listener->notifyAutoExposure(new3aState.aeState, aeTriggerId); + } + if (new3aState.afState != cur3aState.afState) { + listener->notifyAutoFocus(new3aState.afState, afTriggerId); + } + if (new3aState.awbState != cur3aState.awbState) { + listener->notifyAutoWhitebalance(new3aState.awbState, aeTriggerId); + } + } - ALOGE("%s: Unimplemented", __FUNCTION__); } void Camera3Device::notify(const camera3_notify_msg *msg) { - (void)msg; + NotificationListener *listener; + { + Mutex::Autolock l(mOutputLock); + if (mListener == NULL) return; + listener = mListener; + } - ALOGE("%s: Unimplemented", __FUNCTION__); + if (msg == NULL) { + ALOGE("%s: Camera %d: HAL sent NULL notify message!", + __FUNCTION__, mId); + return; + } + + switch (msg->type) { + case CAMERA3_MSG_ERROR: { + int streamId = 0; + if (msg->message.error.error_stream != NULL) { + Camera3Stream *stream = + Camera3Stream::cast( + msg->message.error.error_stream); + streamId = stream->getId(); + } + listener->notifyError(msg->message.error.error_code, + msg->message.error.frame_number, streamId); + break; + } + case CAMERA3_MSG_SHUTTER: { + listener->notifyShutter(msg->message.shutter.frame_number, + msg->message.shutter.timestamp); + break; + } + default: + ALOGE("%s: Camera %d: Unknown notify message from HAL: %d", + __FUNCTION__, mId, msg->type); + } +} + +/** + * RequestThread inner class methods + */ + +Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent, + camera3_device_t *hal3Device) : + Thread(false), + mParent(parent), + mHal3Device(hal3Device), + mReconfigured(false), + mDoPause(false), + mPaused(true), + mFrameNumber(0) { +} + +void Camera3Device::RequestThread::configurationComplete() { + Mutex::Autolock l(mRequestLock); + mReconfigured = true; +} + +status_t Camera3Device::RequestThread::queueRequest( + sp<CaptureRequest> request) { + Mutex::Autolock l(mRequestLock); + mRequestQueue.push_back(request); + + return OK; +} + +status_t Camera3Device::RequestThread::setRepeatingRequests( + const RequestList &requests) { + Mutex::Autolock l(mRequestLock); + mRepeatingRequests.clear(); + mRepeatingRequests.insert(mRepeatingRequests.begin(), + requests.begin(), requests.end()); + return OK; +} + +status_t Camera3Device::RequestThread::clearRepeatingRequests() { + Mutex::Autolock l(mRequestLock); + mRepeatingRequests.clear(); + return OK; +} + +void Camera3Device::RequestThread::setPaused(bool paused) { + Mutex::Autolock l(mPauseLock); + mDoPause = paused; + mDoPauseSignal.signal(); +} + +status_t Camera3Device::RequestThread::waitUntilPaused(nsecs_t timeout) { + status_t res; + Mutex::Autolock l(mPauseLock); + while (!mPaused) { + res = mPausedSignal.waitRelative(mPauseLock, timeout); + if (res == TIMED_OUT) { + return res; + } + } + return OK; +} + +bool Camera3Device::RequestThread::threadLoop() { + + status_t res; + + // Handle paused state. + if (waitIfPaused()) { + return true; + } + + // Get work to do + + sp<CaptureRequest> nextRequest = waitForNextRequest(); + if (nextRequest == NULL) { + return true; + } + + // Create request to HAL + + camera3_capture_request_t request = camera3_capture_request_t(); + + if (mPrevRequest != nextRequest) { + request.settings = nextRequest->mSettings.getAndLock(); + mPrevRequest = nextRequest; + } // else leave request.settings NULL to indicate 'reuse latest given' + + camera3_stream_buffer_t inputBuffer; + Vector<camera3_stream_buffer_t> outputBuffers; + + // Fill in buffers + + if (nextRequest->mInputStream != NULL) { + request.input_buffer = &inputBuffer; + res = nextRequest->mInputStream->getBuffer(&inputBuffer); + if (res != OK) { + ALOGE("RequestThread: Can't get input buffer, skipping request:" + " %s (%d)", strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return true; + } + } else { + request.input_buffer = NULL; + } + + outputBuffers.insertAt(camera3_stream_buffer_t(), 0, + nextRequest->mOutputStreams.size()); + request.output_buffers = outputBuffers.array(); + for (size_t i = 0; i < nextRequest->mOutputStreams.size(); i++) { + res = nextRequest->mOutputStreams.editItemAt(i)-> + getBuffer(&outputBuffers.editItemAt(i)); + if (res != OK) { + ALOGE("RequestThread: Can't get output buffer, skipping request:" + "%s (%d)", strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return true; + } + request.num_output_buffers++; + } + + request.frame_number = mFrameNumber++; + + // Submit request and block until ready for next one + + res = mHal3Device->ops->process_capture_request(mHal3Device, &request); + if (res != OK) { + ALOGE("RequestThread: Unable to submit capture request %d to HAL" + " device: %s (%d)", request.frame_number, strerror(-res), res); + cleanUpFailedRequest(request, nextRequest, outputBuffers); + return false; + } + + if (request.settings != NULL) { + nextRequest->mSettings.unlock(request.settings); + } + return true; +} + +void Camera3Device::RequestThread::cleanUpFailedRequest( + camera3_capture_request_t &request, + sp<CaptureRequest> &nextRequest, + Vector<camera3_stream_buffer_t> &outputBuffers) { + + if (request.settings != NULL) { + nextRequest->mSettings.unlock(request.settings); + } + if (request.input_buffer != NULL) { + request.input_buffer->status = CAMERA3_BUFFER_STATUS_ERROR; + nextRequest->mInputStream->returnBuffer(*(request.input_buffer), 0); + } + for (size_t i = 0; i < request.num_output_buffers; i++) { + outputBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + nextRequest->mOutputStreams.editItemAt(i)->returnBuffer( + outputBuffers[i], 0); + } + // TODO: Report error upstream +} + +sp<Camera3Device::CaptureRequest> + Camera3Device::RequestThread::waitForNextRequest() { + status_t res; + sp<CaptureRequest> nextRequest; + + // Optimized a bit for the simple steady-state case (single repeating + // request), to avoid putting that request in the queue temporarily. + Mutex::Autolock l(mRequestLock); + + while (mRequestQueue.empty()) { + if (!mRepeatingRequests.empty()) { + // Always atomically enqueue all requests in a repeating request + // list. Guarantees a complete in-sequence set of captures to + // application. + const RequestList &requests = mRepeatingRequests; + RequestList::const_iterator firstRequest = + requests.begin(); + nextRequest = *firstRequest; + mRequestQueue.insert(mRequestQueue.end(), + ++firstRequest, + requests.end()); + // No need to wait any longer + break; + } + + res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout); + + if (res == TIMED_OUT) { + // Signal that we're paused by starvation + Mutex::Autolock pl(mPauseLock); + if (mPaused == false) { + mPaused = true; + mPausedSignal.signal(); + } + // Stop waiting for now and let thread management happen + return NULL; + } + } + + if (nextRequest == NULL) { + // Don't have a repeating request already in hand, so queue + // must have an entry now. + RequestList::iterator firstRequest = + mRequestQueue.begin(); + nextRequest = *firstRequest; + mRequestQueue.erase(firstRequest); + } + + // Not paused + Mutex::Autolock pl(mPauseLock); + mPaused = false; + + // Check if we've reconfigured since last time, and reset the preview + // request if so. Can't use 'NULL request == repeat' across configure calls. + if (mReconfigured) { + mPrevRequest.clear(); + mReconfigured = false; + } + + return nextRequest; +} + +bool Camera3Device::RequestThread::waitIfPaused() { + status_t res; + Mutex::Autolock l(mPauseLock); + while (mDoPause) { + // Signal that we're paused by request + if (mPaused == false) { + mPaused = true; + mPausedSignal.signal(); + } + res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout); + if (res == TIMED_OUT) { + return true; + } + } + // We don't set mPaused to false here, because waitForNextRequest needs + // to further manage the paused state in case of starvation. + return false; } /** diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h index df7352c..8600c6c 100644 --- a/services/camera/libcameraservice/Camera3Device.h +++ b/services/camera/libcameraservice/Camera3Device.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_SERVERS_CAMERA_CAMERA3DEVICE_H -#define ANDROID_SERVERS_CAMERA_CAMERA3DEVICE_H +#ifndef ANDROID_SERVERS_CAMERA3DEVICE_H +#define ANDROID_SERVERS_CAMERA3DEVICE_H #include <utils/Condition.h> #include <utils/Errors.h> @@ -24,6 +24,8 @@ #include <utils/Thread.h> #include "CameraDeviceBase.h" +#include "camera3/Camera3Stream.h" +#include "camera3/Camera3OutputStream.h" #include "hardware/camera3.h" @@ -55,63 +57,229 @@ class Camera3Device : virtual ~Camera3Device(); /** - * CameraDevice interface + * CameraDeviceBase interface */ + virtual int getId() const; + + // Transitions to idle state on success. virtual status_t initialize(camera_module_t *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16> &args); virtual const CameraMetadata& info() const; + + // Capture and setStreamingRequest will configure streams if currently in + // idle state virtual status_t capture(CameraMetadata &request); virtual status_t setStreamingRequest(const CameraMetadata &request); virtual status_t clearStreamingRequest(); + virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); + + // Actual stream creation/deletion is delayed until first request is submitted + // If adding streams while actively capturing, will pause device before adding + // stream, reconfiguring device, and unpausing. virtual status_t createStream(sp<ANativeWindow> consumer, uint32_t width, uint32_t height, int format, size_t size, int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); + virtual status_t getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format); virtual status_t setStreamTransform(int id, int transform); + virtual status_t deleteStream(int id); virtual status_t deleteReprocessStream(int id); + virtual status_t createDefaultRequest(int templateId, CameraMetadata *request); + + // Transitions to the idle state on success virtual status_t waitUntilDrained(); + virtual status_t setNotifyCallback(NotificationListener *listener); virtual status_t waitForNextFrame(nsecs_t timeout); virtual status_t getNextFrame(CameraMetadata *frame); + virtual status_t triggerAutofocus(uint32_t id); virtual status_t triggerCancelAutofocus(uint32_t id); virtual status_t triggerPrecaptureMetering(uint32_t id); + virtual status_t pushReprocessBuffer(int reprocessStreamId, buffer_handle_t *buffer, wp<BufferReleasedListener> listener); private: - const int mId; - camera3_device_t *mHal3Device; + static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec + + + Mutex mLock; + + /**** Scope for mLock ****/ - CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t mVendorTagOps; + const int mId; + camera3_device_t *mHal3Device; + + CameraMetadata mDeviceInfo; + vendor_tag_query_ops_t mVendorTagOps; + + enum { + STATUS_ERROR, + STATUS_UNINITIALIZED, + STATUS_IDLE, + STATUS_ACTIVE + } mStatus; + + // Mapping of stream IDs to stream instances + typedef KeyedVector<int, sp<camera3::Camera3OutputStream> > StreamSet; + + StreamSet mOutputStreams; + sp<camera3::Camera3Stream> mInputStream; + int mNextStreamId; + + // Need to hold on to stream references until configure completes. + Vector<sp<camera3::Camera3Stream> > mDeletedStreams; + + /**** End scope for mLock ****/ + + class CaptureRequest : public LightRefBase<CaptureRequest> { + public: + CameraMetadata mSettings; + sp<camera3::Camera3Stream> mInputStream; + Vector<sp<camera3::Camera3Stream> > mOutputStreams; + }; + typedef List<sp<CaptureRequest> > RequestList; + + /** + * Lock-held version of waitUntilDrained. Will transition to IDLE on + * success. + */ + status_t waitUntilDrainedLocked(); + + /** + * Do common work for setting up a streaming or single capture request. + * On success, will transition to ACTIVE if in IDLE. + */ + sp<CaptureRequest> setUpRequestLocked(const CameraMetadata &request); + + /** + * Build a CaptureRequest request from the CameraDeviceBase request + * settings. + */ + sp<CaptureRequest> createCaptureRequest(const CameraMetadata &request); + + /** + * Take the currently-defined set of streams and configure the HAL to use + * them. This is a long-running operation (may be several hundered ms). + */ + status_t configureStreamsLocked(); /** * Thread for managing capture request submission to HAL device. */ - class RequestThread: public Thread { + class RequestThread : public Thread { public: - RequestThread(wp<Camera3Device> parent); + RequestThread(wp<Camera3Device> parent, + camera3_device_t *hal3Device); + + /** + * Call after stream (re)-configuration is completed. + */ + void configurationComplete(); + + /** + * Set or clear the list of repeating requests. Does not block + * on either. Use waitUntilPaused to wait until request queue + * has emptied out. + */ + status_t setRepeatingRequests(const RequestList& requests); + status_t clearRepeatingRequests(); + + status_t queueRequest(sp<CaptureRequest> request); + + /** + * Pause/unpause the capture thread. Doesn't block, so use + * waitUntilPaused to wait until the thread is paused. + */ + void setPaused(bool paused); + + /** + * Wait until thread is paused, either due to setPaused(true) + * or due to lack of input requests. Returns TIMED_OUT in case + * the thread does not pause within the timeout. + */ + status_t waitUntilPaused(nsecs_t timeout); protected: virtual bool threadLoop(); private: + static const nsecs_t kRequestTimeout = 50e6; // 50 ms + + // Waits for a request, or returns NULL if times out. + sp<CaptureRequest> waitForNextRequest(); + + // Return buffers, etc, for a request that couldn't be fully + // constructed. The buffers will be returned in the ERROR state + // to mark them as not having valid data. + // All arguments will be modified. + void cleanUpFailedRequest(camera3_capture_request_t &request, + sp<CaptureRequest> &nextRequest, + Vector<camera3_stream_buffer_t> &outputBuffers); - wp<Camera3Device> mParent; + // Pause handling + bool waitIfPaused(); + wp<Camera3Device> mParent; + camera3_device_t *mHal3Device; + + Mutex mRequestLock; + Condition mRequestSignal; + RequestList mRequestQueue; + RequestList mRepeatingRequests; + + bool mReconfigured; + + // Used by waitIfPaused, waitForNextRequest, and waitUntilPaused + Mutex mPauseLock; + bool mDoPause; + Condition mDoPauseSignal; + bool mPaused; + Condition mPausedSignal; + + sp<CaptureRequest> mPrevRequest; + + int32_t mFrameNumber; }; - sp<RequestThread> requestThread; + sp<RequestThread> mRequestThread; + + /** + * Output result queue and current HAL device 3A state + */ + + // Lock for output side of device + Mutex mOutputLock; + + /**** Scope for mOutputLock ****/ + + List<CameraMetadata> mResultQueue; + Condition mResultSignal; + NotificationListener *mListener; + + struct AlgState { + camera_metadata_enum_android_control_ae_state aeState; + camera_metadata_enum_android_control_af_state afState; + camera_metadata_enum_android_control_awb_state awbState; + + AlgState() : + aeState(ANDROID_CONTROL_AE_STATE_INACTIVE), + afState(ANDROID_CONTROL_AF_STATE_INACTIVE), + awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE) { + } + } m3AState; + + /**** End scope for mOutputLock ****/ /** * Callback functions from HAL device diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 5a6a3c8..2db5224 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -66,6 +66,20 @@ static int getCallingUid() { return IPCThreadState::self()->getCallingUid(); } +extern "C" { +static void camera_device_status_change( + const struct camera_module_callbacks* callbacks, + int camera_id, + int new_status) { + sp<CameraService> cs = const_cast<CameraService*>( + static_cast<const CameraService*>(callbacks)); + + cs->onDeviceStatusChanged( + camera_id, + new_status); +} +} // extern "C" + // ---------------------------------------------------------------------------- // This is ugly and only safe if we never re-create the CameraService, but @@ -79,8 +93,10 @@ CameraService::CameraService() gCameraService = this; for (size_t i = 0; i < MAX_CAMERAS; ++i) { - mStatusList[i] = ICameraServiceListener::STATUS_AVAILABLE; + mStatusList[i] = ICameraServiceListener::STATUS_PRESENT; } + + this->camera_device_status_change = android::camera_device_status_change; } void CameraService::onFirstRef() @@ -105,6 +121,11 @@ void CameraService::onFirstRef() for (int i = 0; i < mNumberOfCameras; i++) { setCameraFree(i); } + + if (mModule->common.module_api_version >= + CAMERA_MODULE_API_VERSION_2_1) { + mModule->set_callbacks(this); + } } } @@ -118,6 +139,67 @@ CameraService::~CameraService() { gCameraService = NULL; } +void CameraService::onDeviceStatusChanged(int cameraId, + int newStatus) +{ + ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, + cameraId, newStatus); + + if (cameraId < 0 || cameraId >= MAX_CAMERAS) { + ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId); + return; + } + + if ((int)getStatus(cameraId) == newStatus) { + ALOGE("%s: State transition to the same status 0x%x not allowed", + __FUNCTION__, (uint32_t)newStatus); + return; + } + + /* don't do this in updateStatus + since it is also called from connect and we could get into a deadlock */ + if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { + Vector<sp<BasicClient> > clientsToDisconnect; + { + Mutex::Autolock al(mServiceLock); + + /* Find all clients that we need to disconnect */ + sp<Client> client = mClient[cameraId].promote(); + if (client.get() != NULL) { + clientsToDisconnect.push_back(client); + } + + int i = cameraId; + for (size_t j = 0; j < mProClientList[i].size(); ++j) { + sp<ProClient> cl = mProClientList[i][j].promote(); + if (cl != NULL) { + clientsToDisconnect.push_back(cl); + } + } + } + + /* now disconnect them. don't hold the lock + or we can get into a deadlock */ + + for (size_t i = 0; i < clientsToDisconnect.size(); ++i) { + sp<BasicClient> client = clientsToDisconnect[i]; + + client->disconnect(); + /** + * The remote app will no longer be able to call methods on the + * client since the client PID will be reset to 0 + */ + } + + ALOGV("%s: After unplug, disconnected %d clients", + __FUNCTION__, clientsToDisconnect.size()); + } + + updateStatus( + static_cast<ICameraServiceListener::Status>(newStatus), cameraId); + +} + int32_t CameraService::getNumberOfCameras() { return mNumberOfCameras; } @@ -212,6 +294,19 @@ bool CameraService::validateConnect(int cameraId, return false; } + ICameraServiceListener::Status currentStatus = getStatus(cameraId); + if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) { + ALOGI("Camera is not plugged in," + " connect X (pid %d) rejected", callingPid); + return false; + } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) { + ALOGI("Camera is enumerating," + " connect X (pid %d) rejected", callingPid); + return false; + } + // Else don't check for STATUS_NOT_AVAILABLE. + // -- It's done implicitly in canConnectUnsafe /w the mBusy array + return true; } @@ -293,6 +388,7 @@ sp<ICamera> CameraService::connect( // If there are other non-exclusive users of the camera, // this will tear them down before we can reuse the camera if (isValidCameraId(cameraId)) { + // transition from PRESENT -> NOT_AVAILABLE updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, cameraId); } @@ -321,7 +417,8 @@ sp<ICamera> CameraService::connect( if (!connectFinishUnsafe(client, client->asBinder())) { // this is probably not recoverable.. maybe the client can try again - updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId); + // OK: we can only get here if we were originally in PRESENT state + updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId); return NULL; } @@ -429,6 +526,15 @@ status_t CameraService::addListener( mListenerList.push_back(listener); + /* Immediately signal current status to this listener only */ + { + Mutex::Autolock m(mStatusMutex) ; + int numCams = getNumberOfCameras(); + for (int i = 0; i < numCams; ++i) { + listener->onStatusChanged(mStatusList[i], i); + } + } + return OK; } status_t CameraService::removeListener( @@ -719,6 +825,8 @@ CameraService::BasicClient::~BasicClient() { void CameraService::BasicClient::disconnect() { mCameraService->removeClientByRemote(mRemoteBinder); + // client shouldn't be able to call into us anymore + mClientPid = 0; } status_t CameraService::BasicClient::startCameraOps() { @@ -816,7 +924,7 @@ void CameraService::Client::notifyError() { void CameraService::Client::disconnect() { BasicClient::disconnect(); mCameraService->setCameraFree(mCameraId); - mCameraService->updateStatus(ICameraServiceListener::STATUS_AVAILABLE, + mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, mCameraId); } @@ -1017,6 +1125,16 @@ void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status, ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x", __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status); + if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && + (status != ICameraServiceListener::STATUS_PRESENT && + status != ICameraServiceListener::STATUS_ENUMERATING)) { + + ALOGW("%s: From NOT_PRESENT can only transition into PRESENT" + " or ENUMERATING", __FUNCTION__); + mStatusList[cameraId] = oldStatus; + return; + } + /** * ProClients lose their exclusive lock. * - Done before the CameraClient can initialize the HAL device, @@ -1041,4 +1159,14 @@ void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status, } } +ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { + if (cameraId < 0 || cameraId >= MAX_CAMERAS) { + ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); + return ICameraServiceListener::STATUS_UNKNOWN; + } + + Mutex::Autolock al(mStatusMutex); + return mStatusList[cameraId]; +} + }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index c5e495f..8cb1691 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -45,7 +45,8 @@ class MediaPlayer; class CameraService : public BinderService<CameraService>, public BnCameraService, - public IBinder::DeathRecipient + public IBinder::DeathRecipient, + public camera_module_callbacks_t { friend class BinderService<CameraService>; public: @@ -59,6 +60,11 @@ public: virtual ~CameraService(); ///////////////////////////////////////////////////////////////////// + // HAL Callbacks + virtual void onDeviceStatusChanged(int cameraId, + int newStatus); + + ///////////////////////////////////////////////////////////////////// // ICameraService virtual int32_t getNumberOfCameras(); virtual status_t getCameraInfo(int cameraId, @@ -327,10 +333,14 @@ private: mListenerList; // guard only mStatusList and the broadcasting of ICameraServiceListener - Mutex mStatusMutex; + mutable Mutex mStatusMutex; ICameraServiceListener::Status mStatusList[MAX_CAMERAS]; + // Read the current status (locks mStatusMutex) + ICameraServiceListener::Status + getStatus(int cameraId) const; + // Broadcast the new status if it changed (locks the service mutex) void updateStatus( ICameraServiceListener::Status status, diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.cpp b/services/camera/libcameraservice/camera3/Camera3InputStream.cpp new file mode 100644 index 0000000..8a48ee5 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3InputStream.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Camera3-InputStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3InputStream.h" + +namespace android { + +namespace camera3 { + +Camera3InputStream::Camera3InputStream(int id, + uint32_t width, uint32_t height, int format) : + Camera3Stream(id, CAMERA3_STREAM_INPUT, width, height, 0, format) { +} + +status_t Camera3InputStream::getBufferLocked(camera3_stream_buffer *buffer) { + (void) buffer; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3InputStream::returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + (void) timestamp; + (void) buffer; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +bool Camera3InputStream::hasOutstandingBuffersLocked() const { + ALOGE("%s: Not implemented", __FUNCTION__); + return false; +} + +status_t Camera3InputStream::waitUntilIdle(nsecs_t timeout) { + (void) timeout; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3InputStream::disconnectLocked() { + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +sp<IGraphicBufferProducer> Camera3InputStream::getProducerInterface() const { + return mConsumer->getProducerInterface(); +} + +void Camera3InputStream::dump(int fd, const Vector<String16> &args) const { + (void) fd; + (void) args; + ALOGE("%s: Not implemented", __FUNCTION__); +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.h b/services/camera/libcameraservice/camera3/Camera3InputStream.h new file mode 100644 index 0000000..c4b5dd9 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3InputStream.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_INPUT_STREAM_H +#define ANDROID_SERVERS_CAMERA3_INPUT_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> +#include <gui/BufferItemConsumer.h> + +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of input data to the camera device. + */ +class Camera3InputStream : public Camera3Stream { + public: + /** + * Set up a stream for formats that have fixed size, such as RAW and YUV. + */ + Camera3InputStream(int id, uint32_t width, uint32_t height, int format); + + virtual status_t waitUntilIdle(nsecs_t timeout); + virtual void dump(int fd, const Vector<String16> &args) const; + + /** + * Get the producer interface for this stream, to hand off to a producer. + * The producer must be connected to the provided interface before + * finishConfigure is called on this stream. + */ + sp<IGraphicBufferProducer> getProducerInterface() const; + + private: + + sp<BufferItemConsumer> mConsumer; + + /** + * Camera3Stream interface + */ + + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, + nsecs_t timestamp); + virtual bool hasOutstandingBuffersLocked() const; + virtual status_t disconnectLocked(); + +}; // class Camera3InputStream + +}; // namespace camera3 + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp new file mode 100644 index 0000000..276b940 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Camera3-OutputStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +// This is needed for stdint.h to define INT64_MAX in C++ +#define __STDC_LIMIT_MACROS + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3OutputStream.h" + +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char*)(ptr) - offsetof(type, member)) +#endif + +namespace android { + +namespace camera3 { + +Camera3OutputStream::Camera3OutputStream(int id, + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format) : + Camera3Stream(id, CAMERA3_STREAM_OUTPUT, width, height, 0, format), + mConsumer(consumer), + mTransform(0), + mTotalBufferCount(0), + mDequeuedBufferCount(0), + mFrameCount(0), + mLastTimestamp(0) { + + mCombinedFence = new Fence(); + if (mConsumer == NULL) { + ALOGE("%s: Consumer is NULL!", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3OutputStream::Camera3OutputStream(int id, + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, size_t maxSize, int format) : + Camera3Stream(id, CAMERA3_STREAM_OUTPUT, + width, height, maxSize, format), + mConsumer(consumer), + mTransform(0), + mTotalBufferCount(0), + mDequeuedBufferCount(0), + mFrameCount(0), + mLastTimestamp(0) { + + mCombinedFence = new Fence(); + + if (format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Bad format for size-only stream: %d", __FUNCTION__, + format); + mState = STATE_ERROR; + } + + if (mConsumer == NULL) { + ALOGE("%s: Consumer is NULL!", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3OutputStream::~Camera3OutputStream() { + disconnectLocked(); +} + +status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + status_t res; + + // Allow dequeue during IN_[RE]CONFIG for registration + if (mState != STATE_CONFIGURED && + mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Only limit dequeue amount when fully configured + if (mState == STATE_CONFIGURED && + mDequeuedBufferCount == camera3_stream::max_buffers) { + ALOGE("%s: Stream %d: Already dequeued maximum number of simultaneous" + " buffers (%d)", __FUNCTION__, mId, + camera3_stream::max_buffers); + return INVALID_OPERATION; + } + + ANativeWindowBuffer* anb; + int fenceFd; + + res = mConsumer->dequeueBuffer(mConsumer.get(), &anb, &fenceFd); + if (res != OK) { + ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + // Handing out a raw pointer to this object. Increment internal refcount. + incStrong(this); + buffer->stream = this; + buffer->buffer = &(anb->handle); + buffer->acquire_fence = fenceFd; + buffer->release_fence = -1; + buffer->status = CAMERA3_BUFFER_STATUS_OK; + + mDequeuedBufferCount++; + + return OK; +} + +status_t Camera3OutputStream::returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + ATRACE_CALL(); + status_t res; + + // returnBuffer may be called from a raw pointer, not a sp<>, and we'll be + // decrementing the internal refcount next. In case this is the last ref, we + // might get destructed on the decStrong(), so keep an sp around until the + // end of the call - otherwise have to sprinkle the decStrong on all exit + // points. + sp<Camera3OutputStream> keepAlive(this); + decStrong(this); + + // Allow buffers to be returned in the error state, to allow for disconnect + // and in the in-config states for registration + if (mState == STATE_CONSTRUCTED) { + ALOGE("%s: Stream %d: Can't return buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + if (mDequeuedBufferCount == 0) { + ALOGE("%s: Stream %d: No buffers outstanding to return", __FUNCTION__, + mId); + return INVALID_OPERATION; + } + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + res = mConsumer->cancelBuffer(mConsumer.get(), + container_of(buffer.buffer, ANativeWindowBuffer, handle), + buffer.release_fence); + if (res != OK) { + ALOGE("%s: Stream %d: Error cancelling buffer to native window:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + return res; + } + } else { + res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp); + if (res != OK) { + ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + sp<Fence> releaseFence = new Fence(buffer.release_fence); + int anwReleaseFence = releaseFence->dup(); + + res = mConsumer->queueBuffer(mConsumer.get(), + container_of(buffer.buffer, ANativeWindowBuffer, handle), + anwReleaseFence); + if (res != OK) { + ALOGE("%s: Stream %d: Error queueing buffer to native window: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + close(anwReleaseFence); + return res; + } + + mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence); + } + + mDequeuedBufferCount--; + mBufferReturnedSignal.signal(); + mLastTimestamp = timestamp; + + return OK; +} + +bool Camera3OutputStream::hasOutstandingBuffersLocked() const { + nsecs_t signalTime = mCombinedFence->getSignalTime(); + ALOGV("%s: Stream %d: Has %d outstanding buffers," + " buffer signal time is %lld", + __FUNCTION__, mId, mDequeuedBufferCount, signalTime); + if (mDequeuedBufferCount > 0 || signalTime == INT64_MAX) { + return true; + } + return false; +} + +status_t Camera3OutputStream::waitUntilIdle(nsecs_t timeout) { + status_t res; + { + Mutex::Autolock l(mLock); + while (mDequeuedBufferCount > 0) { + if (timeout != TIMEOUT_NEVER) { + nsecs_t startTime = systemTime(); + res = mBufferReturnedSignal.waitRelative(mLock, timeout); + if (res == TIMED_OUT) { + return res; + } else if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + nsecs_t deltaTime = systemTime() - startTime; + if (timeout <= deltaTime) { + timeout = 0; + } else { + timeout -= deltaTime; + } + } else { + res = mBufferReturnedSignal.wait(mLock); + if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } + } + } + + // No lock + + unsigned int timeoutMs; + if (timeout == TIMEOUT_NEVER) { + timeoutMs = Fence::TIMEOUT_NEVER; + } else if (timeout == 0) { + timeoutMs = 0; + } else { + // Round up to wait at least 1 ms + timeoutMs = (timeout + 999999) / 1000000; + } + + return mCombinedFence->wait(timeoutMs); +} + +void Camera3OutputStream::dump(int fd, const Vector<String16> &args) const { + (void) args; + String8 lines; + lines.appendFormat(" Stream[%d]: Output\n", mId); + lines.appendFormat(" State: %d\n", mState); + lines.appendFormat(" Dims: %d x %d, format 0x%x\n", + camera3_stream::width, camera3_stream::height, + camera3_stream::format); + lines.appendFormat(" Max size: %d\n", mMaxSize); + lines.appendFormat(" Usage: %d, max HAL buffers: %d\n", + camera3_stream::usage, camera3_stream::max_buffers); + lines.appendFormat(" Frames produced: %d, last timestamp: %lld ns\n", + mFrameCount, mLastTimestamp); + lines.appendFormat(" Total buffers: %d, currently dequeued: %d\n", + mTotalBufferCount, mDequeuedBufferCount); + write(fd, lines.string(), lines.size()); +} + +status_t Camera3OutputStream::setTransform(int transform) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return setTransformLocked(transform); +} + +status_t Camera3OutputStream::setTransformLocked(int transform) { + status_t res = OK; + if (mState == STATE_ERROR) { + ALOGE("%s: Stream in error state", __FUNCTION__); + return INVALID_OPERATION; + } + + mTransform = transform; + if (mState == STATE_CONFIGURED) { + res = native_window_set_buffers_transform(mConsumer.get(), + transform); + if (res != OK) { + ALOGE("%s: Unable to configure stream transform to %x: %s (%d)", + __FUNCTION__, transform, strerror(-res), res); + } + } + return res; +} + +status_t Camera3OutputStream::configureQueueLocked() { + status_t res; + + switch (mState) { + case STATE_IN_RECONFIG: + res = disconnect(); + if (res != OK) { + return res; + } + break; + case STATE_IN_CONFIG: + // OK + break; + default: + ALOGE("%s: Bad state: %d", __FUNCTION__, mState); + return INVALID_OPERATION; + } + + // Configure consumer-side ANativeWindow interface + res = native_window_api_connect(mConsumer.get(), + NATIVE_WINDOW_API_CAMERA); + if (res != OK) { + ALOGE("%s: Unable to connect to native window for stream %d", + __FUNCTION__, mId); + return res; + } + + res = native_window_set_usage(mConsumer.get(), camera3_stream::usage); + if (res != OK) { + ALOGE("%s: Unable to configure usage %08x for stream %d", + __FUNCTION__, camera3_stream::usage, mId); + return res; + } + + res = native_window_set_scaling_mode(mConsumer.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + if (res != OK) { + ALOGE("%s: Unable to configure stream scaling: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + res = setTransformLocked(0); + if (res != OK) { + return res; + } + + if (mMaxSize == 0) { + // For buffers of known size + res = native_window_set_buffers_geometry(mConsumer.get(), + camera3_stream::width, camera3_stream::height, + camera3_stream::format); + } else { + // For buffers with bounded size + res = native_window_set_buffers_geometry(mConsumer.get(), + mMaxSize, 1, + camera3_stream::format); + } + if (res != OK) { + ALOGE("%s: Unable to configure stream buffer geometry" + " %d x %d, format %x for stream %d", + __FUNCTION__, camera3_stream::width, camera3_stream::height, + camera3_stream::format, mId); + return res; + } + + int maxConsumerBuffers; + res = mConsumer->query(mConsumer.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers); + if (res != OK) { + ALOGE("%s: Unable to query consumer undequeued" + " buffer count for stream %d", __FUNCTION__, mId); + return res; + } + + ALOGV("%s: Consumer wants %d buffers", __FUNCTION__, + maxConsumerBuffers); + + mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers; + mDequeuedBufferCount = 0; + mFrameCount = 0; + mLastTimestamp = 0; + + res = native_window_set_buffer_count(mConsumer.get(), + mTotalBufferCount); + if (res != OK) { + ALOGE("%s: Unable to set buffer count for stream %d", + __FUNCTION__, mId); + return res; + } + + res = native_window_set_buffers_transform(mConsumer.get(), + mTransform); + if (res != OK) { + ALOGE("%s: Unable to configure stream transform to %x: %s (%d)", + __FUNCTION__, mTransform, strerror(-res), res); + } + + return OK; +} + +size_t Camera3OutputStream::getBufferCountLocked() { + return mTotalBufferCount; +} + +status_t Camera3OutputStream::disconnectLocked() { + status_t res; + + switch (mState) { + case STATE_IN_RECONFIG: + case STATE_CONFIGURED: + // OK + break; + default: + // No connection, nothing to do + return OK; + } + + if (mDequeuedBufferCount > 0) { + ALOGE("%s: Can't disconnect with %d buffers still dequeued!", + __FUNCTION__, mDequeuedBufferCount); + return INVALID_OPERATION; + } + + res = native_window_api_disconnect(mConsumer.get(), NATIVE_WINDOW_API_CAMERA); + + /** + * This is not an error. if client calling process dies, the window will + * also die and all calls to it will return DEAD_OBJECT, thus it's already + * "disconnected" + */ + if (res == DEAD_OBJECT) { + ALOGW("%s: While disconnecting stream %d from native window, the" + " native window died from under us", __FUNCTION__, mId); + } + else if (res != OK) { + ALOGE("%s: Unable to disconnect stream %d from native window (error %d %s)", + __FUNCTION__, mId, res, strerror(-res)); + mState = STATE_ERROR; + return res; + } + + mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG : STATE_CONSTRUCTED; + return OK; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.h b/services/camera/libcameraservice/camera3/Camera3OutputStream.h new file mode 100644 index 0000000..d331a94 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3OutputStream.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H +#define ANDROID_SERVERS_CAMERA3_OUTPUT_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> + +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of output data from the camera device. + */ +class Camera3OutputStream : public Camera3Stream { + public: + /** + * Set up a stream for formats that have 2 dimensions, such as RAW and YUV. + */ + Camera3OutputStream(int id, sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format); + + /** + * Set up a stream for formats that have a variable buffer size for the same + * dimensions, such as compressed JPEG. + */ + Camera3OutputStream(int id, sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, size_t maxSize, int format); + + virtual ~Camera3OutputStream(); + + /** + * Camera3Stream interface + */ + + virtual status_t waitUntilIdle(nsecs_t timeout); + virtual void dump(int fd, const Vector<String16> &args) const; + + /** + * Set the transform on the output stream; one of the + * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants. + */ + status_t setTransform(int transform); + + private: + sp<ANativeWindow> mConsumer; + int mTransform; + size_t mTotalBufferCount; + size_t mDequeuedBufferCount; + Condition mBufferReturnedSignal; + uint32_t mFrameCount; + nsecs_t mLastTimestamp; + + // The merged release fence for all returned buffers + sp<Fence> mCombinedFence; + + status_t setTransformLocked(int transform); + + /** + * Internal Camera3Stream interface + */ + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp); + virtual bool hasOutstandingBuffersLocked() const; + + virtual status_t configureQueueLocked(); + virtual size_t getBufferCountLocked(); + virtual status_t disconnectLocked(); + +}; // class Camera3OutputStream + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.cpp b/services/camera/libcameraservice/camera3/Camera3Stream.cpp new file mode 100644 index 0000000..cf3072b --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3Stream.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Camera3-Stream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +Camera3Stream::~Camera3Stream() { +} + +Camera3Stream* Camera3Stream::cast(camera3_stream *stream) { + return static_cast<Camera3Stream*>(stream); +} + +const Camera3Stream* Camera3Stream::cast(const camera3_stream *stream) { + return static_cast<const Camera3Stream*>(stream); +} + +Camera3Stream::Camera3Stream(int id, + camera3_stream_type type, + uint32_t width, uint32_t height, size_t maxSize, int format) : + camera3_stream(), + mId(id), + mName(String8::format("Camera3Stream[%d]", id)), + mMaxSize(maxSize), + mState(STATE_CONSTRUCTED) { + + camera3_stream::stream_type = type; + camera3_stream::width = width; + camera3_stream::height = height; + camera3_stream::format = format; + camera3_stream::usage = 0; + camera3_stream::max_buffers = 0; + camera3_stream::priv = NULL; + + if (format == HAL_PIXEL_FORMAT_BLOB && maxSize == 0) { + ALOGE("%s: BLOB format with size == 0", __FUNCTION__); + mState = STATE_ERROR; + } +} + +int Camera3Stream::getId() const { + return mId; +} + +uint32_t Camera3Stream::getWidth() const { + return camera3_stream::width; +} + +uint32_t Camera3Stream::getHeight() const { + return camera3_stream::height; +} + +int Camera3Stream::getFormat() const { + return camera3_stream::format; +} + +camera3_stream* Camera3Stream::startConfiguration() { + Mutex::Autolock l(mLock); + + switch (mState) { + case STATE_ERROR: + ALOGE("%s: In error state", __FUNCTION__); + return NULL; + case STATE_CONSTRUCTED: + // OK + break; + case STATE_IN_CONFIG: + case STATE_IN_RECONFIG: + // Can start config again with no trouble; but don't redo + // oldUsage/oldMaxBuffers + return this; + case STATE_CONFIGURED: + if (stream_type == CAMERA3_STREAM_INPUT) { + ALOGE("%s: Cannot configure an input stream twice", + __FUNCTION__); + return NULL; + } else if (hasOutstandingBuffersLocked()) { + ALOGE("%s: Cannot configure stream; has outstanding buffers", + __FUNCTION__); + return NULL; + } + break; + default: + ALOGE("%s: Unknown state %d", __FUNCTION__, mState); + return NULL; + } + + oldUsage = usage; + oldMaxBuffers = max_buffers; + + if (mState == STATE_CONSTRUCTED) { + mState = STATE_IN_CONFIG; + } else { // mState == STATE_CONFIGURED + mState = STATE_IN_RECONFIG; + } + + return this; +} + +bool Camera3Stream::isConfiguring() const { + Mutex::Autolock l(mLock); + return (mState == STATE_IN_CONFIG) || (mState == STATE_IN_RECONFIG); +} + +status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) { + Mutex::Autolock l(mLock); + switch (mState) { + case STATE_ERROR: + ALOGE("%s: In error state", __FUNCTION__); + return INVALID_OPERATION; + case STATE_IN_CONFIG: + case STATE_IN_RECONFIG: + // OK + break; + case STATE_CONSTRUCTED: + case STATE_CONFIGURED: + ALOGE("%s: Cannot finish configuration that hasn't been started", + __FUNCTION__); + return INVALID_OPERATION; + default: + ALOGE("%s: Unknown state", __FUNCTION__); + return INVALID_OPERATION; + } + + // Check if the stream configuration is unchanged, and skip reallocation if + // so. As documented in hardware/camera3.h:configure_streams(). + if (mState == STATE_IN_RECONFIG && + oldUsage == usage && + oldMaxBuffers == max_buffers) { + mState = STATE_CONFIGURED; + return OK; + } + + status_t res; + res = configureQueueLocked(); + if (res != OK) { + ALOGE("%s: Unable to configure stream %d queue: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + mState = STATE_ERROR; + return res; + } + + res = registerBuffersLocked(hal3Device); + if (res != OK) { + ALOGE("%s: Unable to register stream buffers with HAL: %s (%d)", + __FUNCTION__, strerror(-res), res); + mState = STATE_ERROR; + return res; + } + + mState = STATE_CONFIGURED; + + return res; +} + +status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return getBufferLocked(buffer); +} + +status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return returnBufferLocked(buffer, timestamp); +} + +bool Camera3Stream::hasOutstandingBuffers() const { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return hasOutstandingBuffersLocked(); +} + +status_t Camera3Stream::disconnect() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return disconnectLocked(); +} + +status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { + ATRACE_CALL(); + status_t res; + + size_t bufferCount = getBufferCountLocked(); + + Vector<buffer_handle_t*> buffers; + buffers.insertAt(NULL, 0, bufferCount); + + camera3_stream_buffer_set bufferSet = camera3_stream_buffer_set(); + bufferSet.stream = this; + bufferSet.num_buffers = bufferCount; + bufferSet.buffers = buffers.editArray(); + + Vector<camera3_stream_buffer_t> streamBuffers; + streamBuffers.insertAt(camera3_stream_buffer_t(), 0, bufferCount); + + // Register all buffers with the HAL. This means getting all the buffers + // from the stream, providing them to the HAL with the + // register_stream_buffers() method, and then returning them back to the + // stream in the error state, since they won't have valid data. + // + // Only registered buffers can be sent to the HAL. + + uint32_t bufferIdx = 0; + for (; bufferIdx < bufferCount; bufferIdx++) { + res = getBufferLocked( &streamBuffers.editItemAt(bufferIdx) ); + if (res != OK) { + ALOGE("%s: Unable to get buffer %d for registration with HAL", + __FUNCTION__, bufferIdx); + // Skip registering, go straight to cleanup + break; + } + + sp<Fence> fence = new Fence(streamBuffers[bufferIdx].acquire_fence); + fence->waitForever(kRegisterFenceTimeoutMs, + "Camera3Stream::registerBuffers"); + + buffers.editItemAt(bufferIdx) = streamBuffers[bufferIdx].buffer; + } + if (bufferIdx == bufferCount) { + // Got all buffers, register with HAL + ALOGV("%s: Registering %d buffers with camera HAL", + __FUNCTION__, bufferCount); + res = hal3Device->ops->register_stream_buffers(hal3Device, + &bufferSet); + } + + // Return all valid buffers to stream, in ERROR state to indicate + // they weren't filled. + for (size_t i = 0; i < bufferIdx; i++) { + streamBuffers.editItemAt(i).release_fence = -1; + streamBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; + returnBufferLocked(streamBuffers[i], 0); + } + + return res; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.h b/services/camera/libcameraservice/camera3/Camera3Stream.h new file mode 100644 index 0000000..2364cfd --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3Stream.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_STREAM_H +#define ANDROID_SERVERS_CAMERA3_STREAM_H + +#include <gui/Surface.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/String16.h> + +#include "hardware/camera3.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single stream of input or output data from the camera + * device. + * + * The stream has an internal state machine to track whether it's + * connected/configured/etc. + * + * States: + * + * STATE_ERROR: A serious error has occurred, stream is unusable. Outstanding + * buffers may still be returned. + * + * STATE_CONSTRUCTED: The stream is ready for configuration, but buffers cannot + * be gotten yet. Not connected to any endpoint, no buffers are registered + * with the HAL. + * + * STATE_IN_CONFIG: Configuration has started, but not yet concluded. During this + * time, the usage, max_buffers, and priv fields of camera3_stream returned by + * startConfiguration() may be modified. + * + * STATE_IN_RE_CONFIG: Configuration has started, and the stream has been + * configured before. Need to track separately from IN_CONFIG to avoid + * re-registering buffers with HAL. + * + * STATE_CONFIGURED: Stream is configured, and has registered buffers with the + * HAL. The stream's getBuffer/returnBuffer work. The priv pointer may still be + * modified. + * + * Transition table: + * + * <none> => STATE_CONSTRUCTED: + * When constructed with valid arguments + * <none> => STATE_ERROR: + * When constructed with invalid arguments + * STATE_CONSTRUCTED => STATE_IN_CONFIG: + * When startConfiguration() is called + * STATE_IN_CONFIG => STATE_CONFIGURED: + * When finishConfiguration() is called + * STATE_IN_CONFIG => STATE_ERROR: + * When finishConfiguration() fails to allocate or register buffers. + * STATE_CONFIGURED => STATE_IN_RE_CONFIG: * + * When startConfiguration() is called again, after making sure stream is + * idle with waitUntilIdle(). + * STATE_IN_RE_CONFIG => STATE_CONFIGURED: + * When finishConfiguration() is called. + * STATE_IN_RE_CONFIG => STATE_ERROR: + * When finishConfiguration() fails to allocate or register buffers. + * STATE_CONFIGURED => STATE_CONSTRUCTED: + * When disconnect() is called after making sure stream is idle with + * waitUntilIdle(). + */ +class Camera3Stream : + protected camera3_stream, + public LightRefBase<Camera3Stream> { + public: + + virtual ~Camera3Stream(); + + static Camera3Stream* cast(camera3_stream *stream); + static const Camera3Stream* cast(const camera3_stream *stream); + + /** + * Get the stream's ID + */ + int getId() const; + + /** + * Get the stream's dimensions and format + */ + uint32_t getWidth() const; + uint32_t getHeight() const; + int getFormat() const; + + /** + * Start the stream configuration process. Returns a handle to the stream's + * information to be passed into the HAL device's configure_streams call. + * + * Until finishConfiguration() is called, no other methods on the stream may be + * called. The usage and max_buffers fields of camera3_stream may be modified + * between start/finishConfiguration, but may not be changed after that. + * The priv field of camera3_stream may be modified at any time after + * startConfiguration. + * + * Returns NULL in case of error starting configuration. + */ + camera3_stream* startConfiguration(); + + /** + * Check if the stream is mid-configuration (start has been called, but not + * finish). Used for lazy completion of configuration. + */ + bool isConfiguring() const; + + /** + * Completes the stream configuration process. During this call, the stream + * may call the device's register_stream_buffers() method. The stream + * information structure returned by startConfiguration() may no longer be + * modified after this call, but can still be read until the destruction of + * the stream. + * + * Returns: + * OK on a successful configuration + * NO_INIT in case of a serious error from the HAL device + * NO_MEMORY in case of an error registering buffers + * INVALID_OPERATION in case connecting to the consumer failed + */ + status_t finishConfiguration(camera3_device *hal3Device); + + /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the output-side + * buffers. + * + */ + status_t getBuffer(camera3_stream_buffer *buffer); + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the output-side buffers + */ + status_t returnBuffer(const camera3_stream_buffer &buffer, + nsecs_t timestamp); + + /** + * Whether any of the stream's buffers are currently in use by the HAL, + * including buffers that have been returned but not yet had their + * release fence signaled. + */ + bool hasOutstandingBuffers() const; + + enum { + TIMEOUT_NEVER = -1 + }; + /** + * Wait until the HAL is done with all of this stream's buffers, including + * signalling all release fences. Returns TIMED_OUT if the timeout is exceeded, + * OK on success. Pass in TIMEOUT_NEVER for timeout to indicate an indefinite wait. + */ + virtual status_t waitUntilIdle(nsecs_t timeout) = 0; + + /** + * Disconnect stream from its non-HAL endpoint. After this, + * start/finishConfiguration must be called before the stream can be used + * again. This cannot be called if the stream has outstanding dequeued + * buffers. + */ + status_t disconnect(); + + /** + * Debug dump of the stream's state. + */ + virtual void dump(int fd, const Vector<String16> &args) const = 0; + + protected: + const int mId; + const String8 mName; + // Zero for formats with fixed buffer size for given dimensions. + const size_t mMaxSize; + + enum { + STATE_ERROR, + STATE_CONSTRUCTED, + STATE_IN_CONFIG, + STATE_IN_RECONFIG, + STATE_CONFIGURED + } mState; + + mutable Mutex mLock; + + Camera3Stream(int id, camera3_stream_type type, + uint32_t width, uint32_t height, size_t maxSize, int format); + + /** + * Interface to be implemented by derived classes + */ + + // getBuffer / returnBuffer implementations + + // Since camera3_stream_buffer includes a raw pointer to the stream, + // cast to camera3_stream*, implementations must increment the + // refcount of the stream manually in getBufferLocked, and decrement it in + // returnBufferLocked. + virtual status_t getBufferLocked(camera3_stream_buffer *buffer) = 0; + virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, + nsecs_t timestamp) = 0; + virtual bool hasOutstandingBuffersLocked() const = 0; + virtual status_t disconnectLocked() = 0; + + // Configure the buffer queue interface to the other end of the stream, + // after the HAL has provided usage and max_buffers values. After this call, + // the stream must be ready to produce all buffers for registration with + // HAL. + virtual status_t configureQueueLocked() = 0; + + // Get the total number of buffers in the queue + virtual size_t getBufferCountLocked() = 0; + + private: + static const unsigned int kRegisterFenceTimeoutMs = 5000; + + uint32_t oldUsage; + uint32_t oldMaxBuffers; + + // Gets all buffers from endpoint and registers them with the HAL. + status_t registerBuffersLocked(camera3_device *hal3Device); + +}; // class Camera3Stream + +}; // namespace camera3 + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera3/Camera3ZslStream.cpp b/services/camera/libcameraservice/camera3/Camera3ZslStream.cpp new file mode 100644 index 0000000..e8a5ca6 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3ZslStream.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Camera3-ZslStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3ZslStream.h" + +namespace android { + +namespace camera3 { + +Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height, + int depth) : + Camera3Stream(id, CAMERA3_STREAM_BIDIRECTIONAL, width, height, 0, + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + mDepth(depth) { +} + +status_t Camera3ZslStream::getBufferLocked(camera3_stream_buffer *buffer) { + (void) buffer; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3ZslStream::returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + (void) buffer; + (void) timestamp; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +bool Camera3ZslStream::hasOutstandingBuffersLocked() const { + ALOGE("%s: Not implemented", __FUNCTION__); + return false; +} + +status_t Camera3ZslStream::waitUntilIdle(nsecs_t timeout) { + (void) timeout; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3ZslStream::disconnectLocked() { + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3ZslStream::getInputBuffer(camera3_stream_buffer *buffer, + nsecs_t timestamp) { + (void) buffer; + (void) timestamp; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera3ZslStream::returnInputBuffer(const camera3_stream_buffer &buffer) { + (void) buffer; + ALOGE("%s: Not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +void Camera3ZslStream::dump(int fd, const Vector<String16> &args) const { + (void) fd; + (void) args; + ALOGE("%s: Not implemented", __FUNCTION__); +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/camera3/Camera3ZslStream.h b/services/camera/libcameraservice/camera3/Camera3ZslStream.h new file mode 100644 index 0000000..39d5995 --- /dev/null +++ b/services/camera/libcameraservice/camera3/Camera3ZslStream.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SERVERS_CAMERA3_ZSL_STREAM_H +#define ANDROID_SERVERS_CAMERA3_ZSL_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> + +#include "Camera3Stream.h" + +namespace android { + +namespace camera3 { + +/** + * A class for managing a single opaque ZSL stream to/from the camera device. + * This acts as a bidirectional stream at the HAL layer, caching and discarding + * most output buffers, and when directed, pushes a buffer back to the HAL for + * processing. + */ +class Camera3ZslStream: public Camera3Stream { + public: + /** + * Set up a ZSL stream of a given resolution. Depth is the number of buffers + * cached within the stream that can be retrieved for input. + */ + Camera3ZslStream(int id, uint32_t width, uint32_t height, int depth); + + virtual status_t waitUntilIdle(nsecs_t timeout); + virtual void dump(int fd, const Vector<String16> &args) const; + + /** + * Get an input buffer matching a specific timestamp. If no buffer matching + * the timestamp is available, NO_MEMORY is returned. + */ + status_t getInputBuffer(camera3_stream_buffer *buffer, nsecs_t timestamp); + + /** + * Return input buffer from HAL. The buffer is then marked as unfilled, and + * returned to the output-side stream for refilling. + */ + status_t returnInputBuffer(const camera3_stream_buffer &buffer); + + private: + + int mDepth; + + /** + * Camera3Stream interface + */ + + // getBuffer/returnBuffer operate the output stream side of the ZslStream. + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, + nsecs_t timestamp); + virtual bool hasOutstandingBuffersLocked() const; + virtual status_t disconnectLocked(); + +}; // class Camera3ZslStream + +}; // namespace camera3 + +}; // namespace android + +#endif diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index 2332b3e..f60749d 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -19,6 +19,7 @@ #include <sys/mman.h> #include <utils/Log.h> +#include <binder/PermissionCache.h> #include <media/nbaio/NBLog.h> #include <private/android_filesystem_config.h> #include "MediaLogService.h" @@ -55,6 +56,14 @@ void MediaLogService::unregisterWriter(const sp<IMemory>& shared) status_t MediaLogService::dump(int fd, const Vector<String16>& args) { + // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp + static const String16 sDump("android.permission.DUMP"); + if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA || + PermissionCache::checkCallingPermission(sDump))) { + fdprintf(fd, "Permission denied.\n"); + return NO_ERROR; + } + Vector<NamedReader> namedReaders; { Mutex::Autolock _l(mLock); |