diff options
94 files changed, 2408 insertions, 2816 deletions
diff --git a/camera/Camera.cpp b/camera/Camera.cpp index f417c90..1b136de 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -96,32 +96,14 @@ status_t Camera::unlock() return c->unlock(); } -// pass the buffered Surface to the camera service -status_t Camera::setPreviewDisplay(const sp<Surface>& surface) -{ - ALOGV("setPreviewDisplay(%p)", surface.get()); - sp <ICamera> c = mCamera; - if (c == 0) return NO_INIT; - if (surface != 0) { - return c->setPreviewDisplay(surface); - } else { - ALOGD("app passed NULL surface"); - return c->setPreviewDisplay(0); - } -} - // pass the buffered IGraphicBufferProducer to the camera service status_t Camera::setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer) { ALOGV("setPreviewTexture(%p)", bufferProducer.get()); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - if (bufferProducer != 0) { - return c->setPreviewTexture(bufferProducer); - } else { - ALOGD("app passed NULL surface"); - return c->setPreviewTexture(0); - } + ALOGD_IF(bufferProducer == 0, "app passed NULL surface"); + return c->setPreviewTexture(bufferProducer); } // start preview mode @@ -283,7 +265,14 @@ void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) { - return CameraBaseT::dataCallback(msgType, dataPtr, metadata); + sp<CameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + if (listener != NULL) { + listener->postData(msgType, dataPtr, metadata); + } } // callback from camera service when timestamped frame is ready @@ -302,7 +291,15 @@ void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp< return; } - if (!CameraBaseT::dataCallbackTimestamp(timestamp, msgType, dataPtr)) { + sp<CameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + + if (listener != NULL) { + listener->postDataTimestamp(timestamp, msgType, dataPtr); + } else { ALOGW("No listener was set. Drop a recording frame."); releaseRecordingFrame(dataPtr); } diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp index 29096da..c25c5fd 100644 --- a/camera/CameraBase.cpp +++ b/camera/CameraBase.cpp @@ -176,41 +176,6 @@ void CameraBase<TCam, TCamTraits>::notifyCallback(int32_t msgType, } } -// callback from camera service when frame or image is ready -template <typename TCam, typename TCamTraits> -void CameraBase<TCam, TCamTraits>::dataCallback(int32_t msgType, - const sp<IMemory>& dataPtr, - camera_frame_metadata *metadata) -{ - sp<TCamListener> listener; - { - Mutex::Autolock _l(mLock); - listener = mListener; - } - if (listener != NULL) { - listener->postData(msgType, dataPtr, metadata); - } -} - -// callback from camera service when timestamped frame is ready -template <typename TCam, typename TCamTraits> -bool CameraBase<TCam, TCamTraits>::dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, - const sp<IMemory>& dataPtr) -{ - sp<TCamListener> listener; - { - Mutex::Autolock _l(mLock); - listener = mListener; - } - if (listener != NULL) { - listener->postDataTimestamp(timestamp, msgType, dataPtr); - return true; - } - - return false; -} - template <typename TCam, typename TCamTraits> int CameraBase<TCam, TCamTraits>::getNumberOfCameras() { const sp<ICameraService> cs = getCameraService(); diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp index 5d210e7..8900867 100644 --- a/camera/ICamera.cpp +++ b/camera/ICamera.cpp @@ -29,7 +29,6 @@ namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, - SET_PREVIEW_DISPLAY, SET_PREVIEW_TEXTURE, SET_PREVIEW_CALLBACK_FLAG, START_PREVIEW, @@ -68,17 +67,6 @@ public: remote()->transact(DISCONNECT, data, &reply); } - // pass the buffered Surface to the camera service - status_t setPreviewDisplay(const sp<Surface>& surface) - { - ALOGV("setPreviewDisplay"); - Parcel data, reply; - data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - Surface::writeToParcel(surface, &data); - remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); - return reply.readInt32(); - } - // pass the buffered IGraphicBufferProducer to the camera service status_t setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer) { @@ -282,13 +270,6 @@ status_t BnCamera::onTransact( disconnect(); return NO_ERROR; } break; - case SET_PREVIEW_DISPLAY: { - ALOGV("SET_PREVIEW_DISPLAY"); - CHECK_INTERFACE(ICamera, data, reply); - sp<Surface> surface = Surface::readFromParcel(data); - reply->writeInt32(setPreviewDisplay(surface)); - return NO_ERROR; - } break; case SET_PREVIEW_TEXTURE: { ALOGV("SET_PREVIEW_TEXTURE"); CHECK_INTERFACE(ICamera, data, reply); diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp index 6cd36bf..b9cd14d 100644 --- a/camera/IProCameraCallbacks.cpp +++ b/camera/IProCameraCallbacks.cpp @@ -34,8 +34,6 @@ namespace android { enum { NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, - DATA_CALLBACK, - DATA_CALLBACK_TIMESTAMP, LOCK_STATUS_CHANGED, RESULT_RECEIVED, }; @@ -63,37 +61,6 @@ public: remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); } - // generic data callback from camera service to app with image data - void dataCallback(int32_t msgType, const sp<IMemory>& imageData, - camera_frame_metadata_t *metadata) - { - ALOGV("dataCallback"); - Parcel data, reply; - data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor()); - data.writeInt32(msgType); - data.writeStrongBinder(imageData->asBinder()); - if (metadata) { - data.writeInt32(metadata->number_of_faces); - data.write(metadata->faces, - sizeof(camera_face_t) * metadata->number_of_faces); - } - remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); - } - - // generic data callback from camera service to app with image data - void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& imageData) - { - ALOGV("dataCallback"); - Parcel data, reply; - data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor()); - data.writeInt64(timestamp); - data.writeInt32(msgType); - data.writeStrongBinder(imageData->asBinder()); - remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, - IBinder::FLAG_ONEWAY); - } - void onLockStatusChanged(LockStatus newLockStatus) { ALOGV("onLockStatusChanged"); Parcel data, reply; @@ -132,33 +99,6 @@ status_t BnProCameraCallbacks::onTransact( notifyCallback(msgType, ext1, ext2); return NO_ERROR; } break; - case DATA_CALLBACK: { - ALOGV("DATA_CALLBACK"); - CHECK_INTERFACE(IProCameraCallbacks, data, reply); - int32_t msgType = data.readInt32(); - sp<IMemory> imageData = interface_cast<IMemory>( - data.readStrongBinder()); - camera_frame_metadata_t *metadata = NULL; - if (data.dataAvail() > 0) { - metadata = new camera_frame_metadata_t; - metadata->number_of_faces = data.readInt32(); - metadata->faces = (camera_face_t *) data.readInplace( - sizeof(camera_face_t) * metadata->number_of_faces); - } - dataCallback(msgType, imageData, metadata); - if (metadata) delete metadata; - return NO_ERROR; - } break; - case DATA_CALLBACK_TIMESTAMP: { - ALOGV("DATA_CALLBACK_TIMESTAMP"); - CHECK_INTERFACE(IProCameraCallbacks, data, reply); - nsecs_t timestamp = data.readInt64(); - int32_t msgType = data.readInt32(); - sp<IMemory> imageData = interface_cast<IMemory>( - data.readStrongBinder()); - dataCallbackTimestamp(timestamp, msgType, imageData); - return NO_ERROR; - } break; case LOCK_STATUS_CHANGED: { ALOGV("LOCK_STATUS_CHANGED"); CHECK_INTERFACE(IProCameraCallbacks, data, reply); diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp index c9d98aa..0c94bd4 100644 --- a/camera/IProCameraUser.cpp +++ b/camera/IProCameraUser.cpp @@ -40,8 +40,7 @@ enum { HAS_EXCLUSIVE_LOCK, SUBMIT_REQUEST, CANCEL_REQUEST, - REQUEST_STREAM, - CANCEL_STREAM, + DELETE_STREAM, CREATE_STREAM, CREATE_DEFAULT_REQUEST, GET_CAMERA_INFO, @@ -200,22 +199,13 @@ public: return reply.readInt32(); } - virtual status_t requestStream(int streamId) + virtual status_t deleteStream(int streamId) { Parcel data, reply; data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); data.writeInt32(streamId); - remote()->transact(REQUEST_STREAM, data, &reply); - return reply.readInt32(); - } - virtual status_t cancelStream(int streamId) - { - Parcel data, reply; - data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor()); - data.writeInt32(streamId); - - remote()->transact(CANCEL_STREAM, data, &reply); + remote()->transact(DELETE_STREAM, data, &reply); return reply.readInt32(); } @@ -334,16 +324,10 @@ status_t BnProCameraUser::onTransact( reply->writeInt32(cancelRequest(requestId)); return NO_ERROR; } break; - case REQUEST_STREAM: { - CHECK_INTERFACE(IProCameraUser, data, reply); - int streamId = data.readInt32(); - reply->writeInt32(requestStream(streamId)); - return NO_ERROR; - } break; - case CANCEL_STREAM: { + case DELETE_STREAM: { CHECK_INTERFACE(IProCameraUser, data, reply); int streamId = data.readInt32(); - reply->writeInt32(cancelStream(streamId)); + reply->writeInt32(deleteStream(streamId)); return NO_ERROR; } break; case CREATE_STREAM: { diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp index 3cfabf6..396b009 100644 --- a/camera/ProCamera.cpp +++ b/camera/ProCamera.cpp @@ -60,21 +60,6 @@ void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) return CameraBaseT::notifyCallback(msgType, ext1, ext2); } -// callback from camera service when frame or image is ready -void ProCamera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, - camera_frame_metadata_t *metadata) -{ - return CameraBaseT::dataCallback(msgType, dataPtr, metadata); -} - -// callback from camera service when timestamped frame is ready -void ProCamera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& dataPtr) -{ - CameraBaseT::dataCallbackTimestamp(timestamp, msgType, dataPtr); -} - - void ProCamera::onLockStatusChanged( IProCameraCallbacks::LockStatus newLockStatus) { @@ -185,7 +170,7 @@ status_t ProCamera::deleteStream(int streamId) sp <IProCameraUser> c = mCamera; if (c == 0) return NO_INIT; - status_t s = c->cancelStream(streamId); + status_t s = c->deleteStream(streamId); mStreams.removeItem(streamId); @@ -330,10 +315,7 @@ void ProCamera::onFrameAvailable(int streamId) { CpuConsumer::LockedBuffer buf; if (listener.get() != NULL) { - if (listener->useOnFrameAvailable()) { - listener->onFrameAvailable(streamId, stream.cpuConsumer); - return; - } + listener->onFrameAvailable(streamId, stream.cpuConsumer); } // Unblock waitForFrame(id) callers diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp index 1a8564e..71813ae 100644 --- a/camera/tests/ProCameraTests.cpp +++ b/camera/tests/ProCameraTests.cpp @@ -271,13 +271,11 @@ protected: free_camera_metadata(request); } - // TODO: remove - - virtual void notify(int32_t , int32_t , int32_t ) {} - virtual void postData(int32_t , const sp<IMemory>& , - camera_frame_metadata_t *) {} - virtual void postDataTimestamp(nsecs_t , int32_t , const sp<IMemory>& ) {} - + virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) { + dout << "Notify received: msg " << std::hex << msg + << ", ext1: " << std::hex << ext1 << ", ext2: " << std::hex << ext2 + << std::endl; + } Vector<ProEvent> mProEventList; Mutex mListenerMutex; @@ -717,6 +715,7 @@ TEST_F(ProCameraTest, CpuConsumerSingle) { return; } + // FIXME: Note this test is broken because onBufferReceived was removed mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED)); int streamId = -1; @@ -783,6 +782,7 @@ TEST_F(ProCameraTest, CpuConsumerDual) { return; } + // FIXME: Note this test is broken because onBufferReceived was removed mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED)); int streamId = -1; diff --git a/include/camera/Camera.h b/include/camera/Camera.h index 71c66ce..37626a4 100644 --- a/include/camera/Camera.h +++ b/include/camera/Camera.h @@ -74,9 +74,6 @@ public: status_t lock(); status_t unlock(); - // pass the buffered Surface to the camera service - status_t setPreviewDisplay(const sp<Surface>& surface); - // pass the buffered IGraphicBufferProducer to the camera service status_t setPreviewTexture(const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/include/camera/CameraBase.h b/include/camera/CameraBase.h index 2735a86..9b08c0f 100644 --- a/include/camera/CameraBase.h +++ b/include/camera/CameraBase.h @@ -91,12 +91,6 @@ protected: //////////////////////////////////////////////////////// virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2); - virtual void dataCallback(int32_t msgType, - const sp<IMemory>& dataPtr, - camera_frame_metadata *metadata); - bool dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, - const sp<IMemory>& dataPtr); //////////////////////////////////////////////////////// // Common instance variables @@ -115,7 +109,7 @@ protected: const int mCameraId; - typedef CameraBase<TCam> CameraBaseT; + typedef CameraBase<TCam> CameraBaseT; }; }; // namespace android diff --git a/include/camera/ICamera.h b/include/camera/ICamera.h index eccaa41..2236c1f 100644 --- a/include/camera/ICamera.h +++ b/include/camera/ICamera.h @@ -46,9 +46,6 @@ public: // allow other processes to use this ICamera interface virtual status_t unlock() = 0; - // pass the buffered Surface to the camera service - virtual status_t setPreviewDisplay(const sp<Surface>& surface) = 0; - // pass the buffered IGraphicBufferProducer to the camera service virtual status_t setPreviewTexture( const sp<IGraphicBufferProducer>& bufferProducer) = 0; diff --git a/include/camera/IProCameraCallbacks.h b/include/camera/IProCameraCallbacks.h index fc24026..563ec17 100644 --- a/include/camera/IProCameraCallbacks.h +++ b/include/camera/IProCameraCallbacks.h @@ -28,19 +28,14 @@ struct camera_metadata; namespace android { -class IProCameraCallbacks: public IInterface +class IProCameraCallbacks : public IInterface { public: DECLARE_META_INTERFACE(ProCameraCallbacks); - virtual void notifyCallback(int32_t msgType, int32_t ext1, - int32_t ext2) = 0; - virtual void dataCallback(int32_t msgType, - const sp<IMemory>& data, - camera_frame_metadata_t *metadata) = 0; - virtual void dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, - const sp<IMemory>& data) = 0; + virtual void notifyCallback(int32_t msgType, + int32_t ext1, + int32_t ext2) = 0; enum LockStatus { LOCK_ACQUIRED, @@ -53,12 +48,13 @@ public: /** Missing by design: implementation is client-side in ProCamera.cpp **/ // virtual void onBufferReceived(int streamId, // const CpuConsumer::LockedBufer& buf); - virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0; + virtual void onResultReceived(int32_t frameId, + camera_metadata* result) = 0; }; // ---------------------------------------------------------------------------- -class BnProCameraCallbacks: public BnInterface<IProCameraCallbacks> +class BnProCameraCallbacks : public BnInterface<IProCameraCallbacks> { public: virtual status_t onTransact( uint32_t code, diff --git a/include/camera/IProCameraUser.h b/include/camera/IProCameraUser.h index 7bddb0c..45b818c 100644 --- a/include/camera/IProCameraUser.h +++ b/include/camera/IProCameraUser.h @@ -61,8 +61,7 @@ public: bool streaming = false) = 0; virtual status_t cancelRequest(int requestId) = 0; - virtual status_t requestStream(int streamId) = 0; - virtual status_t cancelStream(int streamId) = 0; + virtual status_t deleteStream(int streamId) = 0; virtual status_t createStream( int width, int height, int format, const sp<IGraphicBufferProducer>& bufferProducer, diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h index 5d6cfaa..3d1652f 100644 --- a/include/camera/ProCamera.h +++ b/include/camera/ProCamera.h @@ -40,9 +40,11 @@ namespace android { // All callbacks on this class are concurrent // (they come from separate threads) -class ProCameraListener : public CameraListener +class ProCameraListener : virtual public RefBase { public: + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; + // Lock has been acquired. Write operations now available. virtual void onLockAcquired() = 0; // Lock has been released with exclusiveUnlock. @@ -53,19 +55,9 @@ public: // Lock free. virtual void onTriggerNotify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; - - // OnBufferReceived and OnRequestReceived can come in with any order, + // onFrameAvailable and OnResultReceived can come in with any order, // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them - // TODO: remove onBufferReceived - - // A new frame buffer has been received for this stream. - // -- This callback only fires for createStreamCpu streams - // -- Use buf.timestamp to correlate with metadata's - // android.sensor.timestamp - // -- The buffer must not be accessed after this function call completes - virtual void onBufferReceived(int streamId, - const CpuConsumer::LockedBuffer& buf) = 0; /** * A new metadata buffer has been received. * -- Ownership of request passes on to the callee, free with @@ -77,17 +69,14 @@ public: // A new frame buffer has been received for this stream. // -- This callback only fires for createStreamCpu streams - // -- Use buf.timestamp to correlate with metadata's android.sensor.timestamp + // -- A buffer may be obtained by calling cpuConsumer->lockNextBuffer + // -- Use buf.timestamp to correlate with result's android.sensor.timestamp // -- The buffer should be accessed with CpuConsumer::lockNextBuffer // and CpuConsumer::unlockBuffer virtual void onFrameAvailable(int /*streamId*/, const sp<CpuConsumer>& /*cpuConsumer*/) { } - // TODO: Remove useOnFrameAvailable - virtual bool useOnFrameAvailable() { - return false; - } }; class ProCamera; @@ -249,14 +238,10 @@ protected: //////////////////////////////////////////////////////// // IProCameraCallbacks implementation //////////////////////////////////////////////////////// - virtual void notifyCallback(int32_t msgType, int32_t ext, + virtual void notifyCallback(int32_t msgType, + int32_t ext, int32_t ext2); - virtual void dataCallback(int32_t msgType, - const sp<IMemory>& dataPtr, - camera_frame_metadata_t *metadata); - virtual void dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, - const sp<IMemory>& dataPtr); + virtual void onLockStatusChanged( IProCameraCallbacks::LockStatus newLockStatus); diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h index 8d7f11d..3e67550 100644 --- a/include/media/IMediaRecorder.h +++ b/include/media/IMediaRecorder.h @@ -35,7 +35,7 @@ public: virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy) = 0; - virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0; + virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0; virtual status_t setVideoSource(int vs) = 0; virtual status_t setAudioSource(int as) = 0; virtual status_t setOutputFormat(int of) = 0; diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h index 39e0a9e..677119b 100644 --- a/include/media/IStreamSource.h +++ b/include/media/IStreamSource.h @@ -37,6 +37,9 @@ struct IStreamSource : public IInterface { enum { // Video PES packets contain exactly one (aligned) access unit. kFlagAlignedVideoData = 1, + + // Timestamps are in ALooper::GetNowUs() units. + kFlagIsRealTimeData = 2, }; virtual uint32_t flags() const { return 0; } }; diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index 8dd40d2..d7ac302 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -42,7 +42,7 @@ struct MediaRecorderBase { virtual status_t setVideoFrameRate(int frames_per_second) = 0; virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy) = 0; - virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0; + virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0; virtual status_t setOutputFile(const char *path) = 0; virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0; virtual status_t setOutputFileAuxiliary(int fd) {return INVALID_OPERATION;} diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 3b33479..88a42a0 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -207,7 +207,7 @@ public: void died(); status_t initCheck(); status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy); - status_t setPreviewSurface(const sp<Surface>& surface); + status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface); status_t setVideoSource(int vs); status_t setAudioSource(int as); status_t setOutputFormat(int of); diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index cf38b14..a829916 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -82,7 +82,7 @@ public: uid_t clientUid, Size videoSize, int32_t frameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, bool storeMetaDataInVideoBuffers = false); virtual ~CameraSource(); @@ -154,7 +154,7 @@ protected: sp<Camera> mCamera; sp<ICameraRecordingProxy> mCameraRecordingProxy; sp<DeathNotifier> mDeathNotifier; - sp<Surface> mSurface; + sp<IGraphicBufferProducer> mSurface; sp<MetaData> mMeta; int64_t mStartTimeUs; @@ -169,7 +169,7 @@ protected: CameraSource(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy, int32_t cameraId, const String16& clientName, uid_t clientUid, Size videoSize, int32_t frameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, bool storeMetaDataInVideoBuffers); virtual void startCameraRecording(); diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h index 774772b..6b7a63c 100644 --- a/include/media/stagefright/CameraSourceTimeLapse.h +++ b/include/media/stagefright/CameraSourceTimeLapse.h @@ -40,7 +40,7 @@ public: uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, int64_t timeBetweenTimeLapseFrameCaptureUs); virtual ~CameraSourceTimeLapse(); @@ -115,7 +115,7 @@ private: uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, int64_t timeBetweenTimeLapseFrameCaptureUs); // Wrapper over CameraSource::signalBufferReturned() to implement quick stop. diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index c935d97..8e58162 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -87,12 +87,12 @@ public: return interface_cast<IGraphicBufferProducer>(reply.readStrongBinder()); } - status_t setPreviewSurface(const sp<Surface>& surface) + status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) { ALOGV("setPreviewSurface(%p)", surface.get()); Parcel data, reply; data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); - Surface::writeToParcel(surface, &data); + data.writeStrongBinder(surface->asBinder()); remote()->transact(SET_PREVIEW_SURFACE, data, &reply); return reply.readInt32(); } @@ -443,7 +443,7 @@ status_t BnMediaRecorder::onTransact( case SET_PREVIEW_SURFACE: { ALOGV("SET_PREVIEW_SURFACE"); CHECK_INTERFACE(IMediaRecorder, data, reply); - sp<Surface> surface = Surface::readFromParcel(data); + sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); reply->writeInt32(setPreviewSurface(surface)); return NO_ERROR; } break; diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 3ac98cc..3710e46 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -49,7 +49,7 @@ status_t MediaRecorder::setCamera(const sp<ICamera>& camera, const sp<ICameraRec return ret; } -status_t MediaRecorder::setPreviewSurface(const sp<Surface>& surface) +status_t MediaRecorder::setPreviewSurface(const sp<IGraphicBufferProducer>& surface) { ALOGV("setPreviewSurface(%p)", surface.get()); if (mMediaRecorder == NULL) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index a52b238..a9820e0 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -81,7 +81,7 @@ status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera, return mRecorder->setCamera(camera, proxy); } -status_t MediaRecorderClient::setPreviewSurface(const sp<Surface>& surface) +status_t MediaRecorderClient::setPreviewSurface(const sp<IGraphicBufferProducer>& surface) { ALOGV("setPreviewSurface"); Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index bd0eaf1..a65ec9f 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -32,7 +32,7 @@ class MediaRecorderClient : public BnMediaRecorder public: virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy); - virtual status_t setPreviewSurface(const sp<Surface>& surface); + virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface); virtual status_t setVideoSource(int vs); virtual status_t setAudioSource(int as); virtual status_t setOutputFormat(int of); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f570856..c2c9985 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -224,7 +224,7 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera, return OK; } -status_t StagefrightRecorder::setPreviewSurface(const sp<Surface> &surface) { +status_t StagefrightRecorder::setPreviewSurface(const sp<IGraphicBufferProducer> &surface) { ALOGV("setPreviewSurface: %p", surface.get()); mPreviewSurface = surface; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index fbe6fa6..c864207 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -51,7 +51,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setVideoSize(int width, int height); virtual status_t setVideoFrameRate(int frames_per_second); virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy); - virtual status_t setPreviewSurface(const sp<Surface>& surface); + virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface); virtual status_t setOutputFile(const char *path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); @@ -71,7 +71,7 @@ struct StagefrightRecorder : public MediaRecorderBase { private: sp<ICamera> mCamera; sp<ICameraRecordingProxy> mCameraProxy; - sp<Surface> mPreviewSurface; + sp<IGraphicBufferProducer> mPreviewSurface; sp<IMediaRecorderClient> mListener; String16 mClientName; uid_t mClientUid; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 2ba6c22..5387e1a 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -381,9 +381,16 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mSource->start(); + uint32_t flags = 0; + + if (mSource->isRealTime()) { + flags |= Renderer::FLAG_REAL_TIME; + } + mRenderer = new Renderer( mAudioSink, - new AMessage(kWhatRendererNotify, id())); + new AMessage(kWhatRendererNotify, id()), + flags); looper()->registerHandler(mRenderer); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index 3c63e80..723af09 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -378,6 +378,7 @@ status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) { int mode = request.readInt32(); return mPlayer->setVideoScalingMode(mode); } + default: { return INVALID_OPERATION; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 1ba76a5..404b56f 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -31,9 +31,11 @@ const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll; NuPlayer::Renderer::Renderer( const sp<MediaPlayerBase::AudioSink> &sink, - const sp<AMessage> ¬ify) + const sp<AMessage> ¬ify, + uint32_t flags) : mAudioSink(sink), mNotify(notify), + mFlags(flags), mNumFramesWritten(0), mDrainAudioQueuePending(false), mDrainVideoQueuePending(false), @@ -323,6 +325,11 @@ void NuPlayer::Renderer::postDrainVideoQueue() { if (entry.mBuffer == NULL) { // EOS doesn't carry a timestamp. delayUs = 0; + } else if (mFlags & FLAG_REAL_TIME) { + int64_t mediaTimeUs; + CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + + delayUs = mediaTimeUs - ALooper::GetNowUs(); } else { int64_t mediaTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); @@ -368,12 +375,17 @@ void NuPlayer::Renderer::onDrainVideoQueue() { return; } - int64_t mediaTimeUs; - CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + int64_t realTimeUs; + if (mFlags & FLAG_REAL_TIME) { + CHECK(entry->mBuffer->meta()->findInt64("timeUs", &realTimeUs)); + } else { + int64_t mediaTimeUs; + CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); + + realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; + } - int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs; mVideoLateByUs = ALooper::GetNowUs() - realTimeUs; - bool tooLate = (mVideoLateByUs > 40000); if (tooLate) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index e4368c7..c9796e2 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -25,8 +25,12 @@ namespace android { struct ABuffer; struct NuPlayer::Renderer : public AHandler { + enum Flags { + FLAG_REAL_TIME = 1, + }; Renderer(const sp<MediaPlayerBase::AudioSink> &sink, - const sp<AMessage> ¬ify); + const sp<AMessage> ¬ify, + uint32_t flags = 0); void queueBuffer( bool audio, @@ -79,6 +83,7 @@ private: sp<MediaPlayerBase::AudioSink> mAudioSink; sp<AMessage> mNotify; + uint32_t mFlags; List<QueueEntry> mAudioQueue; List<QueueEntry> mVideoQueue; uint32_t mNumFramesWritten; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 8622abe..1cbf575 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -74,6 +74,10 @@ struct NuPlayer::Source : public AHandler { return INVALID_OPERATION; } + virtual bool isRealTime() const { + return false; + } + protected: virtual ~Source() {} diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index df03f86..28f0d50 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -182,5 +182,9 @@ status_t NuPlayer::StreamingSource::dequeueAccessUnit( return err; } +bool NuPlayer::StreamingSource::isRealTime() const { + return mSource->flags() & IStreamSource::kFlagIsRealTimeData; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index 80b061c..412b6c4 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -38,6 +38,8 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source { virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit); + virtual bool isRealTime() const; + protected: virtual ~StreamingSource(); diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index f8557d0..5a26b06 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -140,7 +140,7 @@ CameraSource *CameraSource::CreateFromCamera( uid_t clientUid, Size videoSize, int32_t frameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, bool storeMetaDataInVideoBuffers) { CameraSource *source = new CameraSource(camera, proxy, cameraId, @@ -157,7 +157,7 @@ CameraSource::CameraSource( uid_t clientUid, Size videoSize, int32_t frameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, bool storeMetaDataInVideoBuffers) : mCameraFlags(0), mNumInputBuffers(0), @@ -536,7 +536,7 @@ status_t CameraSource::initWithCameraAccess( if (mSurface != NULL) { // This CHECK is good, since we just passed the lock/unlock // check earlier by calling mCamera->setParameters(). - CHECK_EQ((status_t)OK, mCamera->setPreviewDisplay(mSurface)); + CHECK_EQ((status_t)OK, mCamera->setPreviewTexture(mSurface)); } // By default, do not store metadata in video buffers diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 2ed2223..20214e8 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -40,7 +40,7 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, int64_t timeBetweenFrameCaptureUs) { CameraSourceTimeLapse *source = new @@ -66,7 +66,7 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( uid_t clientUid, Size videoSize, int32_t videoFrameRate, - const sp<Surface>& surface, + const sp<IGraphicBufferProducer>& surface, int64_t timeBetweenFrameCaptureUs) : CameraSource(camera, proxy, cameraId, clientName, clientUid, videoSize, videoFrameRate, surface, true), diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index b2e60be..56fad60 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -2067,17 +2067,30 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( sampleRate = br.getBits(24); numChannels = br.getBits(4); } else { - static uint32_t kSamplingRate[] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350 - }; - - if (freqIndex == 13 || freqIndex == 14) { - return ERROR_MALFORMED; + numChannels = br.getBits(4); + if (objectType == 5) { + // SBR specific config per 14496-3 table 1.13 + freqIndex = br.getBits(4); + if (freqIndex == 15) { + if (csd_size < 8) { + return ERROR_MALFORMED; + } + sampleRate = br.getBits(24); + } } - sampleRate = kSamplingRate[freqIndex]; - numChannels = br.getBits(4); + if (sampleRate == 0) { + static uint32_t kSamplingRate[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 + }; + + if (freqIndex == 13 || freqIndex == 14) { + return ERROR_MALFORMED; + } + + sampleRate = kSamplingRate[freqIndex]; + } } if (numChannels == 0) { diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index a167b5a..c12572f 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -452,6 +452,10 @@ int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) { timeUs += mParser->mAbsoluteTimeAnchorUs; } + if (mParser->mTimeOffsetValid) { + timeUs += mParser->mTimeOffsetUs; + } + return timeUs; } @@ -930,6 +934,8 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) { ATSParser::ATSParser(uint32_t flags) : mFlags(flags), mAbsoluteTimeAnchorUs(-1ll), + mTimeOffsetValid(false), + mTimeOffsetUs(0ll), mNumTSPacketsParsed(0), mNumPCRs(0) { mPSISections.add(0 /* PID */, new PSISection); @@ -960,6 +966,13 @@ void ATSParser::signalDiscontinuity( CHECK(mPrograms.empty()); mAbsoluteTimeAnchorUs = timeUs; return; + } else if (type == DISCONTINUITY_TIME_OFFSET) { + int64_t offset; + CHECK(extra->findInt64("offset", &offset)); + + mTimeOffsetValid = true; + mTimeOffsetUs = offset; + return; } for (size_t i = 0; i < mPrograms.size(); ++i) { diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 46edc45..a10edc9 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -39,6 +39,7 @@ struct ATSParser : public RefBase { DISCONTINUITY_AUDIO_FORMAT = 2, DISCONTINUITY_VIDEO_FORMAT = 4, DISCONTINUITY_ABSOLUTE_TIME = 8, + DISCONTINUITY_TIME_OFFSET = 16, DISCONTINUITY_SEEK = DISCONTINUITY_TIME, @@ -106,6 +107,9 @@ private: int64_t mAbsoluteTimeAnchorUs; + bool mTimeOffsetValid; + int64_t mTimeOffsetUs; + size_t mNumTSPacketsParsed; void parseProgramAssociationTable(ABitReader *br); diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp index 211e1d1..3854e52 100644 --- a/media/libstagefright/omx/GraphicBufferSource.cpp +++ b/media/libstagefright/omx/GraphicBufferSource.cpp @@ -32,7 +32,7 @@ static const bool EXTRA_CHECK = true; GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight) : + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) : mInitCheck(UNKNOWN_ERROR), mNodeInstance(nodeInstance), mExecuting(false), @@ -40,20 +40,31 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, mEndOfStream(false), mEndOfStreamSent(false) { - ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight); + ALOGV("GraphicBufferSource w=%u h=%u c=%u", + bufferWidth, bufferHeight, bufferCount); if (bufferWidth == 0 || bufferHeight == 0) { - ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight); + ALOGE("Invalid dimensions %ux%u", bufferWidth, bufferHeight); mInitCheck = BAD_VALUE; return; } + String8 name("GraphicBufferSource"); + mBufferQueue = new BufferQueue(true); + mBufferQueue->setConsumerName(name); mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); mBufferQueue->setSynchronousMode(true); mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE); + mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount); + if (mInitCheck != NO_ERROR) { + ALOGE("Unable to set BQ max acquired buffer count to %u: %d", + bufferCount, mInitCheck); + return; + } + // Note that we can't create an sp<...>(this) in a ctor that will not keep a // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> @@ -64,21 +75,23 @@ GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, sp<BufferQueue::ConsumerListener> proxy; proxy = new BufferQueue::ProxyConsumerListener(listener); - status_t err = mBufferQueue->consumerConnect(proxy); - if (err != NO_ERROR) { + mInitCheck = mBufferQueue->consumerConnect(proxy); + if (mInitCheck != NO_ERROR) { ALOGE("Error connecting to BufferQueue: %s (%d)", - strerror(-err), err); + strerror(-mInitCheck), mInitCheck); return; } - mInitCheck = OK; + CHECK(mInitCheck == NO_ERROR); } GraphicBufferSource::~GraphicBufferSource() { ALOGV("~GraphicBufferSource"); - status_t err = mBufferQueue->consumerDisconnect(); - if (err != NO_ERROR) { - ALOGW("consumerDisconnect failed: %d", err); + if (mBufferQueue != NULL) { + status_t err = mBufferQueue->consumerDisconnect(); + if (err != NO_ERROR) { + ALOGW("consumerDisconnect failed: %d", err); + } } } @@ -98,8 +111,12 @@ void GraphicBufferSource::omxExecuting() { // one codec buffer simultaneously. (We could instead try to submit // all BQ buffers whenever any codec buffer is freed, but if we get the // initial conditions right that will never be useful.) - while (mNumFramesAvailable && isCodecBufferAvailable_l()) { - fillCodecBuffer_l(); + while (mNumFramesAvailable) { + if (!fillCodecBuffer_l()) { + ALOGV("stop load with frames available (codecAvail=%d)", + isCodecBufferAvailable_l()); + break; + } } ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable); @@ -166,7 +183,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { // see if the GraphicBuffer reference was null, which should only ever // happen for EOS. if (codecBuffer.mGraphicBuffer == NULL) { - CHECK(mEndOfStream); + CHECK(mEndOfStream && mEndOfStreamSent); // No GraphicBuffer to deal with, no additional input or output is // expected, so just return. return; @@ -216,8 +233,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { if (mNumFramesAvailable) { // Fill this codec buffer. - CHECK(!mEndOfStream); - ALOGV("buffer freed, %d frames avail", mNumFramesAvailable); + CHECK(!mEndOfStreamSent); + ALOGV("buffer freed, %d frames avail (eos=%d)", + mNumFramesAvailable, mEndOfStream); fillCodecBuffer_l(); } else if (mEndOfStream) { // No frames available, but EOS is pending, so use this buffer to @@ -228,56 +246,58 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { return; } -status_t GraphicBufferSource::fillCodecBuffer_l() { +bool GraphicBufferSource::fillCodecBuffer_l() { CHECK(mExecuting && mNumFramesAvailable > 0); + int cbi = findAvailableCodecBuffer_l(); if (cbi < 0) { // No buffers available, bail. ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d", mNumFramesAvailable); - } else { - ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", - mNumFramesAvailable); - BufferQueue::BufferItem item; - status_t err = mBufferQueue->acquireBuffer(&item); - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // shouldn't happen - ALOGW("fillCodecBuffer_l: frame was not available"); - return err; - } else if (err != OK) { - // now what? fake end-of-stream? - ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); - return err; - } + return false; + } - mNumFramesAvailable--; + ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", + mNumFramesAvailable); + BufferQueue::BufferItem item; + status_t err = mBufferQueue->acquireBuffer(&item); + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // shouldn't happen + ALOGW("fillCodecBuffer_l: frame was not available"); + return false; + } else if (err != OK) { + // now what? fake end-of-stream? + ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); + return false; + } - // Wait for it to become available. - err = item.mFence->waitForever(1000, - "GraphicBufferSource::fillCodecBuffer_l"); - if (err != OK) { - ALOGW("failed to wait for buffer fence: %d", err); - // keep going - } + mNumFramesAvailable--; - // If this is the first time we're seeing this buffer, add it to our - // slot table. - if (item.mGraphicBuffer != NULL) { - ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); - mBufferSlot[item.mBuf] = item.mGraphicBuffer; - } + // Wait for it to become available. + err = item.mFence->waitForever(1000, + "GraphicBufferSource::fillCodecBuffer_l"); + if (err != OK) { + ALOGW("failed to wait for buffer fence: %d", err); + // keep going + } - err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi); - if (err != OK) { - ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + // If this is the first time we're seeing this buffer, add it to our + // slot table. + if (item.mGraphicBuffer != NULL) { + ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); + mBufferSlot[item.mBuf] = item.mGraphicBuffer; + } + + err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi); + if (err != OK) { + ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); + mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); - } else { - ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); - } + } else { + ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); } - return OK; + return true; } status_t GraphicBufferSource::signalEndOfInputStream() { @@ -372,6 +392,7 @@ void GraphicBufferSource::submitEndOfInputStream_l() { } else { ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d", header, cbi); + mEndOfStreamSent = true; } } @@ -400,7 +421,8 @@ int GraphicBufferSource::findMatchingCodecBuffer_l( void GraphicBufferSource::onFrameAvailable() { Mutex::Autolock autoLock(mMutex); - ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable); + ALOGV("onFrameAvailable exec=%d avail=%d", + mExecuting, mNumFramesAvailable); if (mEndOfStream) { // This should only be possible if a new buffer was queued after diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h index 6a34bc5..7f1f22e 100644 --- a/media/libstagefright/omx/GraphicBufferSource.h +++ b/media/libstagefright/omx/GraphicBufferSource.h @@ -47,7 +47,7 @@ namespace android { class GraphicBufferSource : public BufferQueue::ConsumerListener { public: GraphicBufferSource(OMXNodeInstance* nodeInstance, - uint32_t bufferWidth, uint32_t bufferHeight); + uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount); virtual ~GraphicBufferSource(); // We can't throw an exception if the constructor fails, so we just set @@ -124,7 +124,9 @@ private: // in the onFrameAvailable callback, or if we're in codecBufferEmptied // and mNumFramesAvailable is nonzero). Returns without doing anything if // we don't have a codec buffer available. - status_t fillCodecBuffer_l(); + // + // Returns true if we successfully filled a codec buffer with a BQ buffer. + bool fillCodecBuffer_l(); // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer // reference into the codec buffer, and submits the data to the codec. diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index f3d8d14..46ff22f 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -590,7 +590,8 @@ status_t OMXNodeInstance::createInputSurface( } GraphicBufferSource* bufferSource = new GraphicBufferSource( - this, def.format.video.nFrameWidth, def.format.video.nFrameHeight); + this, def.format.video.nFrameWidth, def.format.video.nFrameHeight, + def.nBufferCountActual); if ((err = bufferSource->initCheck()) != OK) { delete bufferSource; return err; diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index cb6011c..23bb04e 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -27,6 +27,7 @@ #include <net/if.h> #include <netdb.h> #include <netinet/in.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <media/stagefright/foundation/ABuffer.h> @@ -103,6 +104,8 @@ private: AString mInBuffer; + int64_t mLastStallReportUs; + void notifyError(bool send, status_t err, const char *detail); void notify(NotificationReason reason); @@ -136,7 +139,8 @@ ANetworkSession::Session::Session( mSocket(s), mNotify(notify), mSawReceiveFailure(false), - mSawSendFailure(false) { + mSawSendFailure(false), + mLastStallReportUs(-1ll) { if (mState == CONNECTED) { struct sockaddr_in localAddr; socklen_t localAddrLen = sizeof(localAddr); @@ -507,6 +511,29 @@ status_t ANetworkSession::Session::writeMore() { mSawSendFailure = true; } +#if 1 + int numBytesQueued; + int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); + if (res == 0 && numBytesQueued > 50 * 1024) { + if (numBytesQueued > 409600) { + ALOGW("!!! numBytesQueued = %d", numBytesQueued); + } + + int64_t nowUs = ALooper::GetNowUs(); + + if (mLastStallReportUs < 0ll + || nowUs > mLastStallReportUs + 500000ll) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatNetworkStall); + msg->setSize("numBytesQueued", numBytesQueued); + msg->post(); + + mLastStallReportUs = nowUs; + } + } +#endif + return err; } diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h index c1acdcc..0d7cbd6 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ b/media/libstagefright/wifi-display/ANetworkSession.h @@ -83,6 +83,7 @@ struct ANetworkSession : public RefBase { kWhatData, kWhatDatagram, kWhatBinaryData, + kWhatNetworkStall, }; protected: diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 19f560c..f81929c 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \ sink/TunnelRenderer.cpp \ sink/WifiDisplaySink.cpp \ SNTPClient.cpp \ + TimeSyncer.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ source/PlaybackSession.cpp \ diff --git a/media/libstagefright/wifi-display/MediaReceiver.cpp b/media/libstagefright/wifi-display/MediaReceiver.cpp index 3c92d41..10a2dff 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.cpp +++ b/media/libstagefright/wifi-display/MediaReceiver.cpp @@ -127,7 +127,10 @@ void MediaReceiver::onMessageReceived(const sp<AMessage> &msg) { notifyInitDone(mInitStatus); } - mTSParser = new ATSParser(ATSParser::ALIGNED_VIDEO_DATA); + mTSParser = new ATSParser( + ATSParser::ALIGNED_VIDEO_DATA + | ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); + mFormatKnownMask = 0; break; } @@ -306,6 +309,15 @@ void MediaReceiver::postAccessUnit( notify->post(); } +status_t MediaReceiver::notifyLateness(size_t trackIndex, int64_t latenessUs) { + if (trackIndex >= mTrackInfos.size()) { + return -ERANGE; + } + + TrackInfo *info = &mTrackInfos.editItemAt(trackIndex); + return info->mReceiver->notifyLateness(latenessUs); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/MediaReceiver.h b/media/libstagefright/wifi-display/MediaReceiver.h index 7adc3c4..cdfde99 100644 --- a/media/libstagefright/wifi-display/MediaReceiver.h +++ b/media/libstagefright/wifi-display/MediaReceiver.h @@ -60,6 +60,8 @@ struct MediaReceiver : public AHandler { }; status_t initAsync(Mode mode); + status_t notifyLateness(size_t trackIndex, int64_t latenessUs); + protected: virtual void onMessageReceived(const sp<AMessage> &msg); virtual ~MediaReceiver(); diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 105c642..e1e957a 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -325,6 +325,15 @@ void MediaSender::onSenderNotify(const sp<AMessage> &msg) { break; } + case kWhatNetworkStall: + { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + notifyNetworkStall(numBytesQueued); + break; + } + default: TRESPASS(); } @@ -344,6 +353,13 @@ void MediaSender::notifyError(status_t err) { notify->post(); } +void MediaSender::notifyNetworkStall(size_t numBytesQueued) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatNetworkStall); + notify->setSize("numBytesQueued", numBytesQueued); + notify->post(); +} + status_t MediaSender::packetizeAccessUnit( size_t trackIndex, sp<ABuffer> accessUnit, diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 9a50f9a..447abf7 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -42,6 +42,7 @@ struct MediaSender : public AHandler { enum { kWhatInitDone, kWhatError, + kWhatNetworkStall, }; MediaSender( @@ -113,6 +114,7 @@ private: void notifyInitDone(status_t err); void notifyError(status_t err); + void notifyNetworkStall(size_t numBytesQueued); status_t packetizeAccessUnit( size_t trackIndex, diff --git a/media/libstagefright/wifi-display/TimeSyncer.cpp b/media/libstagefright/wifi-display/TimeSyncer.cpp new file mode 100644 index 0000000..64e182e --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.cpp @@ -0,0 +1,332 @@ +/* + * Copyright 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_NEBUG 0 +#define LOG_TAG "TimeSyncer" +#include <utils/Log.h> + +#include "TimeSyncer.h" + +#include "ANetworkSession.h" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AHandler.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/Utils.h> + +namespace android { + +TimeSyncer::TimeSyncer( + const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify) + : mNetSession(netSession), + mNotify(notify), + mIsServer(false), + mConnected(false), + mUDPSession(0), + mSeqNo(0), + mTotalTimeUs(0.0), + mPendingT1(0ll), + mTimeoutGeneration(0) { +} + +TimeSyncer::~TimeSyncer() { +} + +void TimeSyncer::startServer(unsigned localPort) { + sp<AMessage> msg = new AMessage(kWhatStartServer, id()); + msg->setInt32("localPort", localPort); + msg->post(); +} + +void TimeSyncer::startClient(const char *remoteHost, unsigned remotePort) { + sp<AMessage> msg = new AMessage(kWhatStartClient, id()); + msg->setString("remoteHost", remoteHost); + msg->setInt32("remotePort", remotePort); + msg->post(); +} + +void TimeSyncer::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatStartClient: + { + AString remoteHost; + CHECK(msg->findString("remoteHost", &remoteHost)); + + int32_t remotePort; + CHECK(msg->findInt32("remotePort", &remotePort)); + + sp<AMessage> notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + 0 /* localPort */, + remoteHost.c_str(), + remotePort, + notify, + &mUDPSession)); + + postSendPacket(); + break; + } + + case kWhatStartServer: + { + mIsServer = true; + + int32_t localPort; + CHECK(msg->findInt32("localPort", &localPort)); + + sp<AMessage> notify = new AMessage(kWhatUDPNotify, id()); + + CHECK_EQ((status_t)OK, + mNetSession->createUDPSession( + localPort, notify, &mUDPSession)); + + break; + } + + case kWhatSendPacket: + { + TimeInfo ti; + memset(&ti, 0, sizeof(ti)); + + ti.mT1 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, &ti, sizeof(ti))); + + mPendingT1 = ti.mT1; + postTimeout(); + break; + } + + case kWhatTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mTimeoutGeneration) { + break; + } + + ALOGI("timed out, sending another request"); + postSendPacket(); + break; + } + + case kWhatUDPNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred in session %d (%d, '%s/%s').", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + mNetSession->destroySession(sessionID); + + cancelTimeout(); + + notifyError(err); + break; + } + + case ANetworkSession::kWhatDatagram: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + sp<ABuffer> packet; + CHECK(msg->findBuffer("data", &packet)); + + int64_t arrivalTimeUs; + CHECK(packet->meta()->findInt64( + "arrivalTimeUs", &arrivalTimeUs)); + + CHECK_EQ(packet->size(), sizeof(TimeInfo)); + + TimeInfo *ti = (TimeInfo *)packet->data(); + + if (mIsServer) { + if (!mConnected) { + AString fromAddr; + CHECK(msg->findString("fromAddr", &fromAddr)); + + int32_t fromPort; + CHECK(msg->findInt32("fromPort", &fromPort)); + + CHECK_EQ((status_t)OK, + mNetSession->connectUDPSession( + mUDPSession, fromAddr.c_str(), fromPort)); + + mConnected = true; + } + + ti->mT2 = arrivalTimeUs; + ti->mT3 = ALooper::GetNowUs(); + + CHECK_EQ((status_t)OK, + mNetSession->sendRequest( + mUDPSession, ti, sizeof(*ti))); + } else { + if (ti->mT1 != mPendingT1) { + break; + } + + cancelTimeout(); + mPendingT1 = 0; + + ti->mT4 = arrivalTimeUs; + + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + mHistory.push_back(*ti); + + ALOGV("delay = %lld us,\toffset %lld us", + delay, + offset); + + if (mHistory.size() < kNumPacketsPerBatch) { + postSendPacket(1000000ll / 30); + } else { + notifyOffset(); + + mHistory.clear(); + postSendPacket(kBatchDelayUs); + } + } + break; + } + + default: + TRESPASS(); + } + + break; + } + + default: + TRESPASS(); + } +} + +void TimeSyncer::postSendPacket(int64_t delayUs) { + (new AMessage(kWhatSendPacket, id()))->post(delayUs); +} + +void TimeSyncer::postTimeout() { + sp<AMessage> msg = new AMessage(kWhatTimedOut, id()); + msg->setInt32("generation", mTimeoutGeneration); + msg->post(kTimeoutDelayUs); +} + +void TimeSyncer::cancelTimeout() { + ++mTimeoutGeneration; +} + +void TimeSyncer::notifyError(status_t err) { + if (mNotify == NULL) { + looper()->stop(); + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatError); + notify->setInt32("err", err); + notify->post(); +} + +// static +int TimeSyncer::CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2) { + int64_t rt1 = ti1->mT4 - ti1->mT1; + int64_t rt2 = ti2->mT4 - ti2->mT1; + + if (rt1 < rt2) { + return -1; + } else if (rt1 > rt2) { + return 1; + } + + return 0; +} + +void TimeSyncer::notifyOffset() { + mHistory.sort(CompareRountripTime); + + int64_t sum = 0ll; + size_t count = 0; + + // Only consider the third of the information associated with the best + // (smallest) roundtrip times. + for (size_t i = 0; i < mHistory.size() / 3; ++i) { + const TimeInfo *ti = &mHistory[i]; + +#if 0 + // One way delay for a packet to travel from client + // to server or back (assumed to be the same either way). + int64_t delay = + (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; +#endif + + // Offset between the client clock (T1, T4) and the + // server clock (T2, T3) timestamps. + int64_t offset = + (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; + + ALOGV("(%d) RT: %lld us, offset: %lld us", + i, ti->mT4 - ti->mT1, offset); + + sum += offset; + ++count; + } + + if (mNotify == NULL) { + ALOGI("avg. offset is %lld", sum / count); + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatTimeOffset); + notify->setInt64("offset", sum / count); + notify->post(); +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSyncer.h b/media/libstagefright/wifi-display/TimeSyncer.h new file mode 100644 index 0000000..0e3aed7 --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSyncer.h @@ -0,0 +1,109 @@ +/* + * Copyright 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 TIME_SYNCER_H_ + +#define TIME_SYNCER_H_ + +#include <media/stagefright/foundation/AHandler.h> + +namespace android { + +struct ANetworkSession; + +/* + TimeSyncer allows us to synchronize time between a client and a server. + The client sends a UDP packet containing its send-time to the server, + the server sends that packet back to the client amended with information + about when it was received as well as the time the reply was sent back. + Finally the client receives the reply and has now enough information to + compute the clock offset between client and server assuming that packet + exchange is symmetric, i.e. time for a packet client->server and + server->client is roughly equal. + This exchange is repeated a number of times and the average offset computed + over the 30% of packets that had the lowest roundtrip times. + The offset is determined every 10 secs to account for slight differences in + clock frequency. +*/ +struct TimeSyncer : public AHandler { + enum { + kWhatError, + kWhatTimeOffset, + }; + TimeSyncer( + const sp<ANetworkSession> &netSession, + const sp<AMessage> ¬ify); + + void startServer(unsigned localPort); + void startClient(const char *remoteHost, unsigned remotePort); + +protected: + virtual ~TimeSyncer(); + + virtual void onMessageReceived(const sp<AMessage> &msg); + +private: + enum { + kWhatStartServer, + kWhatStartClient, + kWhatUDPNotify, + kWhatSendPacket, + kWhatTimedOut, + }; + + struct TimeInfo { + int64_t mT1; // client timestamp at send + int64_t mT2; // server timestamp at receive + int64_t mT3; // server timestamp at send + int64_t mT4; // client timestamp at receive + }; + + enum { + kNumPacketsPerBatch = 30, + }; + static const int64_t kTimeoutDelayUs = 500000ll; + static const int64_t kBatchDelayUs = 10000000ll; // every 10 secs + + sp<ANetworkSession> mNetSession; + sp<AMessage> mNotify; + + bool mIsServer; + bool mConnected; + int32_t mUDPSession; + uint32_t mSeqNo; + double mTotalTimeUs; + + Vector<TimeInfo> mHistory; + + int64_t mPendingT1; + int32_t mTimeoutGeneration; + + void postSendPacket(int64_t delayUs = 0ll); + + void postTimeout(); + void cancelTimeout(); + + void notifyError(status_t err); + void notifyOffset(); + + static int CompareRountripTime(const TimeInfo *ti1, const TimeInfo *ti2); + + DISALLOW_EVIL_CONSTRUCTORS(TimeSyncer); +}; + +} // namespace android + +#endif // TIME_SYNCER_H_ diff --git a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp index d0ab60d..5f189e7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPAssembler.cpp @@ -53,6 +53,11 @@ void RTPReceiver::TSAssembler::signalDiscontinuity() { } status_t RTPReceiver::TSAssembler::processPacket(const sp<ABuffer> &packet) { + int32_t rtpTime; + CHECK(packet->meta()->findInt32("rtp-time", &rtpTime)); + + packet->meta()->setInt64("timeUs", (rtpTime * 100ll) / 9); + postAccessUnit(packet, mSawDiscontinuity); if (mSawDiscontinuity) { diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp index 29482af..8711b08 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.cpp @@ -221,10 +221,12 @@ void RTPReceiver::Source::dequeueMore() { mNumDeclaredLostPrior = mNumDeclaredLost; - ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", - lostInterval, - 100.0f * lostInterval / expectedInterval, - declaredLostInterval); + if (declaredLostInterval > 0) { + ALOGI("lost %lld packets (%.2f %%), declared %d lost\n", + lostInterval, + 100.0f * lostInterval / expectedInterval, + declaredLostInterval); + } } mNextReportTimeUs = nowUs + kReportIntervalUs; @@ -530,6 +532,40 @@ status_t RTPReceiver::connect( return OK; } +status_t RTPReceiver::notifyLateness(int64_t latenessUs) { + sp<ABuffer> buf = new ABuffer(20); + + uint8_t *ptr = buf->data(); + ptr[0] = 0x80 | 0; + ptr[1] = 204; // APP + ptr[2] = 0; + + CHECK((buf->size() % 4) == 0u); + ptr[3] = (buf->size() / 4) - 1; + + ptr[4] = kSourceID >> 24; // SSRC + ptr[5] = (kSourceID >> 16) & 0xff; + ptr[6] = (kSourceID >> 8) & 0xff; + ptr[7] = kSourceID & 0xff; + ptr[8] = 'l'; + ptr[9] = 'a'; + ptr[10] = 't'; + ptr[11] = 'e'; + + ptr[12] = latenessUs >> 56; + ptr[13] = (latenessUs >> 48) & 0xff; + ptr[14] = (latenessUs >> 40) & 0xff; + ptr[15] = (latenessUs >> 32) & 0xff; + ptr[16] = (latenessUs >> 24) & 0xff; + ptr[17] = (latenessUs >> 16) & 0xff; + ptr[18] = (latenessUs >> 8) & 0xff; + ptr[19] = latenessUs & 0xff; + + mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size()); + + return OK; +} + void RTPReceiver::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatRTPNotify: diff --git a/media/libstagefright/wifi-display/rtp/RTPReceiver.h b/media/libstagefright/wifi-display/rtp/RTPReceiver.h index 2ae864a..ec4671d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPReceiver.h +++ b/media/libstagefright/wifi-display/rtp/RTPReceiver.h @@ -53,6 +53,8 @@ struct RTPReceiver : public RTPBase, public AHandler { int32_t remoteRTPPort, int32_t remoteRTCPPort); + status_t notifyLateness(int64_t latenessUs); + protected: virtual ~RTPReceiver(); virtual void onMessageReceived(const sp<AMessage> &msg); diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 85c5933..8cd712d 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -530,6 +530,18 @@ void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) { } break; } + + case ANetworkSession::kWhatNetworkStall: + { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + notifyNetworkStall(numBytesQueued); + break; + } + + default: + TRESPASS(); } } @@ -577,6 +589,8 @@ status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) { case 202: // SDES case 203: + break; + case 204: // APP break; @@ -697,5 +711,12 @@ void RTPSender::notifyError(status_t err) { notify->post(); } +void RTPSender::notifyNetworkStall(size_t numBytesQueued) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatNetworkStall); + notify->setSize("numBytesQueued", numBytesQueued); + notify->post(); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 2b683a4..83c6223 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -36,6 +36,7 @@ struct RTPSender : public RTPBase, public AHandler { enum { kWhatInitDone, kWhatError, + kWhatNetworkStall, }; RTPSender( const sp<ANetworkSession> &netSession, @@ -103,6 +104,7 @@ private: void notifyInitDone(status_t err); void notifyError(status_t err); + void notifyNetworkStall(size_t numBytesQueued); DISALLOW_EVIL_CONSTRUCTORS(RTPSender); }; diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp index b53252d..12338e9 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.cpp @@ -22,6 +22,7 @@ #include <gui/SurfaceComposerClient.h> #include <gui/Surface.h> +#include <media/AudioTrack.h> #include <media/ICrypto.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -34,133 +35,208 @@ namespace android { -DirectRenderer::DirectRenderer( - const sp<IGraphicBufferProducer> &bufferProducer) - : mSurfaceTex(bufferProducer), - mVideoDecoderNotificationPending(false), - mRenderPending(false), - mFirstRenderTimeUs(-1ll), - mFirstRenderRealUs(-1ll) { -} +/* + Drives the decoding process using a MediaCodec instance. Input buffers + queued by calls to "queueInputBuffer" are fed to the decoder as soon + as the decoder is ready for them, the client is notified about output + buffers as the decoder spits them out. +*/ +struct DirectRenderer::DecoderContext : public AHandler { + enum { + kWhatOutputBufferReady, + }; + DecoderContext(const sp<AMessage> ¬ify); -DirectRenderer::~DirectRenderer() { - if (mVideoDecoder != NULL) { - mVideoDecoder->release(); - mVideoDecoder.clear(); + status_t init( + const sp<AMessage> &format, + const sp<IGraphicBufferProducer> &surfaceTex); - mVideoDecoderLooper->stop(); - mVideoDecoderLooper.clear(); - } -} + void queueInputBuffer(const sp<ABuffer> &accessUnit); -void DirectRenderer::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatVideoDecoderNotify: - { - onVideoDecoderNotify(); - break; - } + status_t renderOutputBufferAndRelease(size_t index); + status_t releaseOutputBuffer(size_t index); - case kWhatRender: - { - onRender(); - break; - } +protected: + virtual ~DecoderContext(); - default: - TRESPASS(); - } + virtual void onMessageReceived(const sp<AMessage> &msg); + +private: + enum { + kWhatDecoderNotify, + }; + + sp<AMessage> mNotify; + sp<ALooper> mDecoderLooper; + sp<MediaCodec> mDecoder; + Vector<sp<ABuffer> > mDecoderInputBuffers; + Vector<sp<ABuffer> > mDecoderOutputBuffers; + List<size_t> mDecoderInputBuffersAvailable; + bool mDecoderNotificationPending; + + List<sp<ABuffer> > mAccessUnits; + + void onDecoderNotify(); + void scheduleDecoderNotification(); + void queueDecoderInputBuffers(); + + void queueOutputBuffer( + size_t index, int64_t timeUs, const sp<ABuffer> &buffer); + + DISALLOW_EVIL_CONSTRUCTORS(DecoderContext); +}; + +//////////////////////////////////////////////////////////////////////////////// + +/* + A "push" audio renderer. The primary function of this renderer is to use + an AudioTrack in push mode and making sure not to block the event loop + be ensuring that calls to AudioTrack::write never block. This is done by + estimating an upper bound of data that can be written to the AudioTrack + buffer without delay. +*/ +struct DirectRenderer::AudioRenderer : public AHandler { + AudioRenderer(const sp<DecoderContext> &decoderContext); + + void queueInputBuffer( + size_t index, int64_t timeUs, const sp<ABuffer> &buffer); + +protected: + virtual ~AudioRenderer(); + virtual void onMessageReceived(const sp<AMessage> &msg); + +private: + enum { + kWhatPushAudio, + }; + + struct BufferInfo { + size_t mIndex; + int64_t mTimeUs; + sp<ABuffer> mBuffer; + }; + + sp<DecoderContext> mDecoderContext; + sp<AudioTrack> mAudioTrack; + + List<BufferInfo> mInputBuffers; + bool mPushPending; + + size_t mNumFramesWritten; + + void schedulePushIfNecessary(); + void onPushAudio(); + + ssize_t writeNonBlocking(const uint8_t *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(AudioRenderer); +}; + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::DecoderContext::DecoderContext(const sp<AMessage> ¬ify) + : mNotify(notify), + mDecoderNotificationPending(false) { } -void DirectRenderer::setFormat( - size_t trackIndex, const sp<AMessage> &format) { - if (trackIndex == 1) { - // Ignore audio for now. - return; +DirectRenderer::DecoderContext::~DecoderContext() { + if (mDecoder != NULL) { + mDecoder->release(); + mDecoder.clear(); + + mDecoderLooper->stop(); + mDecoderLooper.clear(); } +} - CHECK(mVideoDecoder == NULL); +status_t DirectRenderer::DecoderContext::init( + const sp<AMessage> &format, + const sp<IGraphicBufferProducer> &surfaceTex) { + CHECK(mDecoder == NULL); AString mime; CHECK(format->findString("mime", &mime)); - mVideoDecoderLooper = new ALooper; - mVideoDecoderLooper->setName("video codec looper"); + mDecoderLooper = new ALooper; + mDecoderLooper->setName("video codec looper"); - mVideoDecoderLooper->start( + mDecoderLooper->start( false /* runOnCallingThread */, false /* canCallJava */, PRIORITY_DEFAULT); - mVideoDecoder = MediaCodec::CreateByType( - mVideoDecoderLooper, mime.c_str(), false /* encoder */); + mDecoder = MediaCodec::CreateByType( + mDecoderLooper, mime.c_str(), false /* encoder */); - CHECK(mVideoDecoder != NULL); + CHECK(mDecoder != NULL); - status_t err = mVideoDecoder->configure( + status_t err = mDecoder->configure( format, - mSurfaceTex == NULL - ? NULL : new Surface(mSurfaceTex), + surfaceTex == NULL + ? NULL : new Surface(surfaceTex), NULL /* crypto */, 0 /* flags */); CHECK_EQ(err, (status_t)OK); - err = mVideoDecoder->start(); + err = mDecoder->start(); CHECK_EQ(err, (status_t)OK); - err = mVideoDecoder->getInputBuffers( - &mVideoDecoderInputBuffers); + err = mDecoder->getInputBuffers( + &mDecoderInputBuffers); CHECK_EQ(err, (status_t)OK); - scheduleVideoDecoderNotification(); + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); + + scheduleDecoderNotification(); + + return OK; } -void DirectRenderer::queueAccessUnit( - size_t trackIndex, const sp<ABuffer> &accessUnit) { - if (trackIndex == 1) { - // Ignore audio for now. - return; - } +void DirectRenderer::DecoderContext::queueInputBuffer( + const sp<ABuffer> &accessUnit) { + CHECK(mDecoder != NULL); - if (mVideoDecoder == NULL) { - sp<AMessage> format = new AMessage; - format->setString("mime", "video/avc"); - format->setInt32("width", 640); - format->setInt32("height", 360); + mAccessUnits.push_back(accessUnit); + queueDecoderInputBuffers(); +} - setFormat(0, format); - } +status_t DirectRenderer::DecoderContext::renderOutputBufferAndRelease( + size_t index) { + return mDecoder->renderOutputBufferAndRelease(index); +} - mVideoAccessUnits.push_back(accessUnit); - queueVideoDecoderInputBuffers(); +status_t DirectRenderer::DecoderContext::releaseOutputBuffer(size_t index) { + return mDecoder->releaseOutputBuffer(index); } -void DirectRenderer::queueVideoDecoderInputBuffers() { - if (mVideoDecoder == NULL) { +void DirectRenderer::DecoderContext::queueDecoderInputBuffers() { + if (mDecoder == NULL) { return; } bool submittedMore = false; - while (!mVideoAccessUnits.empty() - && !mVideoDecoderInputBuffersAvailable.empty()) { - size_t index = *mVideoDecoderInputBuffersAvailable.begin(); + while (!mAccessUnits.empty() + && !mDecoderInputBuffersAvailable.empty()) { + size_t index = *mDecoderInputBuffersAvailable.begin(); - mVideoDecoderInputBuffersAvailable.erase( - mVideoDecoderInputBuffersAvailable.begin()); + mDecoderInputBuffersAvailable.erase( + mDecoderInputBuffersAvailable.begin()); - sp<ABuffer> srcBuffer = *mVideoAccessUnits.begin(); - mVideoAccessUnits.erase(mVideoAccessUnits.begin()); + sp<ABuffer> srcBuffer = *mAccessUnits.begin(); + mAccessUnits.erase(mAccessUnits.begin()); const sp<ABuffer> &dstBuffer = - mVideoDecoderInputBuffers.itemAt(index); + mDecoderInputBuffers.itemAt(index); memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); int64_t timeUs; CHECK(srcBuffer->meta()->findInt64("timeUs", &timeUs)); - status_t err = mVideoDecoder->queueInputBuffer( + status_t err = mDecoder->queueInputBuffer( index, 0 /* offset */, srcBuffer->size(), @@ -172,19 +248,33 @@ void DirectRenderer::queueVideoDecoderInputBuffers() { } if (submittedMore) { - scheduleVideoDecoderNotification(); + scheduleDecoderNotification(); } } -void DirectRenderer::onVideoDecoderNotify() { - mVideoDecoderNotificationPending = false; +void DirectRenderer::DecoderContext::onMessageReceived( + const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::DecoderContext::onDecoderNotify() { + mDecoderNotificationPending = false; for (;;) { size_t index; - status_t err = mVideoDecoder->dequeueInputBuffer(&index); + status_t err = mDecoder->dequeueInputBuffer(&index); if (err == OK) { - mVideoDecoderInputBuffersAvailable.push_back(index); + mDecoderInputBuffersAvailable.push_back(index); } else if (err == -EAGAIN) { break; } else { @@ -192,7 +282,7 @@ void DirectRenderer::onVideoDecoderNotify() { } } - queueVideoDecoderInputBuffers(); + queueDecoderInputBuffers(); for (;;) { size_t index; @@ -200,7 +290,7 @@ void DirectRenderer::onVideoDecoderNotify() { size_t size; int64_t timeUs; uint32_t flags; - status_t err = mVideoDecoder->dequeueOutputBuffer( + status_t err = mDecoder->dequeueOutputBuffer( &index, &offset, &size, @@ -208,9 +298,12 @@ void DirectRenderer::onVideoDecoderNotify() { &flags); if (err == OK) { - queueOutputBuffer(index, timeUs); + queueOutputBuffer( + index, timeUs, mDecoderOutputBuffers.itemAt(index)); } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { - // We don't care. + err = mDecoder->getOutputBuffers( + &mDecoderOutputBuffers); + CHECK_EQ(err, (status_t)OK); } else if (err == INFO_FORMAT_CHANGED) { // We don't care. } else if (err == -EAGAIN) { @@ -220,75 +313,336 @@ void DirectRenderer::onVideoDecoderNotify() { } } - scheduleVideoDecoderNotification(); + scheduleDecoderNotification(); } -void DirectRenderer::queueOutputBuffer(size_t index, int64_t timeUs) { -#if 0 - OutputInfo info; +void DirectRenderer::DecoderContext::scheduleDecoderNotification() { + if (mDecoderNotificationPending) { + return; + } + + sp<AMessage> notify = + new AMessage(kWhatDecoderNotify, id()); + + mDecoder->requestActivityNotification(notify); + mDecoderNotificationPending = true; +} + +void DirectRenderer::DecoderContext::queueOutputBuffer( + size_t index, int64_t timeUs, const sp<ABuffer> &buffer) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("what", kWhatOutputBufferReady); + msg->setSize("index", index); + msg->setInt64("timeUs", timeUs); + msg->setBuffer("buffer", buffer); + msg->post(); +} + +//////////////////////////////////////////////////////////////////////////////// + +DirectRenderer::AudioRenderer::AudioRenderer( + const sp<DecoderContext> &decoderContext) + : mDecoderContext(decoderContext), + mPushPending(false), + mNumFramesWritten(0) { + mAudioTrack = new AudioTrack( + AUDIO_STREAM_DEFAULT, + 48000.0f, + AUDIO_FORMAT_PCM, + AUDIO_CHANNEL_OUT_STEREO, + (int)0 /* frameCount */); + + CHECK_EQ((status_t)OK, mAudioTrack->initCheck()); + + mAudioTrack->start(); +} + +DirectRenderer::AudioRenderer::~AudioRenderer() { +} + +void DirectRenderer::AudioRenderer::queueInputBuffer( + size_t index, int64_t timeUs, const sp<ABuffer> &buffer) { + BufferInfo info; info.mIndex = index; info.mTimeUs = timeUs; - mOutputBuffers.push_back(info); + info.mBuffer = buffer; - scheduleRenderIfNecessary(); -#else - status_t err = mVideoDecoder->renderOutputBufferAndRelease(index); - CHECK_EQ(err, (status_t)OK); -#endif + mInputBuffers.push_back(info); + schedulePushIfNecessary(); +} + +void DirectRenderer::AudioRenderer::onMessageReceived( + const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatPushAudio: + { + onPushAudio(); + break; + } + + default: + break; + } } -void DirectRenderer::scheduleRenderIfNecessary() { - if (mRenderPending || mOutputBuffers.empty()) { +void DirectRenderer::AudioRenderer::schedulePushIfNecessary() { + if (mPushPending || mInputBuffers.empty()) { return; } - mRenderPending = true; + mPushPending = true; - int64_t timeUs = (*mOutputBuffers.begin()).mTimeUs; - int64_t nowUs = ALooper::GetNowUs(); + uint32_t numFramesPlayed; + CHECK_EQ(mAudioTrack->getPosition(&numFramesPlayed), + (status_t)OK); + + uint32_t numFramesPendingPlayout = mNumFramesWritten - numFramesPlayed; + + // This is how long the audio sink will have data to + // play back. + const float msecsPerFrame = 1000.0f / mAudioTrack->getSampleRate(); + + int64_t delayUs = + msecsPerFrame * numFramesPendingPlayout * 1000ll; + + // Let's give it more data after about half that time + // has elapsed. + (new AMessage(kWhatPushAudio, id()))->post(delayUs / 2); +} + +void DirectRenderer::AudioRenderer::onPushAudio() { + mPushPending = false; + + while (!mInputBuffers.empty()) { + const BufferInfo &info = *mInputBuffers.begin(); - if (mFirstRenderTimeUs < 0ll) { - mFirstRenderTimeUs = timeUs; - mFirstRenderRealUs = nowUs; + ssize_t n = writeNonBlocking( + info.mBuffer->data(), info.mBuffer->size()); + + if (n < (ssize_t)info.mBuffer->size()) { + CHECK_GE(n, 0); + + info.mBuffer->setRange( + info.mBuffer->offset() + n, info.mBuffer->size() - n); + break; + } + + mDecoderContext->releaseOutputBuffer(info.mIndex); + + mInputBuffers.erase(mInputBuffers.begin()); + } + + schedulePushIfNecessary(); +} + +ssize_t DirectRenderer::AudioRenderer::writeNonBlocking( + const uint8_t *data, size_t size) { + uint32_t numFramesPlayed; + status_t err = mAudioTrack->getPosition(&numFramesPlayed); + if (err != OK) { + return err; + } + + ssize_t numFramesAvailableToWrite = + mAudioTrack->frameCount() - (mNumFramesWritten - numFramesPlayed); + + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * mAudioTrack->frameSize(); + + if (size > numBytesAvailableToWrite) { + size = numBytesAvailableToWrite; } - int64_t whenUs = timeUs - mFirstRenderTimeUs + mFirstRenderRealUs; - int64_t delayUs = whenUs - nowUs; + CHECK_EQ(mAudioTrack->write(data, size), (ssize_t)size); - (new AMessage(kWhatRender, id()))->post(delayUs); + size_t numFramesWritten = size / mAudioTrack->frameSize(); + mNumFramesWritten += numFramesWritten; + + return size; } -void DirectRenderer::onRender() { - mRenderPending = false; +//////////////////////////////////////////////////////////////////////////////// - int64_t nowUs = ALooper::GetNowUs(); +DirectRenderer::DirectRenderer( + const sp<IGraphicBufferProducer> &bufferProducer) + : mSurfaceTex(bufferProducer), + mVideoRenderPending(false), + mLatencySum(0ll), + mLatencyCount(0), + mNumFramesLate(0), + mNumFrames(0) { +} - while (!mOutputBuffers.empty()) { - const OutputInfo &info = *mOutputBuffers.begin(); +DirectRenderer::~DirectRenderer() { +} - if (info.mTimeUs > nowUs) { +int64_t DirectRenderer::getAvgLatenessUs() { + if (mLatencyCount == 0) { + return 0ll; + } + + int64_t avgLatencyUs = mLatencySum / mLatencyCount; + + mLatencySum = 0ll; + mLatencyCount = 0; + + if (mNumFrames > 0) { + ALOGI("%d / %d frames late", mNumFramesLate, mNumFrames); + mNumFramesLate = 0; + mNumFrames = 0; + } + + return avgLatencyUs; +} + +void DirectRenderer::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatDecoderNotify: + { + onDecoderNotify(msg); break; } - status_t err = mVideoDecoder->renderOutputBufferAndRelease(info.mIndex); - CHECK_EQ(err, (status_t)OK); + case kWhatRenderVideo: + { + onRenderVideo(); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::setFormat(size_t trackIndex, const sp<AMessage> &format) { + CHECK_LT(trackIndex, 2u); + + CHECK(mDecoderContext[trackIndex] == NULL); + + sp<AMessage> notify = new AMessage(kWhatDecoderNotify, id()); + notify->setSize("trackIndex", trackIndex); + + mDecoderContext[trackIndex] = new DecoderContext(notify); + looper()->registerHandler(mDecoderContext[trackIndex]); + + CHECK_EQ((status_t)OK, + mDecoderContext[trackIndex]->init( + format, trackIndex == 0 ? mSurfaceTex : NULL)); + + if (trackIndex == 1) { + // Audio + mAudioRenderer = new AudioRenderer(mDecoderContext[1]); + looper()->registerHandler(mAudioRenderer); + } +} + +void DirectRenderer::queueAccessUnit( + size_t trackIndex, const sp<ABuffer> &accessUnit) { + CHECK_LT(trackIndex, 2u); + + if (mDecoderContext[trackIndex] == NULL) { + CHECK_EQ(trackIndex, 0u); + + sp<AMessage> format = new AMessage; + format->setString("mime", "video/avc"); + format->setInt32("width", 640); + format->setInt32("height", 360); - mOutputBuffers.erase(mOutputBuffers.begin()); + setFormat(trackIndex, format); } - scheduleRenderIfNecessary(); + mDecoderContext[trackIndex]->queueInputBuffer(accessUnit); } -void DirectRenderer::scheduleVideoDecoderNotification() { - if (mVideoDecoderNotificationPending) { +void DirectRenderer::onDecoderNotify(const sp<AMessage> &msg) { + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case DecoderContext::kWhatOutputBufferReady: + { + size_t index; + CHECK(msg->findSize("index", &index)); + + int64_t timeUs; + CHECK(msg->findInt64("timeUs", &timeUs)); + + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + queueOutputBuffer(trackIndex, index, timeUs, buffer); + break; + } + + default: + TRESPASS(); + } +} + +void DirectRenderer::queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp<ABuffer> &buffer) { + if (trackIndex == 1) { + // Audio + mAudioRenderer->queueInputBuffer(index, timeUs, buffer); return; } - sp<AMessage> notify = - new AMessage(kWhatVideoDecoderNotify, id()); + OutputInfo info; + info.mIndex = index; + info.mTimeUs = timeUs; + info.mBuffer = buffer; + mVideoOutputBuffers.push_back(info); + + scheduleVideoRenderIfNecessary(); +} + +void DirectRenderer::scheduleVideoRenderIfNecessary() { + if (mVideoRenderPending || mVideoOutputBuffers.empty()) { + return; + } + + mVideoRenderPending = true; + + int64_t timeUs = (*mVideoOutputBuffers.begin()).mTimeUs; + int64_t nowUs = ALooper::GetNowUs(); + + int64_t delayUs = timeUs - nowUs; + + (new AMessage(kWhatRenderVideo, id()))->post(delayUs); +} + +void DirectRenderer::onRenderVideo() { + mVideoRenderPending = false; + + int64_t nowUs = ALooper::GetNowUs(); + + while (!mVideoOutputBuffers.empty()) { + const OutputInfo &info = *mVideoOutputBuffers.begin(); + + if (info.mTimeUs > nowUs) { + break; + } + + if (info.mTimeUs + 15000ll < nowUs) { + ++mNumFramesLate; + } + ++mNumFrames; + + mLatencySum += nowUs - info.mTimeUs; + ++mLatencyCount; + + status_t err = + mDecoderContext[0]->renderOutputBufferAndRelease(info.mIndex); + CHECK_EQ(err, (status_t)OK); + + mVideoOutputBuffers.erase(mVideoOutputBuffers.begin()); + } - mVideoDecoder->requestActivityNotification(notify); - mVideoDecoderNotificationPending = true; + scheduleVideoRenderIfNecessary(); } } // namespace android diff --git a/media/libstagefright/wifi-display/sink/DirectRenderer.h b/media/libstagefright/wifi-display/sink/DirectRenderer.h index 7219080..92c176a 100644 --- a/media/libstagefright/wifi-display/sink/DirectRenderer.h +++ b/media/libstagefright/wifi-display/sink/DirectRenderer.h @@ -23,57 +23,61 @@ namespace android { struct ABuffer; +struct AudioTrack; struct IGraphicBufferProducer; struct MediaCodec; -// An experimental renderer that only supports video and decodes video data -// as soon as it arrives using a MediaCodec instance, rendering it without -// delay. Primarily meant to finetune packet loss discovery and minimize -// latency. +// Renders audio and video data queued by calls to "queueAccessUnit". struct DirectRenderer : public AHandler { DirectRenderer(const sp<IGraphicBufferProducer> &bufferProducer); void setFormat(size_t trackIndex, const sp<AMessage> &format); void queueAccessUnit(size_t trackIndex, const sp<ABuffer> &accessUnit); + int64_t getAvgLatenessUs(); + protected: virtual void onMessageReceived(const sp<AMessage> &msg); virtual ~DirectRenderer(); private: + struct DecoderContext; + struct AudioRenderer; + enum { - kWhatVideoDecoderNotify, - kWhatRender, + kWhatDecoderNotify, + kWhatRenderVideo, }; struct OutputInfo { size_t mIndex; int64_t mTimeUs; + sp<ABuffer> mBuffer; }; sp<IGraphicBufferProducer> mSurfaceTex; - sp<ALooper> mVideoDecoderLooper; - sp<MediaCodec> mVideoDecoder; - Vector<sp<ABuffer> > mVideoDecoderInputBuffers; - List<size_t> mVideoDecoderInputBuffersAvailable; - bool mVideoDecoderNotificationPending; + sp<DecoderContext> mDecoderContext[2]; + List<OutputInfo> mVideoOutputBuffers; + + bool mVideoRenderPending; + + sp<AudioRenderer> mAudioRenderer; - List<sp<ABuffer> > mVideoAccessUnits; + int64_t mLatencySum; + size_t mLatencyCount; - List<OutputInfo> mOutputBuffers; - bool mRenderPending; - int64_t mFirstRenderTimeUs; - int64_t mFirstRenderRealUs; + int32_t mNumFramesLate; + int32_t mNumFrames; - void onVideoDecoderNotify(); - void onRender(); + void onDecoderNotify(const sp<AMessage> &msg); - void queueVideoDecoderInputBuffers(); - void scheduleVideoDecoderNotification(); - void scheduleRenderIfNecessary(); + void queueOutputBuffer( + size_t trackIndex, + size_t index, int64_t timeUs, const sp<ABuffer> &buffer); - void queueOutputBuffer(size_t index, int64_t timeUs); + void scheduleVideoRenderIfNecessary(); + void onRenderVideo(); DISALLOW_EVIL_CONSTRUCTORS(DirectRenderer); }; diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp index d9d8a76..6b185db 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp @@ -27,6 +27,7 @@ #include <gui/SurfaceComposerClient.h> #include <media/IMediaPlayerService.h> #include <media/IStreamSource.h> +#include <media/mediaplayer.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> @@ -60,6 +61,8 @@ struct TunnelRenderer::StreamSource : public BnStreamSource { void doSomeWork(); + void setTimeOffset(int64_t offset); + protected: virtual ~StreamSource(); @@ -75,6 +78,9 @@ private: size_t mNumDeqeued; + int64_t mTimeOffsetUs; + bool mTimeOffsetChanged; + DISALLOW_EVIL_CONSTRUCTORS(StreamSource); }; @@ -82,7 +88,9 @@ private: TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner) : mOwner(owner), - mNumDeqeued(0) { + mNumDeqeued(0), + mTimeOffsetUs(0ll), + mTimeOffsetChanged(false) { } TunnelRenderer::StreamSource::~StreamSource() { @@ -110,7 +118,7 @@ void TunnelRenderer::StreamSource::onBufferAvailable(size_t index) { } uint32_t TunnelRenderer::StreamSource::flags() const { - return kFlagAlignedVideoData; + return kFlagAlignedVideoData | kFlagIsRealTimeData; } void TunnelRenderer::StreamSource::doSomeWork() { @@ -124,21 +132,21 @@ void TunnelRenderer::StreamSource::doSomeWork() { ++mNumDeqeued; - if (mNumDeqeued == 1) { - ALOGI("fixing real time now."); - + if (mTimeOffsetChanged) { sp<AMessage> extra = new AMessage; extra->setInt32( IStreamListener::kKeyDiscontinuityMask, - ATSParser::DISCONTINUITY_ABSOLUTE_TIME); + ATSParser::DISCONTINUITY_TIME_OFFSET); - extra->setInt64("timeUs", ALooper::GetNowUs()); + extra->setInt64("offset", mTimeOffsetUs); mListener->issueCommand( IStreamListener::DISCONTINUITY, false /* synchronous */, extra); + + mTimeOffsetChanged = false; } ALOGV("dequeue TS packet of size %d", srcBuffer->size()); @@ -155,18 +163,32 @@ void TunnelRenderer::StreamSource::doSomeWork() { } } +void TunnelRenderer::StreamSource::setTimeOffset(int64_t offset) { + Mutex::Autolock autoLock(mLock); + + if (offset != mTimeOffsetUs) { + mTimeOffsetUs = offset; + mTimeOffsetChanged = true; + } +} + //////////////////////////////////////////////////////////////////////////////// TunnelRenderer::TunnelRenderer( const sp<IGraphicBufferProducer> &bufferProducer) : mSurfaceTex(bufferProducer), mStartup(true) { + mStreamSource = new StreamSource(this); } TunnelRenderer::~TunnelRenderer() { destroyPlayer(); } +void TunnelRenderer::setTimeOffset(int64_t offset) { + mStreamSource->setTimeOffset(offset); +} + void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { default: @@ -209,8 +231,6 @@ void TunnelRenderer::initPlayer() { sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); CHECK(service.get() != NULL); - mStreamSource = new StreamSource(this); - mPlayerClient = new PlayerClient; mPlayer = service->create(mPlayerClient, 0); @@ -226,6 +246,8 @@ void TunnelRenderer::initPlayer() { void TunnelRenderer::destroyPlayer() { mStreamSource.clear(); + mPlayer->setVideoSurfaceTexture(NULL); + mPlayer->stop(); mPlayer.clear(); diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h index 8e96665..479e73c 100644 --- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h +++ b/media/libstagefright/wifi-display/sink/TunnelRenderer.h @@ -39,6 +39,12 @@ struct TunnelRenderer : public AHandler { void queueBuffer(const sp<ABuffer> &buffer); sp<ABuffer> dequeueBuffer(); + void setTimeOffset(int64_t offset); + + int64_t getAvgLatenessUs() { + return 0ll; + } + protected: virtual void onMessageReceived(const sp<AMessage> &msg); virtual ~TunnelRenderer(); diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp index 158c2da..62021c0 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -23,22 +23,24 @@ #include "DirectRenderer.h" #include "MediaReceiver.h" #include "ParsedMessage.h" +#include "TimeSyncer.h" #include "TunnelRenderer.h" +#include <cutils/properties.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> -#include <cutils/properties.h> - namespace android { WifiDisplaySink::WifiDisplaySink( + uint32_t flags, const sp<ANetworkSession> &netSession, const sp<IGraphicBufferProducer> &bufferProducer, const sp<AMessage> ¬ify) : mState(UNDEFINED), + mFlags(flags), mNetSession(netSession), mSurfaceTex(bufferProducer), mNotify(notify), @@ -46,7 +48,11 @@ WifiDisplaySink::WifiDisplaySink( mUsingTCPInterleaving(false), mSessionID(0), mNextCSeq(1), - mIDRFrameRequestPending(false) { + mIDRFrameRequestPending(false), + mTimeOffsetUs(0ll), + mTimeOffsetValid(false), + mTargetLatencyUs(-1ll), + mSetupDeferred(false) { // We support any and all resolutions, but prefer 720p30 mSinkSupportedVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 @@ -199,6 +205,16 @@ void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) { { ALOGI("We're now connected."); mState = CONNECTED; + + if (mFlags & FLAG_SPECIAL_MODE) { + sp<AMessage> notify = new AMessage( + kWhatTimeSyncerNotify, id()); + + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); + } break; } @@ -226,6 +242,41 @@ void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatTimeSyncerNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == TimeSyncer::kWhatTimeOffset) { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + + if (mSetupDeferred) { + CHECK_EQ((status_t)OK, + sendSetup( + mSessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); + + mSetupDeferred = false; + } + } + break; + } + + case kWhatReportLateness: + { + int64_t latenessUs = mRenderer->getAvgLatenessUs(); + + ALOGI("avg. lateness = %lld ms", + (latenessUs + mTargetLatencyUs) / 1000ll); + + mMediaReceiver->notifyLateness( + 0 /* trackIndex */, latenessUs); + + msg->post(kReportLatenessEveryUs); + break; + } + default: TRESPASS(); } @@ -266,15 +317,44 @@ void WifiDisplaySink::onMediaReceiverNotify(const sp<AMessage> &msg) { looper()->registerHandler(mRenderer); } + CHECK(mTimeOffsetValid); + + int64_t latencyUs = 200000ll; // 200ms by default + + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd-sink.latency", val, NULL)) { + char *end; + int64_t x = strtoll(val, &end, 10); + + if (end > val && *end == '\0' && x >= 0ll) { + latencyUs = x; + } + } + + if (latencyUs != mTargetLatencyUs) { + mTargetLatencyUs = latencyUs; + + ALOGI("Assuming %lld ms of latency.", latencyUs / 1000ll); + } + sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); -#if USE_TUNNEL_RENDERER - mRenderer->queueBuffer(accessUnit); -#else + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + // We are the timesync _client_, + // client time = server time - time offset. + timeUs += mTargetLatencyUs - mTimeOffsetUs; + + accessUnit->meta()->setInt64("timeUs", timeUs); + size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); +#if USE_TUNNEL_RENDERER + mRenderer->queueBuffer(accessUnit); +#else sp<AMessage> format; if (msg->findMessage("format", &format)) { mRenderer->setFormat(trackIndex, format); @@ -445,6 +525,8 @@ status_t WifiDisplaySink::onReceivePlayResponse( mState = PLAYING; + (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); + return OK; } @@ -555,6 +637,8 @@ void WifiDisplaySink::onGetParameterRequest( mUsingTCPTransport = true; mUsingTCPInterleaving = true; } + } else if (mFlags & FLAG_SPECIAL_MODE) { + mUsingTCPTransport = true; } body = "wfd_video_formats: "; @@ -735,12 +819,16 @@ void WifiDisplaySink::onSetParameterRequest( const char *content = data->getContent(); if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { - status_t err = - sendSetup( - sessionID, - "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); + if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { + mSetupDeferred = true; + } else { + status_t err = + sendSetup( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); - CHECK_EQ(err, (status_t)OK); + CHECK_EQ(err, (status_t)OK); + } } AString response = "RTSP/1.0 200 OK\r\n"; diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h index 01af58b..2b8c6f7 100644 --- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h @@ -31,6 +31,7 @@ struct AMessage; struct DirectRenderer; struct MediaReceiver; struct ParsedMessage; +struct TimeSyncer; struct TunnelRenderer; #define USE_TUNNEL_RENDERER 0 @@ -43,11 +44,16 @@ struct WifiDisplaySink : public AHandler { kWhatDisconnected, }; + enum Flags { + FLAG_SPECIAL_MODE = 1, + }; + // If no notification message is specified (notify == NULL) // the sink will stop its looper() once the session ends, // otherwise it will post an appropriate notification but leave // the looper() running. WifiDisplaySink( + uint32_t flags, const sp<ANetworkSession> &netSession, const sp<IGraphicBufferProducer> &bufferProducer = NULL, const sp<AMessage> ¬ify = NULL); @@ -73,6 +79,8 @@ private: kWhatRTSPNotify, kWhatStop, kWhatMediaReceiverNotify, + kWhatTimeSyncerNotify, + kWhatReportLateness, }; struct ResponseID { @@ -89,11 +97,15 @@ private: typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)( int32_t sessionID, const sp<ParsedMessage> &msg); + static const int64_t kReportLatenessEveryUs = 1000000ll; + State mState; + uint32_t mFlags; VideoFormats mSinkSupportedVideoFormats; sp<ANetworkSession> mNetSession; sp<IGraphicBufferProducer> mSurfaceTex; sp<AMessage> mNotify; + sp<TimeSyncer> mTimeSyncer; bool mUsingTCPTransport; bool mUsingTCPInterleaving; AString mRTSPHost; @@ -117,6 +129,13 @@ private: bool mIDRFrameRequestPending; + int64_t mTimeOffsetUs; + bool mTimeOffsetValid; + + int64_t mTargetLatencyUs; + + bool mSetupDeferred; + status_t sendM2(int32_t sessionID); status_t sendSetup(int32_t sessionID, const char *uri); status_t sendPlay(int32_t sessionID, const char *uri); diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 2861aa9..bb8c387 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -55,6 +55,7 @@ Converter::Converter( ,mInSilentMode(false) #endif ,mPrevVideoBitrate(-1) + ,mNumFramesToDrop(0) { AString mime; CHECK(mInputFormat->findString("mime", &mime)); @@ -327,6 +328,13 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); + if (mIsVideo && mNumFramesToDrop) { + --mNumFramesToDrop; + ALOGI("dropping frame."); + ReleaseMediaBufferReference(accessUnit); + break; + } + #if 0 void *mbuf; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) @@ -422,6 +430,12 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatDropAFrame: + { + ++mNumFramesToDrop; + break; + } + default: TRESPASS(); } @@ -690,4 +704,8 @@ void Converter::requestIDRFrame() { (new AMessage(kWhatRequestIDRFrame, id()))->post(); } +void Converter::dropAFrame() { + (new AMessage(kWhatDropAFrame, id()))->post(); +} + } // namespace android diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 57802bd..a418f69 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -51,6 +51,8 @@ struct Converter : public AHandler { void requestIDRFrame(); + void dropAFrame(); + enum { kWhatAccessUnit, kWhatEOS, @@ -63,6 +65,7 @@ struct Converter : public AHandler { kWhatShutdown, kWhatMediaPullerNotify, kWhatEncoderActivity, + kWhatDropAFrame, }; void shutdownAsync(); @@ -102,6 +105,8 @@ private: int32_t mPrevVideoBitrate; + int32_t mNumFramesToDrop; + status_t initEncoder(); void releaseEncoder(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index ea195b3..94cb2a4 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -515,6 +515,16 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } else if (what == MediaSender::kWhatError) { notifySessionDead(); + } else if (what == MediaSender::kWhatNetworkStall) { + size_t numBytesQueued; + CHECK(msg->findSize("numBytesQueued", &numBytesQueued)); + + if (mVideoTrackIndex >= 0) { + const sp<Track> &videoTrack = + mTracks.valueFor(mVideoTrackIndex); + + videoTrack->converter()->dropAFrame(); + } } else { TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b8524f6..c8798c6 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -23,6 +23,7 @@ #include "Parameters.h" #include "ParsedMessage.h" #include "rtp/RTPSender.h" +#include "TimeSyncer.h" #include <binder/IServiceManager.h> #include <gui/IGraphicBufferProducer.h> @@ -157,6 +158,12 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { } if (err == OK) { + sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id()); + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startServer(8123); + mState = AWAITING_CLIENT_CONNECTION; } @@ -265,6 +272,11 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { break; } + case ANetworkSession::kWhatNetworkStall: + { + break; + } + default: TRESPASS(); } @@ -520,6 +532,11 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatTimeSyncerNotify: + { + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 724462c..9e72682 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -30,6 +30,7 @@ namespace android { struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; +struct TimeSyncer; // Represents the RTSP server acting as a wifi display source. // Manages incoming connections, sets up Playback sessions as necessary. @@ -81,6 +82,7 @@ private: kWhatHDCPNotify, kWhatFinishStop2, kWhatTeardownTriggerTimedOut, + kWhatTimeSyncerNotify, }; struct ResponseID { @@ -114,6 +116,7 @@ private: VideoFormats mSupportedSourceVideoFormats; sp<ANetworkSession> mNetSession; sp<IRemoteDisplayClient> mClient; + sp<TimeSyncer> mTimeSyncer; struct in_addr mInterfaceAddr; int32_t mSessionID; diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp index 86437e0..111846d 100644 --- a/media/libstagefright/wifi-display/udptest.cpp +++ b/media/libstagefright/wifi-display/udptest.cpp @@ -19,292 +19,13 @@ #include <utils/Log.h> #include "ANetworkSession.h" +#include "TimeSyncer.h" #include <binder/ProcessState.h> -#include <media/stagefright/foundation/ABuffer.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AHandler.h> -#include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/Utils.h> namespace android { -struct TestHandler : public AHandler { - TestHandler(const sp<ANetworkSession> &netSession); - - void startServer(unsigned localPort); - void startClient(const char *remoteHost, unsigned remotePort); - -protected: - virtual ~TestHandler(); - - virtual void onMessageReceived(const sp<AMessage> &msg); - -private: - enum { - kWhatStartServer, - kWhatStartClient, - kWhatUDPNotify, - kWhatSendPacket, - kWhatTimedOut, - }; - - struct TimeInfo { - int64_t mT1; // client timestamp at send - int64_t mT2; // server timestamp at receive - int64_t mT3; // server timestamp at send - int64_t mT4; // client timestamp at receive - }; - - static const int64_t kTimeoutDelayUs = 1000000ll; - - sp<ANetworkSession> mNetSession; - - bool mIsServer; - bool mConnected; - int32_t mUDPSession; - uint32_t mSeqNo; - double mTotalTimeUs; - int32_t mCount; - int64_t mSumOffsets; - - int64_t mPendingT1; - int32_t mTimeoutGeneration; - - void postSendPacket(int64_t delayUs = 0ll); - - void postTimeout(); - void cancelTimeout(); - - DISALLOW_EVIL_CONSTRUCTORS(TestHandler); -}; - -TestHandler::TestHandler(const sp<ANetworkSession> &netSession) - : mNetSession(netSession), - mIsServer(false), - mConnected(false), - mUDPSession(0), - mSeqNo(0), - mTotalTimeUs(0.0), - mCount(0), - mSumOffsets(0ll), - mPendingT1(0ll), - mTimeoutGeneration(0) { -} - -TestHandler::~TestHandler() { -} - -void TestHandler::startServer(unsigned localPort) { - sp<AMessage> msg = new AMessage(kWhatStartServer, id()); - msg->setInt32("localPort", localPort); - msg->post(); -} - -void TestHandler::startClient(const char *remoteHost, unsigned remotePort) { - sp<AMessage> msg = new AMessage(kWhatStartClient, id()); - msg->setString("remoteHost", remoteHost); - msg->setInt32("remotePort", remotePort); - msg->post(); -} - -void TestHandler::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatStartClient: - { - AString remoteHost; - CHECK(msg->findString("remoteHost", &remoteHost)); - - int32_t remotePort; - CHECK(msg->findInt32("remotePort", &remotePort)); - - sp<AMessage> notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - 0 /* localPort */, - remoteHost.c_str(), - remotePort, - notify, - &mUDPSession)); - - postSendPacket(); - break; - } - - case kWhatStartServer: - { - mIsServer = true; - - int32_t localPort; - CHECK(msg->findInt32("localPort", &localPort)); - - sp<AMessage> notify = new AMessage(kWhatUDPNotify, id()); - - CHECK_EQ((status_t)OK, - mNetSession->createUDPSession( - localPort, notify, &mUDPSession)); - - break; - } - - case kWhatSendPacket: - { - TimeInfo ti; - memset(&ti, 0, sizeof(ti)); - - ti.mT1 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, &ti, sizeof(ti))); - - mPendingT1 = ti.mT1; - postTimeout(); - break; - } - - case kWhatTimedOut: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mTimeoutGeneration) { - break; - } - - ALOGI("timed out, sending another request"); - postSendPacket(); - break; - } - - case kWhatUDPNotify: - { - int32_t reason; - CHECK(msg->findInt32("reason", &reason)); - - switch (reason) { - case ANetworkSession::kWhatError: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - int32_t err; - CHECK(msg->findInt32("err", &err)); - - AString detail; - CHECK(msg->findString("detail", &detail)); - - ALOGE("An error occurred in session %d (%d, '%s/%s').", - sessionID, - err, - detail.c_str(), - strerror(-err)); - - mNetSession->destroySession(sessionID); - - cancelTimeout(); - looper()->stop(); - break; - } - - case ANetworkSession::kWhatDatagram: - { - int32_t sessionID; - CHECK(msg->findInt32("sessionID", &sessionID)); - - sp<ABuffer> packet; - CHECK(msg->findBuffer("data", &packet)); - - int64_t arrivalTimeUs; - CHECK(packet->meta()->findInt64( - "arrivalTimeUs", &arrivalTimeUs)); - - CHECK_EQ(packet->size(), sizeof(TimeInfo)); - - TimeInfo *ti = (TimeInfo *)packet->data(); - - if (mIsServer) { - if (!mConnected) { - AString fromAddr; - CHECK(msg->findString("fromAddr", &fromAddr)); - - int32_t fromPort; - CHECK(msg->findInt32("fromPort", &fromPort)); - - CHECK_EQ((status_t)OK, - mNetSession->connectUDPSession( - mUDPSession, fromAddr.c_str(), fromPort)); - - mConnected = true; - } - - ti->mT2 = arrivalTimeUs; - ti->mT3 = ALooper::GetNowUs(); - - CHECK_EQ((status_t)OK, - mNetSession->sendRequest( - mUDPSession, ti, sizeof(*ti))); - } else { - if (ti->mT1 != mPendingT1) { - break; - } - - cancelTimeout(); - mPendingT1 = 0; - - ti->mT4 = arrivalTimeUs; - - // One way delay for a packet to travel from client - // to server or back (assumed to be the same either way). - int64_t delay = - (ti->mT2 - ti->mT1 + ti->mT4 - ti->mT3) / 2; - - // Offset between the client clock (T1, T4) and the - // server clock (T2, T3) timestamps. - int64_t offset = - (ti->mT2 - ti->mT1 - ti->mT4 + ti->mT3) / 2; - - mSumOffsets += offset; - ++mCount; - - printf("delay = %lld us,\toffset %lld us\n", - delay, - offset); - fflush(stdout); - - postSendPacket(1000000ll / 30); - } - break; - } - - default: - TRESPASS(); - } - - break; - } - - default: - TRESPASS(); - } -} - -void TestHandler::postSendPacket(int64_t delayUs) { - (new AMessage(kWhatSendPacket, id()))->post(delayUs); -} - -void TestHandler::postTimeout() { - sp<AMessage> msg = new AMessage(kWhatTimedOut, id()); - msg->setInt32("generation", mTimeoutGeneration); - msg->post(kTimeoutDelayUs); -} - -void TestHandler::cancelTimeout() { - ++mTimeoutGeneration; -} - } // namespace android static void usage(const char *me) { @@ -379,7 +100,7 @@ int main(int argc, char **argv) { sp<ALooper> looper = new ALooper; - sp<TestHandler> handler = new TestHandler(netSession); + sp<TimeSyncer> handler = new TimeSyncer(netSession, NULL /* notify */); looper->registerHandler(handler); if (localPort >= 0) { diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 3f4216a..0b18484 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -321,7 +321,10 @@ int main(int argc, char **argv) { sp<ALooper> looper = new ALooper; sp<WifiDisplaySink> sink = new WifiDisplaySink( - session, surface->getIGraphicBufferProducer()); + 0 /* flags */, + session, + surface->getIGraphicBufferProducer()); + looper->registerHandler(sink); if (connectToPort >= 0) { diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 462739b..2ab1d04 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -107,7 +107,7 @@ struct FastMixerDumpState { #ifdef FAST_MIXER_STATISTICS // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. // kSamplingN is the size of the sampling frame, and must be a power of 2 <= 0x8000. - static const uint32_t kSamplingN = 0x1000; + static const uint32_t kSamplingN = 0x8000; // The bounds define the interval of valid samples, and are represented as follows: // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index d6ad889..8600735 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES:= \ CameraClient.cpp \ Camera2Client.cpp \ ProCamera2Client.cpp \ + Camera2ClientBase.cpp \ CameraDeviceBase.cpp \ Camera2Device.cpp \ Camera3Device.cpp \ diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp index 8295905..056271d 100644 --- a/services/camera/libcameraservice/Camera2Client.cpp +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -49,9 +49,8 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, uid_t clientUid, int servicePid, int deviceVersion): - Client(cameraService, cameraClient, clientPackageName, + Camera2ClientBase(cameraService, cameraClient, clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid), - mSharedCameraClient(cameraClient), mParameters(cameraId, cameraFacing) { ATRACE_CALL(); @@ -76,42 +75,17 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, l.mParameters.state = Parameters::DISCONNECTED; } -status_t Camera2Client::checkPid(const char* checkLocation) const { - int callingPid = getCallingPid(); - if (callingPid == mClientPid) return NO_ERROR; - - ALOGE("%s: attempt to use a locked camera from a different process" - " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid); - return PERMISSION_DENIED; -} - status_t Camera2Client::initialize(camera_module_t *module) { ATRACE_CALL(); ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId); status_t res; - // Verify ops permissions - res = startCameraOps(); + res = Camera2ClientBase::initialize(module); if (res != OK) { return res; } - if (mDevice == NULL) { - ALOGE("%s: Camera %d: No device connected", - __FUNCTION__, mCameraId); - return NO_INIT; - } - - res = mDevice->initialize(module); - if (res != OK) { - ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); - return NO_INIT; - } - - res = mDevice->setNotifyCallback(this); - SharedParameters::Lock l(mParameters); res = l.mParameters.initialize(&(mDevice->info())); @@ -125,7 +99,7 @@ status_t Camera2Client::initialize(camera_module_t *module) mStreamingProcessor = new StreamingProcessor(this); - mFrameProcessor = new FrameProcessor(this); + mFrameProcessor = new FrameProcessor(mDevice, this); threadName = String8::format("C2-%d-FrameProc", mCameraId); mFrameProcessor->run(threadName.string()); @@ -173,7 +147,7 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) { String8 result; result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId, - getCameraClient()->asBinder().get(), + getRemoteCallback()->asBinder().get(), mClientPid); result.append(" State: "); #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; @@ -376,25 +350,15 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) { mZslProcessor->dump(fd, args); - result = " Device dump:\n"; - write(fd, result.string(), result.size()); - - status_t res = mDevice->dump(fd, args); - if (res != OK) { - result = String8::format(" Error dumping device: %s (%d)", - strerror(-res), res); - write(fd, result.string(), result.size()); - } - + return dumpDevice(fd, args); #undef CASE_APPEND_ENUM - return NO_ERROR; } // ICamera interface void Camera2Client::disconnect() { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); // Allow both client and the media server to disconnect at all times int callingPid = getCallingPid(); @@ -444,7 +408,7 @@ void Camera2Client::disconnect() { status_t Camera2Client::connect(const sp<ICameraClient>& client) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); if (mClientPid != 0 && getCallingPid() != mClientPid) { ALOGE("%s: Camera %d: Connection attempt from pid %d; " @@ -455,8 +419,8 @@ status_t Camera2Client::connect(const sp<ICameraClient>& client) { mClientPid = getCallingPid(); - mCameraClient = client; - mSharedCameraClient = client; + mRemoteCallback = client; + mSharedCameraCallbacks = client; return OK; } @@ -464,7 +428,7 @@ status_t Camera2Client::connect(const sp<ICameraClient>& client) { status_t Camera2Client::lock() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); ALOGV("%s: Camera %d: Lock call from pid %d; current client pid %d", __FUNCTION__, mCameraId, getCallingPid(), mClientPid); @@ -485,7 +449,7 @@ status_t Camera2Client::lock() { status_t Camera2Client::unlock() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); ALOGV("%s: Camera %d: Unlock call from pid %d; current client pid %d", __FUNCTION__, mCameraId, getCallingPid(), mClientPid); @@ -497,8 +461,8 @@ status_t Camera2Client::unlock() { return INVALID_OPERATION; } mClientPid = 0; - mCameraClient.clear(); - mSharedCameraClient.clear(); + mRemoteCallback.clear(); + mSharedCameraCallbacks.clear(); return OK; } @@ -511,7 +475,7 @@ status_t Camera2Client::setPreviewDisplay( const sp<Surface>& surface) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -529,7 +493,7 @@ status_t Camera2Client::setPreviewTexture( const sp<IGraphicBufferProducer>& bufferProducer) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -598,7 +562,7 @@ status_t Camera2Client::setPreviewWindowL(const sp<IBinder>& binder, void Camera2Client::setPreviewCallbackFlag(int flag) { ATRACE_CALL(); ALOGV("%s: Camera %d: Flag 0x%x", __FUNCTION__, mCameraId, flag); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); if ( checkPid(__FUNCTION__) != OK) return; @@ -637,7 +601,7 @@ void Camera2Client::setPreviewCallbackFlagL(Parameters ¶ms, int flag) { status_t Camera2Client::startPreview() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; SharedParameters::Lock l(mParameters); @@ -753,7 +717,7 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { void Camera2Client::stopPreview() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return; stopPreviewL(); @@ -801,7 +765,7 @@ void Camera2Client::stopPreviewL() { bool Camera2Client::previewEnabled() { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return false; @@ -811,7 +775,7 @@ bool Camera2Client::previewEnabled() { status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -836,7 +800,7 @@ status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { status_t Camera2Client::startRecording() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; SharedParameters::Lock l(mParameters); @@ -927,7 +891,7 @@ status_t Camera2Client::startRecordingL(Parameters ¶ms, bool restart) { void Camera2Client::stopRecording() { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedParameters::Lock l(mParameters); status_t res; @@ -959,7 +923,7 @@ void Camera2Client::stopRecording() { bool Camera2Client::recordingEnabled() { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); if ( checkPid(__FUNCTION__) != OK) return false; @@ -976,7 +940,7 @@ bool Camera2Client::recordingEnabledL() { void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); if ( checkPid(__FUNCTION__) != OK) return; mStreamingProcessor->releaseRecordingFrame(mem); @@ -984,7 +948,7 @@ void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { status_t Camera2Client::autoFocus() { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -1022,9 +986,9 @@ status_t Camera2Client::autoFocus() { * Send immediate notification back to client */ if (notifyImmediately) { - SharedCameraClient::Lock l(mSharedCameraClient); - if (l.mCameraClient != 0) { - l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, + SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, notifySuccess ? 1 : 0, 0); } return OK; @@ -1055,7 +1019,7 @@ status_t Camera2Client::autoFocus() { status_t Camera2Client::cancelAutoFocus() { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -1087,7 +1051,7 @@ status_t Camera2Client::cancelAutoFocus() { status_t Camera2Client::takePicture(int msgType) { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -1146,7 +1110,7 @@ status_t Camera2Client::takePicture(int msgType) { status_t Camera2Client::setParameters(const String8& params) { ATRACE_CALL(); ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -1163,7 +1127,7 @@ status_t Camera2Client::setParameters(const String8& params) { String8 Camera2Client::getParameters() const { ATRACE_CALL(); ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); if ( checkPid(__FUNCTION__) != OK) return String8(); SharedParameters::ReadLock l(mParameters); @@ -1173,7 +1137,7 @@ String8 Camera2Client::getParameters() const { status_t Camera2Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { ATRACE_CALL(); - Mutex::Autolock icl(mICameraLock); + Mutex::Autolock icl(mBinderSerializationLock); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; @@ -1348,18 +1312,6 @@ status_t Camera2Client::commandSetVideoBufferCountL(size_t count) { } /** Device-related methods */ - -void Camera2Client::notifyError(int errorCode, int arg1, int arg2) { - ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, arg1, arg2); -} - -void Camera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) { - (void)frameNumber; - (void)timestamp; - ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__, - frameNumber, timestamp); -} - void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) { ALOGV("%s: Autofocus state now %d, last trigger %d", __FUNCTION__, newState, triggerId); @@ -1455,16 +1407,16 @@ void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) { } } if (sendMovingMessage) { - SharedCameraClient::Lock l(mSharedCameraClient); - if (l.mCameraClient != 0) { - l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE, + SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, afInMotion ? 1 : 0, 0); } } if (sendCompletedMessage) { - SharedCameraClient::Lock l(mSharedCameraClient); - if (l.mCameraClient != 0) { - l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, + SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, success ? 1 : 0, 0); } } @@ -1476,25 +1428,6 @@ void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) { mCaptureSequencer->notifyAutoExposure(newState, triggerId); } -void Camera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) { - (void)newState; - (void)triggerId; - ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", - __FUNCTION__, newState, triggerId); -} - -int Camera2Client::getCameraId() const { - return mCameraId; -} - -const sp<CameraDeviceBase>& Camera2Client::getCameraDevice() { - return mDevice; -} - -const sp<CameraService>& Camera2Client::getCameraService() { - return mCameraService; -} - camera2::SharedParameters& Camera2Client::getParameters() { return mParameters; } @@ -1533,32 +1466,6 @@ status_t Camera2Client::stopStream() { return mStreamingProcessor->stopStream(); } -Camera2Client::SharedCameraClient::Lock::Lock(SharedCameraClient &client): - mCameraClient(client.mCameraClient), - mSharedClient(client) { - mSharedClient.mCameraClientLock.lock(); -} - -Camera2Client::SharedCameraClient::Lock::~Lock() { - mSharedClient.mCameraClientLock.unlock(); -} - -Camera2Client::SharedCameraClient::SharedCameraClient(const sp<ICameraClient>&client): - mCameraClient(client) { -} - -Camera2Client::SharedCameraClient& Camera2Client::SharedCameraClient::operator=( - const sp<ICameraClient>&client) { - Mutex::Autolock l(mCameraClientLock); - mCameraClient = client; - return *this; -} - -void Camera2Client::SharedCameraClient::clear() { - Mutex::Autolock l(mCameraClientLock); - mCameraClient.clear(); -} - const int32_t Camera2Client::kPreviewRequestIdStart; const int32_t Camera2Client::kPreviewRequestIdEnd; const int32_t Camera2Client::kRecordingRequestIdStart; @@ -1660,4 +1567,5 @@ status_t Camera2Client::syncWithDevice() { return res; } + } // namespace android diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h index 80b88f4..713fab3 100644 --- a/services/camera/libcameraservice/Camera2Client.h +++ b/services/camera/libcameraservice/Camera2Client.h @@ -26,6 +26,7 @@ #include "camera2/ZslProcessor.h" #include "camera2/CaptureSequencer.h" #include "camera2/CallbackProcessor.h" +#include "Camera2ClientBase.h" namespace android { @@ -35,8 +36,7 @@ class IMemory; * CAMERA_DEVICE_API_VERSION_2_0 and 3_0. */ class Camera2Client : - public CameraService::Client, - public CameraDeviceBase::NotificationListener + public Camera2ClientBase<CameraService::Client> { public: /** @@ -90,19 +90,13 @@ public: * Interface used by CameraDeviceBase */ - virtual void notifyError(int errorCode, int arg1, int arg2); - virtual void notifyShutter(int frameNumber, nsecs_t timestamp); virtual void notifyAutoFocus(uint8_t newState, int triggerId); virtual void notifyAutoExposure(uint8_t newState, int triggerId); - virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId); /** * Interface used by independent components of Camera2Client. */ - int getCameraId() const; - const sp<CameraDeviceBase>& getCameraDevice(); - const sp<CameraService>& getCameraService(); camera2::SharedParameters& getParameters(); int getPreviewStreamId() const; @@ -118,27 +112,6 @@ public: status_t stopStream(); - // Simple class to ensure that access to ICameraClient is serialized by - // requiring mCameraClientLock to be locked before access to mCameraClient - // is possible. - class SharedCameraClient { - public: - class Lock { - public: - Lock(SharedCameraClient &client); - ~Lock(); - sp<ICameraClient> &mCameraClient; - private: - SharedCameraClient &mSharedClient; - }; - SharedCameraClient(const sp<ICameraClient>& client); - SharedCameraClient& operator=(const sp<ICameraClient>& client); - void clear(); - private: - sp<ICameraClient> mCameraClient; - mutable Mutex mCameraClientLock; - } mSharedCameraClient; - static size_t calculateBufferSize(int width, int height, int format, int stride); @@ -153,13 +126,6 @@ public: private: /** ICamera interface-related private members */ - - // Mutex that must be locked by methods implementing the ICamera interface. - // Ensures serialization between incoming ICamera calls. All methods below - // that append 'L' to the name assume that mICameraLock is locked when - // they're called - mutable Mutex mICameraLock; - typedef camera2::Parameters Parameters; status_t setPreviewWindowL(const sp<IBinder>& binder, @@ -213,17 +179,10 @@ private: bool mAfInMotion; - /** CameraDevice instance, wraps HAL camera device */ - - sp<CameraDeviceBase> mDevice; - /** Utility members */ // Wait until the camera device has received the latest control settings status_t syncWithDevice(); - - // Verify that caller is the owner of the camera - status_t checkPid(const char *checkLocation) const; }; }; // namespace android diff --git a/services/camera/libcameraservice/Camera2ClientBase.cpp b/services/camera/libcameraservice/Camera2ClientBase.cpp new file mode 100644 index 0000000..0623b89 --- /dev/null +++ b/services/camera/libcameraservice/Camera2ClientBase.cpp @@ -0,0 +1,329 @@ +/* + * 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 "Camera2ClientBase" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include <cutils/properties.h> +#include <gui/Surface.h> +#include <gui/Surface.h> +#include "camera2/Parameters.h" +#include "Camera2ClientBase.h" +#include "camera2/ProFrameProcessor.h" + +#include "Camera2Device.h" + +namespace android { +using namespace camera2; + +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +// Interface used by CameraService + +template <typename TClientBase> +Camera2ClientBase<TClientBase>::Camera2ClientBase( + const sp<CameraService>& cameraService, + const sp<TCamCallbacks>& remoteCallback, + const String16& clientPackageName, + int cameraId, + int cameraFacing, + int clientPid, + uid_t clientUid, + int servicePid): + TClientBase(cameraService, remoteCallback, clientPackageName, + cameraId, cameraFacing, clientPid, clientUid, servicePid), + mSharedCameraCallbacks(remoteCallback) +{ + ALOGI("Camera %d: Opened", cameraId); + mDevice = new Camera2Device(cameraId); +} + +template <typename TClientBase> +status_t Camera2ClientBase<TClientBase>::checkPid(const char* checkLocation) + const { + + int callingPid = getCallingPid(); + if (callingPid == TClientBase::mClientPid) return NO_ERROR; + + ALOGE("%s: attempt to use a locked camera from a different process" + " (old pid %d, new pid %d)", checkLocation, TClientBase::mClientPid, callingPid); + return PERMISSION_DENIED; +} + +template <typename TClientBase> +status_t Camera2ClientBase<TClientBase>::initialize(camera_module_t *module) { + ATRACE_CALL(); + ALOGV("%s: Initializing client for camera %d", __FUNCTION__, + TClientBase::mCameraId); + status_t res; + + // Verify ops permissions + res = TClientBase::startCameraOps(); + if (res != OK) { + return res; + } + + if (mDevice == NULL) { + ALOGE("%s: Camera %d: No device connected", + __FUNCTION__, TClientBase::mCameraId); + return NO_INIT; + } + + res = mDevice->initialize(module); + if (res != OK) { + ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", + __FUNCTION__, TClientBase::mCameraId, strerror(-res), res); + return NO_INIT; + } + + res = mDevice->setNotifyCallback(this); + + return OK; +} + +template <typename TClientBase> +Camera2ClientBase<TClientBase>::~Camera2ClientBase() { + ATRACE_CALL(); + + TClientBase::mDestructionStarted = true; + + TClientBase::finishCameraOps(); + + disconnect(); + + ALOGI("Closed Camera %d", TClientBase::mCameraId); +} + +template <typename TClientBase> +status_t Camera2ClientBase<TClientBase>::dump(int fd, + const Vector<String16>& args) { + String8 result; + result.appendFormat("Camera2ClientBase[%d] (%p) PID: %d, dump:\n", + TClientBase::mCameraId, + TClientBase::getRemoteCallback()->asBinder().get(), + TClientBase::mClientPid); + result.append(" State: "); + + write(fd, result.string(), result.size()); + // TODO: print dynamic/request section from most recent requests + + return dumpDevice(fd, args); +} + +template <typename TClientBase> +status_t Camera2ClientBase<TClientBase>::dumpDevice( + int fd, + const Vector<String16>& args) { + String8 result; + + result = " Device dump:\n"; + write(fd, result.string(), result.size()); + + if (!mDevice.get()) { + result = " *** Device is detached\n"; + write(fd, result.string(), result.size()); + return NO_ERROR; + } + + status_t res = mDevice->dump(fd, args); + if (res != OK) { + result = String8::format(" Error dumping device: %s (%d)", + strerror(-res), res); + write(fd, result.string(), result.size()); + } + + return NO_ERROR; +} + +// ICameraClient2BaseUser interface + + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::disconnect() { + ATRACE_CALL(); + Mutex::Autolock icl(mBinderSerializationLock); + + // Allow both client and the media server to disconnect at all times + int callingPid = getCallingPid(); + if (callingPid != TClientBase::mClientPid && + callingPid != TClientBase::mServicePid) return; + + ALOGV("Camera %d: Shutting down", TClientBase::mCameraId); + + detachDevice(); + + CameraService::BasicClient::disconnect(); + + ALOGV("Camera %d: Shut down complete complete", TClientBase::mCameraId); +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::detachDevice() { + if (mDevice == 0) return; + mDevice->disconnect(); + + mDevice.clear(); + + ALOGV("Camera %d: Detach complete", TClientBase::mCameraId); +} + +template <typename TClientBase> +status_t Camera2ClientBase<TClientBase>::connect( + const sp<TCamCallbacks>& client) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mBinderSerializationLock); + + if (TClientBase::mClientPid != 0 && + getCallingPid() != TClientBase::mClientPid) { + + ALOGE("%s: Camera %d: Connection attempt from pid %d; " + "current locked to pid %d", + __FUNCTION__, + TClientBase::mCameraId, + getCallingPid(), + TClientBase::mClientPid); + return BAD_VALUE; + } + + TClientBase::mClientPid = getCallingPid(); + + TClientBase::mRemoteCallback = client; + mSharedCameraCallbacks = client; + + return OK; +} + +/** Device-related methods */ + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyError(int errorCode, int arg1, + int arg2) { + ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, + arg1, arg2); +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyShutter(int frameNumber, + nsecs_t timestamp) { + (void)frameNumber; + (void)timestamp; + + ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__, + frameNumber, timestamp); +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyAutoFocus(uint8_t newState, + int triggerId) { + (void)newState; + (void)triggerId; + + ALOGV("%s: Autofocus state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); + + typename SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, 1, 0); + } + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, 1, 0); + } +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyAutoExposure(uint8_t newState, + int triggerId) { + (void)newState; + (void)triggerId; + + ALOGV("%s: Autoexposure state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyAutoWhitebalance(uint8_t newState, + int triggerId) { + (void)newState; + (void)triggerId; + + ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); +} + +template <typename TClientBase> +int Camera2ClientBase<TClientBase>::getCameraId() const { + return TClientBase::mCameraId; +} + +template <typename TClientBase> +const sp<CameraDeviceBase>& Camera2ClientBase<TClientBase>::getCameraDevice() { + return mDevice; +} + +template <typename TClientBase> +const sp<CameraService>& Camera2ClientBase<TClientBase>::getCameraService() { + return TClientBase::mCameraService; +} + +template <typename TClientBase> +Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::Lock( + SharedCameraCallbacks &client) : + + mRemoteCallback(client.mRemoteCallback), + mSharedClient(client) { + + mSharedClient.mRemoteCallbackLock.lock(); +} + +template <typename TClientBase> +Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::~Lock() { + mSharedClient.mRemoteCallbackLock.unlock(); +} + +template <typename TClientBase> +Camera2ClientBase<TClientBase>::SharedCameraCallbacks::SharedCameraCallbacks( + const sp<TCamCallbacks>&client) : + + mRemoteCallback(client) { +} + +template <typename TClientBase> +typename Camera2ClientBase<TClientBase>::SharedCameraCallbacks& +Camera2ClientBase<TClientBase>::SharedCameraCallbacks::operator=( + const sp<TCamCallbacks>&client) { + + Mutex::Autolock l(mRemoteCallbackLock); + mRemoteCallback = client; + return *this; +} + +template <typename TClientBase> +void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() { + Mutex::Autolock l(mRemoteCallbackLock); + mRemoteCallback.clear(); +} + +template class Camera2ClientBase<CameraService::ProClient>; +template class Camera2ClientBase<CameraService::Client>; + +} // namespace android diff --git a/services/camera/libcameraservice/Camera2ClientBase.h b/services/camera/libcameraservice/Camera2ClientBase.h new file mode 100644 index 0000000..9001efb --- /dev/null +++ b/services/camera/libcameraservice/Camera2ClientBase.h @@ -0,0 +1,128 @@ +/* + * 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_CAMERA_CAMERA2CLIENT_BASE_H +#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H + +#include "CameraDeviceBase.h" +#include "CameraService.h" + +namespace android { + +class IMemory; + +template <typename TClientBase> +class Camera2ClientBase : + public TClientBase, + public CameraDeviceBase::NotificationListener +{ +public: + typedef typename TClientBase::TCamCallbacks TCamCallbacks; + + /** + * Base binder interface (see ICamera/IProCameraUser for details) + */ + virtual status_t connect(const sp<TCamCallbacks>& callbacks); + virtual void disconnect(); + + /** + * Interface used by CameraService + */ + + // TODO: too many params, move into a ClientArgs<T> + Camera2ClientBase(const sp<CameraService>& cameraService, + const sp<TCamCallbacks>& remoteCallback, + const String16& clientPackageName, + int cameraId, + int cameraFacing, + int clientPid, + uid_t clientUid, + int servicePid); + virtual ~Camera2ClientBase(); + + virtual status_t initialize(camera_module_t *module); + virtual status_t dump(int fd, const Vector<String16>& args); + + /** + * CameraDeviceBase::NotificationListener implementation + */ + + virtual void notifyError(int errorCode, int arg1, int arg2); + virtual void notifyShutter(int frameNumber, nsecs_t timestamp); + virtual void notifyAutoFocus(uint8_t newState, int triggerId); + virtual void notifyAutoExposure(uint8_t newState, int triggerId); + virtual void notifyAutoWhitebalance(uint8_t newState, + int triggerId); + + + int getCameraId() const; + const sp<CameraDeviceBase>& + getCameraDevice(); + const sp<CameraService>& + getCameraService(); + + /** + * Interface used by independent components of CameraClient2Base. + */ + + // Simple class to ensure that access to TCamCallbacks is serialized + // by requiring mRemoteCallbackLock to be locked before access to + // mRemoteCallback is possible. + class SharedCameraCallbacks { + public: + class Lock { + public: + Lock(SharedCameraCallbacks &client); + ~Lock(); + sp<TCamCallbacks> &mRemoteCallback; + private: + SharedCameraCallbacks &mSharedClient; + }; + SharedCameraCallbacks(const sp<TCamCallbacks>& client); + SharedCameraCallbacks& operator=(const sp<TCamCallbacks>& client); + void clear(); + private: + sp<TCamCallbacks> mRemoteCallback; + mutable Mutex mRemoteCallbackLock; + } mSharedCameraCallbacks; + +protected: + + virtual status_t dumpDevice(int fd, const Vector<String16>& args); + + /** Binder client interface-related private members */ + + // Mutex that must be locked by methods implementing the binder client + // interface. Ensures serialization between incoming client calls. + // All methods in this class hierarchy that append 'L' to the name assume + // that mBinderSerializationLock is locked when they're called + mutable Mutex mBinderSerializationLock; + + /** CameraDeviceBase instance wrapping HAL2+ entry */ + + sp<CameraDeviceBase> mDevice; + + /** Utility members */ + + // Verify that caller is the owner of the camera + status_t checkPid(const char *checkLocation) const; + + virtual void detachDevice(); +}; + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp index 81e58ca..37ba5ae 100644 --- a/services/camera/libcameraservice/Camera2Device.cpp +++ b/services/camera/libcameraservice/Camera2Device.cpp @@ -47,6 +47,10 @@ Camera2Device::~Camera2Device() disconnect(); } +int Camera2Device::getId() const { + return mId; +} + status_t Camera2Device::initialize(camera_module_t *module) { ATRACE_CALL(); diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h index 1adb7a9..3034a1d 100644 --- a/services/camera/libcameraservice/Camera2Device.h +++ b/services/camera/libcameraservice/Camera2Device.h @@ -38,6 +38,7 @@ class Camera2Device: public CameraDeviceBase { /** * CameraDevice interface */ + virtual int getId() const; virtual status_t initialize(camera_module_t *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp index 2a1be09..04a6e6a 100644 --- a/services/camera/libcameraservice/Camera3Device.cpp +++ b/services/camera/libcameraservice/Camera3Device.cpp @@ -50,6 +50,10 @@ Camera3Device::~Camera3Device() disconnect(); } +int Camera3Device::getId() const { + return mId; +} + status_t Camera3Device::initialize(camera_module_t *module) { ATRACE_CALL(); diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h index 2bc7cf0..df7352c 100644 --- a/services/camera/libcameraservice/Camera3Device.h +++ b/services/camera/libcameraservice/Camera3Device.h @@ -57,6 +57,7 @@ class Camera3Device : /** * CameraDevice interface */ + virtual int getId() const; virtual status_t initialize(camera_module_t *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16> &args); diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp index 90f8f40..e577fa3 100644 --- a/services/camera/libcameraservice/CameraClient.cpp +++ b/services/camera/libcameraservice/CameraClient.cpp @@ -117,7 +117,7 @@ status_t CameraClient::dump(int fd, const Vector<String16>& args) { size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", mCameraId, - getCameraClient()->asBinder().get(), + getRemoteCallback()->asBinder().get(), mClientPid); len = (len > SIZE - 1) ? SIZE - 1 : len; write(fd, buffer, len); @@ -173,10 +173,10 @@ status_t CameraClient::unlock() { return INVALID_OPERATION; } mClientPid = 0; - LOG1("clear mCameraClient (pid %d)", callingPid); + LOG1("clear mRemoteCallback (pid %d)", callingPid); // we need to remove the reference to ICameraClient so that when the app // goes away, the reference count goes to 0. - mCameraClient.clear(); + mRemoteCallback.clear(); } return result; } @@ -193,14 +193,15 @@ status_t CameraClient::connect(const sp<ICameraClient>& client) { return EBUSY; } - if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) { + if (mRemoteCallback != 0 && + (client->asBinder() == mRemoteCallback->asBinder())) { LOG1("Connect to the same client"); return NO_ERROR; } mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; mClientPid = callingPid; - mCameraClient = client; + mRemoteCallback = client; LOG1("connect X (pid %d)", callingPid); return NO_ERROR; @@ -780,7 +781,7 @@ void CameraClient::handleShutter(void) { mCameraService->playSound(CameraService::SOUND_SHUTTER); } - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; if (c != 0) { mLock.unlock(); c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); @@ -811,7 +812,7 @@ void CameraClient::handlePreviewData(int32_t msgType, } // hold a strong pointer to the client - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; // clear callback flags if no client or one-shot mode if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { @@ -841,7 +842,7 @@ void CameraClient::handlePreviewData(int32_t msgType, void CameraClient::handlePostview(const sp<IMemory>& mem) { disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL); @@ -856,7 +857,7 @@ void CameraClient::handleRawPicture(const sp<IMemory>& mem) { size_t size; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL); @@ -867,7 +868,7 @@ void CameraClient::handleRawPicture(const sp<IMemory>& mem) { void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) { disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL); @@ -877,7 +878,7 @@ void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) { void CameraClient::handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2) { - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->notifyCallback(msgType, ext1, ext2); @@ -886,7 +887,7 @@ void CameraClient::handleGenericNotify(int32_t msgType, void CameraClient::handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) { - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->dataCallback(msgType, dataPtr, metadata); @@ -895,7 +896,7 @@ void CameraClient::handleGenericData(int32_t msgType, void CameraClient::handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { - sp<ICameraClient> c = mCameraClient; + sp<ICameraClient> c = mRemoteCallback; mLock.unlock(); if (c != 0) { c->dataCallbackTimestamp(timestamp, msgType, dataPtr); diff --git a/services/camera/libcameraservice/CameraDeviceBase.h b/services/camera/libcameraservice/CameraDeviceBase.h index 8252af7..8c457d9 100644 --- a/services/camera/libcameraservice/CameraDeviceBase.h +++ b/services/camera/libcameraservice/CameraDeviceBase.h @@ -36,6 +36,11 @@ class CameraDeviceBase : public virtual RefBase { public: virtual ~CameraDeviceBase(); + /** + * The device's camera ID + */ + virtual int getId() const = 0; + virtual status_t initialize(camera_module_t *module) = 0; virtual status_t disconnect() = 0; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 8c4f619..5a6a3c8 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -176,18 +176,12 @@ bool CameraService::isValidCameraId(int cameraId) { return false; } -sp<ICamera> CameraService::connect( - const sp<ICameraClient>& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid) { +bool CameraService::validateConnect(int cameraId, + /*inout*/ + int& clientUid) const { - String8 clientName8(clientPackageName); int callingPid = getCallingPid(); - LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - if (clientUid == USE_CALLING_UID) { clientUid = getCallingUid(); } else { @@ -195,20 +189,19 @@ sp<ICamera> CameraService::connect( if (callingPid != getpid()) { ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)", callingPid); - return NULL; + return false; } } if (!mModule) { ALOGE("Camera HAL module not loaded"); - return NULL; + return false; } - sp<Client> client; if (cameraId < 0 || cameraId >= mNumberOfCameras) { ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", callingPid, cameraId); - return NULL; + return false; } char value[PROPERTY_VALUE_MAX]; @@ -216,22 +209,32 @@ sp<ICamera> CameraService::connect( if (strcmp(value, "1") == 0) { // Camera is disabled by DevicePolicyManager. ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); - return NULL; + return false; } - Mutex::Autolock lock(mServiceLock); + return true; +} + +bool CameraService::canConnectUnsafe(int cameraId, + const String16& clientPackageName, + const sp<IBinder>& remoteCallback, + sp<Client> &client) { + String8 clientName8(clientPackageName); + int callingPid = getCallingPid(); + if (mClient[cameraId] != 0) { client = mClient[cameraId].promote(); if (client != 0) { - if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { + if (remoteCallback == client->getRemoteCallback()->asBinder()) { LOG1("CameraService::connect X (pid %d) (the same client)", callingPid); - return client; + return true; } else { - // TODOSC: need to support 1 regular client, multiple shared clients here - ALOGW("CameraService::connect X (pid %d) rejected (existing client).", - callingPid); - return NULL; + // TODOSC: need to support 1 regular client, + // multiple shared clients here + ALOGW("CameraService::connect X (pid %d) rejected" + " (existing client).", callingPid); + return false; } } mClient[cameraId].clear(); @@ -247,57 +250,103 @@ sp<ICamera> CameraService::connect( would be fine */ if (mBusy[cameraId]) { - ALOGW("CameraService::connect X (pid %d, \"%s\") rejected" " (camera %d is still busy).", callingPid, clientName8.string(), cameraId); - return NULL; + return false; } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + return true; +} - if (isValidCameraId(cameraId)) { - updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, cameraId); - } +sp<ICamera> CameraService::connect( + const sp<ICameraClient>& cameraClient, + int cameraId, + const String16& clientPackageName, + int clientUid) { - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid()); - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - client = new Camera2Client(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), - deviceVersion); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return NULL; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return NULL; - } + String8 clientName8(clientPackageName); + int callingPid = getCallingPid(); - if (client->initialize(mModule) != OK) { - // this is probably not recoverable.. but maybe the client can try again - updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId); + LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, + clientName8.string(), cameraId); + if (!validateConnect(cameraId, /*inout*/clientUid)) { return NULL; } - cameraClient->asBinder()->linkToDeath(this); + sp<Client> client; + + { + Mutex::Autolock lock(mServiceLock); + if (!canConnectUnsafe(cameraId, clientPackageName, + cameraClient->asBinder(), + /*out*/client)) { + return NULL; + } else if (client.get() != NULL) { + return client; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); + + // If there are other non-exclusive users of the camera, + // this will tear them down before we can reuse the camera + if (isValidCameraId(cameraId)) { + updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, + cameraId); + } + + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + client = new CameraClient(this, cameraClient, + clientPackageName, cameraId, + facing, callingPid, clientUid, getpid()); + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + client = new Camera2Client(this, cameraClient, + clientPackageName, cameraId, + facing, callingPid, clientUid, getpid(), + deviceVersion); + break; + case -1: + ALOGE("Invalid camera id %d", cameraId); + return NULL; + default: + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return NULL; + } + + if (!connectFinishUnsafe(client, client->asBinder())) { + // this is probably not recoverable.. maybe the client can try again + updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId); + + return NULL; + } - mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid()); + mClient[cameraId] = client; + LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, + getpid()); + } + // important: release the mutex here so the client can call back + // into the service from its destructor (can be at the end of the call) return client; } +bool CameraService::connectFinishUnsafe(const sp<BasicClient>& client, + const sp<IBinder>& clientBinder) { + if (client->initialize(mModule) != OK) { + return false; + } + + clientBinder->linkToDeath(this); + + return true; +} + sp<IProCameraUser> CameraService::connect( const sp<IProCameraCallbacks>& cameraCb, int cameraId, @@ -307,70 +356,59 @@ sp<IProCameraUser> CameraService::connect( String8 clientName8(clientPackageName); int callingPid = getCallingPid(); - // TODO: use clientPackageName and clientUid with appOpsMangr - - LOG1("CameraService::connectPro E (pid %d, id %d)", callingPid, cameraId); + LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid, + clientName8.string(), cameraId); - if (!mModule) { - ALOGE("Camera HAL module not loaded"); + if (!validateConnect(cameraId, /*inout*/clientUid)) { return NULL; } sp<ProClient> client; - if (cameraId < 0 || cameraId >= mNumberOfCameras) { - ALOGE("CameraService::connectPro X (pid %d) rejected (invalid cameraId %d).", - callingPid, cameraId); - return NULL; - } - - char value[PROPERTY_VALUE_MAX]; - property_get("sys.secpolicy.camera.disabled", value, "0"); - if (strcmp(value, "1") == 0) { - // Camera is disabled by DevicePolicyManager. - ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); - return NULL; - } + { + Mutex::Autolock lock(mServiceLock); + { + sp<Client> client; + if (!canConnectUnsafe(cameraId, clientPackageName, + cameraCb->asBinder(), + /*out*/client)) { + return NULL; + } + } - // TODO: allow concurrent connections with a ProCamera - if (mBusy[cameraId]) { + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); - ALOGW("CameraService::connectPro X (pid %d, \"%s\") rejected" - " (camera %d is still busy).", callingPid, - clientName8.string(), cameraId); - return NULL; - } + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", + cameraId); + return NULL; + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + client = new ProCamera2Client(this, cameraCb, String16(), + cameraId, facing, callingPid, USE_CALLING_UID, getpid()); + break; + case -1: + ALOGE("Invalid camera id %d", cameraId); + return NULL; + default: + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return NULL; + } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + if (!connectFinishUnsafe(client, client->asBinder())) { + return NULL; + } - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", cameraId); - return NULL; - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - client = new ProCamera2Client(this, cameraCb, String16(), - cameraId, facing, callingPid, USE_CALLING_UID, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return NULL; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return NULL; - } + mProClientList[cameraId].push(client); - if (client->initialize(mModule) != OK) { - return NULL; + LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, + getpid()); } + // important: release the mutex here so the client can call back + // into the service from its destructor (can be at the end of the call) - mProClientList[cameraId].push(client); - - cameraCb->asBinder()->linkToDeath(this); - - LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, - getpid()); return client; } @@ -496,7 +534,7 @@ sp<CameraService::Client> CameraService::findClientUnsafe( continue; } - if (cameraClient == client->getCameraClient()->asBinder()) { + if (cameraClient == client->getRemoteCallback()->asBinder()) { // Found our camera outIndex = i; return client; @@ -639,7 +677,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, int callingPid = getCallingPid(); LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId); - mCameraClient = cameraClient; + mRemoteCallback = cameraClient; cameraService->setCameraBusy(cameraId); cameraService->loadSound(); @@ -652,7 +690,6 @@ CameraService::Client::~Client() { mDestructionStarted = true; mCameraService->releaseSound(); - finishCameraOps(); // unconditionally disconnect. function is idempotent Client::disconnect(); } @@ -666,7 +703,7 @@ CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService, mClientPackageName(clientPackageName) { mCameraService = cameraService; - mRemoteCallback = remoteCallback; + mRemoteBinder = remoteCallback; mCameraId = cameraId; mCameraFacing = cameraFacing; mClientPid = clientPid; @@ -681,7 +718,7 @@ CameraService::BasicClient::~BasicClient() { } void CameraService::BasicClient::disconnect() { - mCameraService->removeClientByRemote(mRemoteCallback); + mCameraService->removeClientByRemote(mRemoteBinder); } status_t CameraService::BasicClient::startCameraOps() { @@ -689,6 +726,11 @@ status_t CameraService::BasicClient::startCameraOps() { mOpsCallback = new OpsCallback(this); + { + ALOGV("%s: Start camera ops, package name = %s, client UID = %d", + __FUNCTION__, String8(mClientPackageName).string(), mClientUid); + } + mAppOpsManager.startWatchingMode(AppOpsManager::OP_CAMERA, mClientPackageName, mOpsCallback); res = mAppOpsManager.startOp(AppOpsManager::OP_CAMERA, @@ -767,7 +809,7 @@ CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { } void CameraService::Client::notifyError() { - mCameraClient->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); + mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); } // NOTE: function is idempotent @@ -810,79 +852,10 @@ CameraService::ProClient::ProClient(const sp<CameraService>& cameraService, } CameraService::ProClient::~ProClient() { - mDestructionStarted = true; - - ProClient::disconnect(); -} - -status_t CameraService::ProClient::connect(const sp<IProCameraCallbacks>& callbacks) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - return INVALID_OPERATION; -} - -void CameraService::ProClient::disconnect() { - BasicClient::disconnect(); -} - -status_t CameraService::ProClient::initialize(camera_module_t* module) -{ - ALOGW("%s: not implemented yet", __FUNCTION__); - return OK; -} - -status_t CameraService::ProClient::exclusiveTryLock() { - ALOGE("%s: not implemented yet", __FUNCTION__); - return INVALID_OPERATION; -} - -status_t CameraService::ProClient::exclusiveLock() { - ALOGE("%s: not implemented yet", __FUNCTION__); - return INVALID_OPERATION; -} - -status_t CameraService::ProClient::exclusiveUnlock() { - ALOGE("%s: not implemented yet", __FUNCTION__); - return INVALID_OPERATION; -} - -bool CameraService::ProClient::hasExclusiveLock() { - ALOGE("%s: not implemented yet", __FUNCTION__); - return false; -} - -void CameraService::ProClient::onExclusiveLockStolen() { - ALOGE("%s: not implemented yet", __FUNCTION__); -} - -status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - free_camera_metadata(request); - - return INVALID_OPERATION; -} - -status_t CameraService::ProClient::cancelRequest(int requestId) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - return INVALID_OPERATION; -} - -status_t CameraService::ProClient::requestStream(int streamId) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - return INVALID_OPERATION; -} - -status_t CameraService::ProClient::cancelStream(int streamId) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - return INVALID_OPERATION; } void CameraService::ProClient::notifyError() { - ALOGE("%s: not implemented yet", __FUNCTION__); + mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); } // ---------------------------------------------------------------------------- diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 8acc63f..c5e495f 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -108,8 +108,9 @@ public: virtual void disconnect() = 0; + // Return the remote callback binder object (e.g. IProCameraCallbacks) wp<IBinder> getRemote() { - return mRemoteCallback; + return mRemoteBinder; } protected: @@ -140,7 +141,7 @@ public: pid_t mServicePid; // immutable after constructor // - The app-side Binder interface to receive callbacks from us - wp<IBinder> mRemoteCallback; // immutable after constructor + wp<IBinder> mRemoteBinder; // immutable after constructor // permissions management status_t startCameraOps(); @@ -173,6 +174,8 @@ public: class Client : public BnCamera, public BasicClient { public: + typedef ICameraClient TCamCallbacks; + // ICamera interface (see ICamera for details) virtual void disconnect(); virtual status_t connect(const sp<ICameraClient>& client) = 0; @@ -208,8 +211,8 @@ public: ~Client(); // return our camera client - const sp<ICameraClient>& getCameraClient() { - return mCameraClient; + const sp<ICameraClient>& getRemoteCallback() { + return mRemoteCallback; } protected: @@ -222,12 +225,14 @@ public: // Initialized in constructor // - The app-side Binder interface to receive callbacks from us - sp<ICameraClient> mCameraClient; + sp<ICameraClient> mRemoteCallback; }; // class Client class ProClient : public BnProCameraUser, public BasicClient { public: + typedef IProCameraCallbacks TCamCallbacks; + ProClient(const sp<CameraService>& cameraService, const sp<IProCameraCallbacks>& remoteCallback, const String16& clientPackageName, @@ -243,34 +248,24 @@ public: return mRemoteCallback; } - // BasicClient implementation - virtual status_t initialize(camera_module_t *module); - /*** IProCamera implementation ***/ + virtual status_t connect(const sp<IProCameraCallbacks>& callbacks) + = 0; + virtual status_t exclusiveTryLock() = 0; + virtual status_t exclusiveLock() = 0; + virtual status_t exclusiveUnlock() = 0; - - virtual status_t connect( - const sp<IProCameraCallbacks>& callbacks); - virtual void disconnect(); - - virtual status_t exclusiveTryLock(); - virtual status_t exclusiveLock(); - virtual status_t exclusiveUnlock(); - - virtual bool hasExclusiveLock(); + virtual bool hasExclusiveLock() = 0; // Note that the callee gets a copy of the metadata. virtual int submitRequest(camera_metadata_t* metadata, - bool streaming = false); - virtual status_t cancelRequest(int requestId); - - virtual status_t requestStream(int streamId); - virtual status_t cancelStream(int streamId); + bool streaming = false) = 0; + virtual status_t cancelRequest(int requestId) = 0; // Callbacks from camera service - virtual void onExclusiveLockStolen(); + virtual void onExclusiveLockStolen() = 0; protected: virtual void notifyError(); @@ -283,6 +278,22 @@ private: // Delay-load the Camera HAL module virtual void onFirstRef(); + // Step 1. Check if we can connect, before we acquire the service lock. + bool validateConnect(int cameraId, + /*inout*/ + int& clientUid) const; + + // Step 2. Check if we can connect, after we acquire the service lock. + bool canConnectUnsafe(int cameraId, + const String16& clientPackageName, + const sp<IBinder>& remoteCallback, + /*out*/ + sp<Client> &client); + + // When connection is successful, initialize client and track its death + bool connectFinishUnsafe(const sp<BasicClient>& client, + const sp<IBinder>& clientBinder); + virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient); Mutex mServiceLock; diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp index 6fed8b4..575b075 100644 --- a/services/camera/libcameraservice/ProCamera2Client.cpp +++ b/services/camera/libcameraservice/ProCamera2Client.cpp @@ -27,68 +27,43 @@ #include "camera2/Parameters.h" #include "ProCamera2Client.h" #include "camera2/ProFrameProcessor.h" +#include "CameraDeviceBase.h" namespace android { using namespace camera2; -static int getCallingPid() { - return IPCThreadState::self()->getCallingPid(); -} - -static int getCallingUid() { - return IPCThreadState::self()->getCallingUid(); -} - // Interface used by CameraService ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid): - ProClient(cameraService, remoteCallback, clientPackageName, - cameraId, cameraFacing, clientPid, clientUid, servicePid), - mSharedCameraCallbacks(remoteCallback) + const sp<IProCameraCallbacks>& remoteCallback, + const String16& clientPackageName, + int cameraId, + int cameraFacing, + int clientPid, + uid_t clientUid, + int servicePid) : + Camera2ClientBase(cameraService, remoteCallback, clientPackageName, + cameraId, cameraFacing, clientPid, clientUid, servicePid) { ATRACE_CALL(); ALOGI("ProCamera %d: Opened", cameraId); - mDevice = new Camera2Device(cameraId); - mExclusiveLock = false; } -status_t ProCamera2Client::checkPid(const char* checkLocation) const { - int callingPid = getCallingPid(); - if (callingPid == mClientPid) return NO_ERROR; - - ALOGE("%s: attempt to use a locked camera from a different process" - " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid); - return PERMISSION_DENIED; -} - status_t ProCamera2Client::initialize(camera_module_t *module) { ATRACE_CALL(); - ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId); status_t res; - res = mDevice->initialize(module); + res = Camera2ClientBase::initialize(module); if (res != OK) { - ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", - __FUNCTION__, mCameraId, strerror(-res), res); - return NO_INIT; + return res; } - res = mDevice->setNotifyCallback(this); - String8 threadName; - mFrameProcessor = new ProFrameProcessor(this); - threadName = String8::format("PC2-%d-FrameProc", - mCameraId); + mFrameProcessor = new ProFrameProcessor(mDevice); + threadName = String8::format("PC2-%d-FrameProc", mCameraId); mFrameProcessor->run(threadName.string()); mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID, @@ -99,20 +74,13 @@ status_t ProCamera2Client::initialize(camera_module_t *module) } ProCamera2Client::~ProCamera2Client() { - ATRACE_CALL(); - - mDestructionStarted = true; - - disconnect(); - - ALOGI("ProCamera %d: Closed", mCameraId); } status_t ProCamera2Client::exclusiveTryLock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (!mDevice.get()) return PERMISSION_DENIED; @@ -143,7 +111,7 @@ status_t ProCamera2Client::exclusiveLock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (!mDevice.get()) return PERMISSION_DENIED; @@ -177,7 +145,7 @@ status_t ProCamera2Client::exclusiveUnlock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); // don't allow unlocking if we have no lock @@ -198,6 +166,7 @@ status_t ProCamera2Client::exclusiveUnlock() { } bool ProCamera2Client::hasExclusiveLock() { + Mutex::Autolock icl(mBinderSerializationLock); return mExclusiveLock; } @@ -205,7 +174,7 @@ void ProCamera2Client::onExclusiveLockStolen() { ALOGV("%s: ProClient lost exclusivity (id %d)", __FUNCTION__, mCameraId); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (mExclusiveLock && mRemoteCallback.get() != NULL) { @@ -224,7 +193,7 @@ status_t ProCamera2Client::submitRequest(camera_metadata_t* request, ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; @@ -248,7 +217,7 @@ status_t ProCamera2Client::cancelRequest(int requestId) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; @@ -256,24 +225,19 @@ status_t ProCamera2Client::cancelRequest(int requestId) { return PERMISSION_DENIED; } + // TODO: implement ALOGE("%s: not fully implemented yet", __FUNCTION__); return INVALID_OPERATION; } -status_t ProCamera2Client::requestStream(int streamId) { - ALOGE("%s: not implemented yet", __FUNCTION__); - - return INVALID_OPERATION; -} - -status_t ProCamera2Client::cancelStream(int streamId) { +status_t ProCamera2Client::deleteStream(int streamId) { ATRACE_CALL(); ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; mDevice->clearStreamingRequest(); @@ -301,7 +265,7 @@ status_t ProCamera2Client::createStream(int width, int height, int format, status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; @@ -332,7 +296,7 @@ status_t ProCamera2Client::createDefaultRequest(int templateId, status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; @@ -352,7 +316,7 @@ status_t ProCamera2Client::getCameraInfo(int cameraId, return INVALID_OPERATION; } - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; @@ -373,47 +337,11 @@ status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) { // TODO: print dynamic/request section from most recent requests mFrameProcessor->dump(fd, args); -#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; - - result = " Device dump:\n"; - write(fd, result.string(), result.size()); - - if (!mDevice.get()) { - result = " *** Device is detached\n"; - write(fd, result.string(), result.size()); - return NO_ERROR; - } - - status_t res = mDevice->dump(fd, args); - if (res != OK) { - result = String8::format(" Error dumping device: %s (%d)", - strerror(-res), res); - write(fd, result.string(), result.size()); - } - -#undef CASE_APPEND_ENUM - return NO_ERROR; + return dumpDevice(fd, args); } // IProCameraUser interface -void ProCamera2Client::disconnect() { - ATRACE_CALL(); - Mutex::Autolock icl(mIProCameraUserLock); - status_t res; - - // Allow both client and the media server to disconnect at all times - int callingPid = getCallingPid(); - if (callingPid != mClientPid && callingPid != mServicePid) return; - - ALOGV("Camera %d: Shutting down", mCameraId); - - detachDevice(); - ProClient::disconnect(); - - ALOGV("Camera %d: Shut down complete complete", mCameraId); -} - void ProCamera2Client::detachDevice() { if (mDevice == 0) return; @@ -438,117 +366,16 @@ void ProCamera2Client::detachDevice() { } } - mDevice->disconnect(); - - mDevice.clear(); - - ALOGV("Camera %d: Detach complete", mCameraId); -} - -status_t ProCamera2Client::connect(const sp<IProCameraCallbacks>& client) { - ATRACE_CALL(); - ALOGV("%s: E", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); - - if (mClientPid != 0 && getCallingPid() != mClientPid) { - ALOGE("%s: Camera %d: Connection attempt from pid %d; " - "current locked to pid %d", __FUNCTION__, - mCameraId, getCallingPid(), mClientPid); - return BAD_VALUE; - } - - mClientPid = getCallingPid(); - - mRemoteCallback = client; - mSharedCameraCallbacks = client; - - return OK; + Camera2ClientBase::detachDevice(); } /** Device-related methods */ - -void ProCamera2Client::notifyError(int errorCode, int arg1, int arg2) { - ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, - arg1, arg2); -} - -void ProCamera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) { - ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__, - frameNumber, timestamp); -} - -void ProCamera2Client::notifyAutoFocus(uint8_t newState, int triggerId) { - ALOGV("%s: Autofocus state now %d, last trigger %d", - __FUNCTION__, newState, triggerId); - - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - if (l.mRemoteCallback != 0) { - l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, - 1, 0); - } - if (l.mRemoteCallback != 0) { - l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, - 1, 0); - } -} - -void ProCamera2Client::notifyAutoExposure(uint8_t newState, int triggerId) { - ALOGV("%s: Autoexposure state now %d, last trigger %d", - __FUNCTION__, newState, triggerId); -} - -void ProCamera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) { - ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", - __FUNCTION__, newState, triggerId); -} - -int ProCamera2Client::getCameraId() const { - return mCameraId; -} - -const sp<Camera2Device>& ProCamera2Client::getCameraDevice() { - return mDevice; -} - -const sp<CameraService>& ProCamera2Client::getCameraService() { - return mCameraService; -} - -ProCamera2Client::SharedCameraCallbacks::Lock::Lock( - SharedCameraCallbacks &client): - mRemoteCallback(client.mRemoteCallback), - mSharedClient(client) { - mSharedClient.mRemoteCallbackLock.lock(); -} - -ProCamera2Client::SharedCameraCallbacks::Lock::~Lock() { - mSharedClient.mRemoteCallbackLock.unlock(); -} - -ProCamera2Client::SharedCameraCallbacks::SharedCameraCallbacks - (const sp<IProCameraCallbacks>&client): - mRemoteCallback(client) { -} - -ProCamera2Client::SharedCameraCallbacks& - ProCamera2Client::SharedCameraCallbacks::operator=( - const sp<IProCameraCallbacks>&client) { - Mutex::Autolock l(mRemoteCallbackLock); - mRemoteCallback = client; - return *this; -} - -void ProCamera2Client::SharedCameraCallbacks::clear() { - Mutex::Autolock l(mRemoteCallbackLock); - mRemoteCallback.clear(); -} - void ProCamera2Client::onFrameAvailable(int32_t frameId, const CameraMetadata& frame) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); - Mutex::Autolock icl(mIProCameraUserLock); + Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (mRemoteCallback != NULL) { diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h index ff6f4e2..1dec263 100644 --- a/services/camera/libcameraservice/ProCamera2Client.h +++ b/services/camera/libcameraservice/ProCamera2Client.h @@ -20,6 +20,7 @@ #include "Camera2Device.h" #include "CameraService.h" #include "camera2/ProFrameProcessor.h" +#include "Camera2ClientBase.h" namespace android { @@ -29,17 +30,13 @@ class IMemory; * meant for HAL2-level private API access. */ class ProCamera2Client : - public CameraService::ProClient, - public Camera2Device::NotificationListener, + public Camera2ClientBase<CameraService::ProClient>, public camera2::ProFrameProcessor::FilteredListener { public: /** * IProCameraUser interface (see IProCameraUser for details) */ - virtual status_t connect(const sp<IProCameraCallbacks>& callbacks); - virtual void disconnect(); - virtual status_t exclusiveTryLock(); virtual status_t exclusiveLock(); virtual status_t exclusiveUnlock(); @@ -51,13 +48,15 @@ public: bool streaming = false); virtual status_t cancelRequest(int requestId); - virtual status_t requestStream(int streamId); - virtual status_t cancelStream(int streamId); + virtual status_t deleteStream(int streamId); - virtual status_t createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer, - /*out*/ - int* streamId); + virtual status_t createStream( + int width, + int height, + int format, + const sp<IGraphicBufferProducer>& bufferProducer, + /*out*/ + int* streamId); // Create a request object from a template. // -- Caller owns the newly allocated metadata @@ -85,24 +84,9 @@ public: int servicePid); virtual ~ProCamera2Client(); - status_t initialize(camera_module_t *module); - - virtual status_t dump(int fd, const Vector<String16>& args); - - /** - * Interface used by Camera2Device - */ - - virtual void notifyError(int errorCode, int arg1, int arg2); - virtual void notifyShutter(int frameNumber, nsecs_t timestamp); - virtual void notifyAutoFocus(uint8_t newState, int triggerId); - virtual void notifyAutoExposure(uint8_t newState, int triggerId); - virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId); + virtual status_t initialize(camera_module_t *module); - - int getCameraId() const; - const sp<Camera2Device>& getCameraDevice(); - const sp<CameraService>& getCameraService(); + virtual status_t dump(int fd, const Vector<String16>& args); // Callbacks from camera service virtual void onExclusiveLockStolen(); @@ -111,67 +95,26 @@ public: * Interface used by independent components of ProCamera2Client. */ - // Simple class to ensure that access to IProCameraCallbacks is serialized - // by requiring mRemoteCallbackLock to be locked before access to - // mCameraClient is possible. - class SharedCameraCallbacks { - public: - class Lock { - public: - Lock(SharedCameraCallbacks &client); - ~Lock(); - sp<IProCameraCallbacks> &mRemoteCallback; - private: - SharedCameraCallbacks &mSharedClient; - }; - SharedCameraCallbacks(const sp<IProCameraCallbacks>& client); - SharedCameraCallbacks& operator=(const sp<IProCameraCallbacks>& client); - void clear(); - private: - sp<IProCameraCallbacks> mRemoteCallback; - mutable Mutex mRemoteCallbackLock; - } mSharedCameraCallbacks; - protected: /** FilteredListener implementation **/ - virtual void onFrameAvailable(int32_t frameId, const CameraMetadata& frame); + virtual void onFrameAvailable(int32_t frameId, + const CameraMetadata& frame); + virtual void detachDevice(); private: /** IProCameraUser interface-related private members */ - // Mutex that must be locked by methods implementing the IProCameraUser - // interface. Ensures serialization between incoming IProCameraUser calls. - // All methods below that append 'L' to the name assume that - // mIProCameraUserLock is locked when they're called - mutable Mutex mIProCameraUserLock; - - // Used with stream IDs - static const int NO_STREAM = -1; - - /* Preview/Recording related members */ - - sp<IBinder> mPreviewSurface; - /** Preview callback related members */ sp<camera2::ProFrameProcessor> mFrameProcessor; static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0; static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL; - /** Camera2Device instance wrapping HAL2 entry */ - - sp<Camera2Device> mDevice; - /** Utility members */ - // Verify that caller is the owner of the camera - status_t checkPid(const char *checkLocation) const; - // Whether or not we have an exclusive lock on the device // - if no we can't modify the request queue. // note that creating/deleting streams we own is still OK bool mExclusiveLock; - - void detachDevice(); }; }; // namespace android diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp index 9a14758..30c14ef 100644 --- a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp +++ b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp @@ -278,11 +278,12 @@ status_t CallbackProcessor::processNewCallback(sp<Camera2Client> &client) { // Call outside parameter lock to allow re-entrancy from notification { - Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); - if (l.mCameraClient != 0) { + Camera2Client::SharedCameraCallbacks::Lock + l(client->mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { ALOGV("%s: Camera %d: Invoking client data callback", __FUNCTION__, client->getCameraId()); - l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME, + l.mRemoteCallback->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mCallbackHeap->mBuffers[heapIdx], NULL); } } diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp index 513a47e..1880912 100644 --- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp +++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp @@ -271,10 +271,11 @@ CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &c } if (mCaptureBuffer != 0 && res == OK) { - Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + Camera2Client::SharedCameraCallbacks::Lock + l(client->mSharedCameraCallbacks); ALOGV("%s: Sending still image to client", __FUNCTION__); - if (l.mCameraClient != 0) { - l.mCameraClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mCaptureBuffer, NULL); } else { ALOGV("%s: No client!", __FUNCTION__); @@ -344,7 +345,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageZslStart( } SharedParameters::Lock l(client->getParameters()); - /* warning: this also locks a SharedCameraClient */ + /* warning: this also locks a SharedCameraCallbacks */ shutterNotifyLocked(l.mParameters, client, mMsgType); mShutterNotified = true; mTimeoutCount = kMaxTimeoutsForCaptureEnd; @@ -496,7 +497,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait( } if (mNewFrameReceived && !mShutterNotified) { SharedParameters::Lock l(client->getParameters()); - /* warning: this also locks a SharedCameraClient */ + /* warning: this also locks a SharedCameraCallbacks */ shutterNotifyLocked(l.mParameters, client, mMsgType); mShutterNotified = true; } @@ -651,16 +652,17 @@ status_t CaptureSequencer::updateCaptureRequest(const Parameters ¶ms, } { - Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + Camera2Client::SharedCameraCallbacks::Lock + l(client->mSharedCameraCallbacks); ALOGV("%s: Notifying of shutter close to client", __FUNCTION__); - if (l.mCameraClient != 0) { + if (l.mRemoteCallback != 0) { // ShutterCallback - l.mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, + l.mRemoteCallback->notifyCallback(CAMERA_MSG_SHUTTER, /*ext1*/0, /*ext2*/0); // RawCallback with null buffer - l.mCameraClient->notifyCallback(CAMERA_MSG_RAW_IMAGE_NOTIFY, + l.mRemoteCallback->notifyCallback(CAMERA_MSG_RAW_IMAGE_NOTIFY, /*ext1*/0, /*ext2*/0); } else { ALOGV("%s: No client!", __FUNCTION__); diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp index 1f2659c..d13d398 100644 --- a/services/camera/libcameraservice/camera2/FrameProcessor.cpp +++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp @@ -28,146 +28,37 @@ namespace android { namespace camera2 { -FrameProcessor::FrameProcessor(wp<Camera2Client> client): - Thread(false), mClient(client), mLastFrameNumberOfFaces(0) { +FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device, + wp<Camera2Client> client) : + ProFrameProcessor(device), + mClient(client), + mLastFrameNumberOfFaces(0) { } FrameProcessor::~FrameProcessor() { - ALOGV("%s: Exit", __FUNCTION__); } -status_t FrameProcessor::registerListener(int32_t minId, - int32_t maxId, wp<FilteredListener> listener) { - Mutex::Autolock l(mInputMutex); - ALOGV("%s: Registering listener for frame id range %d - %d", - __FUNCTION__, minId, maxId); - RangeListener rListener = { minId, maxId, listener }; - mRangeListeners.push_back(rListener); - return OK; -} +bool FrameProcessor::processSingleFrame(CameraMetadata &frame, + const sp<CameraDeviceBase> &device) { -status_t FrameProcessor::removeListener(int32_t minId, - int32_t maxId, wp<FilteredListener> listener) { - Mutex::Autolock l(mInputMutex); - List<RangeListener>::iterator item = mRangeListeners.begin(); - while (item != mRangeListeners.end()) { - if (item->minId == minId && - item->maxId == maxId && - item->listener == listener) { - item = mRangeListeners.erase(item); - } else { - item++; - } + sp<Camera2Client> client = mClient.promote(); + if (!client.get()) { + return false; } - return OK; -} -void FrameProcessor::dump(int fd, const Vector<String16>& /*args*/) { - String8 result(" Latest received frame:\n"); - write(fd, result.string(), result.size()); - mLastFrame.dump(fd, 2, 6); -} - -bool FrameProcessor::threadLoop() { - status_t res; - - sp<CameraDeviceBase> device; - { - sp<Camera2Client> client = mClient.promote(); - if (client == 0) return false; - device = client->getCameraDevice(); - if (device == 0) return false; + if (processFaceDetect(frame, client) != OK) { + return false; } - res = device->waitForNextFrame(kWaitDuration); - if (res == OK) { - sp<Camera2Client> client = mClient.promote(); - if (client == 0) return false; - processNewFrames(client); - } else if (res != TIMED_OUT) { - ALOGE("Camera2Client::FrameProcessor: Error waiting for new " - "frames: %s (%d)", strerror(-res), res); + if (!ProFrameProcessor::processSingleFrame(frame, device)) { + return false; } return true; } -void FrameProcessor::processNewFrames(sp<Camera2Client> &client) { - status_t res; - ATRACE_CALL(); - CameraMetadata frame; - while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) { - camera_metadata_entry_t entry; - - entry = frame.find(ANDROID_REQUEST_FRAME_COUNT); - if (entry.count == 0) { - ALOGE("%s: Camera %d: Error reading frame number", - __FUNCTION__, client->getCameraId()); - break; - } - ATRACE_INT("cam2_frame", entry.data.i32[0]); - - res = processFaceDetect(frame, client); - if (res != OK) break; - - res = processListeners(frame, client); - if (res != OK) break; - - if (!frame.isEmpty()) { - mLastFrame.acquire(frame); - } - } - if (res != NOT_ENOUGH_DATA) { - ALOGE("%s: Camera %d: Error getting next frame: %s (%d)", - __FUNCTION__, client->getCameraId(), strerror(-res), res); - return; - } - - return; -} - -status_t FrameProcessor::processListeners(const CameraMetadata &frame, - sp<Camera2Client> &client) { - ATRACE_CALL(); - camera_metadata_ro_entry_t entry; - - entry = frame.find(ANDROID_REQUEST_ID); - if (entry.count == 0) { - ALOGE("%s: Camera %d: Error reading frame id", - __FUNCTION__, client->getCameraId()); - return BAD_VALUE; - } - int32_t frameId = entry.data.i32[0]; - - List<sp<FilteredListener> > listeners; - { - Mutex::Autolock l(mInputMutex); - - List<RangeListener>::iterator item = mRangeListeners.begin(); - while (item != mRangeListeners.end()) { - if (frameId >= item->minId && - frameId < item->maxId) { - sp<FilteredListener> listener = item->listener.promote(); - if (listener == 0) { - item = mRangeListeners.erase(item); - continue; - } else { - listeners.push_back(listener); - } - } - item++; - } - } - ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size()); - List<sp<FilteredListener> >::iterator item = listeners.begin(); - for (; item != listeners.end(); item++) { - (*item)->onFrameAvailable(frameId, frame); - } - return OK; -} - status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, - sp<Camera2Client> &client) { + const sp<Camera2Client> &client) { status_t res = BAD_VALUE; ATRACE_CALL(); camera_metadata_ro_entry_t entry; @@ -190,12 +81,14 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, Vector<camera_face_t> faces; metadata.number_of_faces = 0; - if (enableFaceDetect && faceDetectMode != ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) { + if (enableFaceDetect && + faceDetectMode != ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) { + SharedParameters::Lock l(client->getParameters()); entry = frame.find(ANDROID_STATISTICS_FACE_RECTANGLES); if (entry.count == 0) { // No faces this frame - /* warning: locks SharedCameraClient */ + /* warning: locks SharedCameraCallbacks */ callbackFaceDetection(client, metadata); return OK; } @@ -263,17 +156,17 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) { face.id = faceIds[i]; face.left_eye[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 0]); + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 0]); face.left_eye[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 1]); + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 1]); face.right_eye[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 2]); + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 2]); face.right_eye[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 3]); + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 3]); face.mouth[0] = - l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 4]); + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 4]); face.mouth[1] = - l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 5]); + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 5]); } else { face.id = 0; face.left_eye[0] = face.left_eye[1] = -2000; @@ -286,21 +179,31 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, metadata.faces = faces.editArray(); } - /* warning: locks SharedCameraClient */ + /* warning: locks SharedCameraCallbacks */ callbackFaceDetection(client, metadata); return OK; } void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client, - /*in*/camera_frame_metadata &metadata) { - - /* Filter out repeated 0-face callbacks, but not when the last frame was >0 */ - if (metadata.number_of_faces != 0 || mLastFrameNumberOfFaces != metadata.number_of_faces) { - Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); - if (l.mCameraClient != NULL) { - l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_METADATA, - NULL, &metadata); + const camera_frame_metadata &metadata) { + + camera_frame_metadata *metadata_ptr = + const_cast<camera_frame_metadata*>(&metadata); + + /** + * Filter out repeated 0-face callbacks, + * but not when the last frame was >0 + */ + if (metadata.number_of_faces != 0 || + mLastFrameNumberOfFaces != metadata.number_of_faces) { + + Camera2Client::SharedCameraCallbacks::Lock + l(client->mSharedCameraCallbacks); + if (l.mRemoteCallback != NULL) { + l.mRemoteCallback->dataCallback(CAMERA_MSG_PREVIEW_METADATA, + NULL, + metadata_ptr); } } diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h index 66e3cda..27ed8f6 100644 --- a/services/camera/libcameraservice/camera2/FrameProcessor.h +++ b/services/camera/libcameraservice/camera2/FrameProcessor.h @@ -22,7 +22,9 @@ #include <utils/Vector.h> #include <utils/KeyedVector.h> #include <utils/List.h> -#include "camera/CameraMetadata.h" +#include <camera/CameraMetadata.h> + +#include "ProFrameProcessor.h" struct camera_frame_metadata; @@ -35,51 +37,26 @@ namespace camera2 { /* Output frame metadata processing thread. This thread waits for new * frames from the device, and analyzes them as necessary. */ -class FrameProcessor: public Thread { +class FrameProcessor : public ProFrameProcessor { public: - FrameProcessor(wp<Camera2Client> client); + FrameProcessor(wp<CameraDeviceBase> device, wp<Camera2Client> client); ~FrameProcessor(); - struct FilteredListener: virtual public RefBase { - virtual void onFrameAvailable(int32_t frameId, - const CameraMetadata &frame) = 0; - }; - - // Register a listener for a range of IDs [minId, maxId). Multiple listeners - // can be listening to the same range - status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); - status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); - - void dump(int fd, const Vector<String16>& args); private: - static const nsecs_t kWaitDuration = 10000000; // 10 ms wp<Camera2Client> mClient; + int mLastFrameNumberOfFaces; - virtual bool threadLoop(); - - Mutex mInputMutex; - - struct RangeListener { - int32_t minId; - int32_t maxId; - wp<FilteredListener> listener; - }; - List<RangeListener> mRangeListeners; + void processNewFrames(const sp<Camera2Client> &client); - void processNewFrames(sp<Camera2Client> &client); + virtual bool processSingleFrame(CameraMetadata &frame, + const sp<CameraDeviceBase> &device); status_t processFaceDetect(const CameraMetadata &frame, - sp<Camera2Client> &client); - - status_t processListeners(const CameraMetadata &frame, - sp<Camera2Client> &client); - - CameraMetadata mLastFrame; - int mLastFrameNumberOfFaces; + const sp<Camera2Client> &client); // Emit FaceDetection event to java if faces changed void callbackFaceDetection(sp<Camera2Client> client, - camera_frame_metadata &metadata); + const camera_frame_metadata &metadata); }; diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp index 8d4933c..257a45f 100644 --- a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp +++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp @@ -22,14 +22,14 @@ #include <utils/Trace.h> #include "ProFrameProcessor.h" -#include "../Camera2Device.h" -#include "../ProCamera2Client.h" +#include "../CameraDeviceBase.h" namespace android { namespace camera2 { -ProFrameProcessor::ProFrameProcessor(wp<ProCamera2Client> client): - Thread(false), mClient(client) { +ProFrameProcessor::ProFrameProcessor(wp<CameraDeviceBase> device) : + Thread(/*canCallJava*/false), + mDevice(device) { } ProFrameProcessor::~ProFrameProcessor() { @@ -47,7 +47,8 @@ status_t ProFrameProcessor::registerListener(int32_t minId, } status_t ProFrameProcessor::removeListener(int32_t minId, - int32_t maxId, wp<FilteredListener> listener) { + int32_t maxId, + wp<FilteredListener> listener) { Mutex::Autolock l(mInputMutex); List<RangeListener>::iterator item = mRangeListeners.begin(); while (item != mRangeListeners.end()) { @@ -62,7 +63,7 @@ status_t ProFrameProcessor::removeListener(int32_t minId, return OK; } -void ProFrameProcessor::dump(int fd, const Vector<String16>& args) { +void ProFrameProcessor::dump(int fd, const Vector<String16>& /*args*/) { String8 result(" Latest received frame:\n"); write(fd, result.string(), result.size()); mLastFrame.dump(fd, 2, 6); @@ -71,44 +72,42 @@ void ProFrameProcessor::dump(int fd, const Vector<String16>& args) { bool ProFrameProcessor::threadLoop() { status_t res; - sp<Camera2Device> device; + sp<CameraDeviceBase> device; { - sp<ProCamera2Client> client = mClient.promote(); - if (client == 0) return false; - device = client->getCameraDevice(); + device = mDevice.promote(); if (device == 0) return false; } res = device->waitForNextFrame(kWaitDuration); if (res == OK) { - sp<ProCamera2Client> client = mClient.promote(); - if (client == 0) return false; - processNewFrames(client); + processNewFrames(device); } else if (res != TIMED_OUT) { - ALOGE("ProCamera2Client::ProFrameProcessor: Error waiting for new " + ALOGE("ProFrameProcessor: Error waiting for new " "frames: %s (%d)", strerror(-res), res); } return true; } -void ProFrameProcessor::processNewFrames(sp<ProCamera2Client> &client) { +void ProFrameProcessor::processNewFrames(const sp<CameraDeviceBase> &device) { status_t res; ATRACE_CALL(); CameraMetadata frame; - while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) { + while ( (res = device->getNextFrame(&frame)) == OK) { + camera_metadata_entry_t entry; entry = frame.find(ANDROID_REQUEST_FRAME_COUNT); if (entry.count == 0) { ALOGE("%s: Camera %d: Error reading frame number", - __FUNCTION__, client->getCameraId()); + __FUNCTION__, device->getId()); break; } ATRACE_INT("cam2_frame", entry.data.i32[0]); - res = processListeners(frame, client); - if (res != OK) break; + if (!processSingleFrame(frame, device)) { + break; + } if (!frame.isEmpty()) { mLastFrame.acquire(frame); @@ -116,23 +115,27 @@ void ProFrameProcessor::processNewFrames(sp<ProCamera2Client> &client) { } if (res != NOT_ENOUGH_DATA) { ALOGE("%s: Camera %d: Error getting next frame: %s (%d)", - __FUNCTION__, client->getCameraId(), strerror(-res), res); + __FUNCTION__, device->getId(), strerror(-res), res); return; } return; } +bool ProFrameProcessor::processSingleFrame(CameraMetadata &frame, + const sp<CameraDeviceBase> &device) { + return processListeners(frame, device) == OK; +} + status_t ProFrameProcessor::processListeners(const CameraMetadata &frame, - sp<ProCamera2Client> &client) { - status_t res; + const sp<CameraDeviceBase> &device) { ATRACE_CALL(); camera_metadata_ro_entry_t entry; entry = frame.find(ANDROID_REQUEST_ID); if (entry.count == 0) { ALOGE("%s: Camera %d: Error reading frame id", - __FUNCTION__, client->getCameraId()); + __FUNCTION__, device->getId()); return BAD_VALUE; } int32_t frameId = entry.data.i32[0]; diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.h b/services/camera/libcameraservice/camera2/ProFrameProcessor.h index e4094a6..b82942c 100644 --- a/services/camera/libcameraservice/camera2/ProFrameProcessor.h +++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.h @@ -24,11 +24,9 @@ #include <utils/List.h> #include <camera/CameraMetadata.h> -struct camera_frame_metadata; - namespace android { -class ProCamera2Client; +class CameraDeviceBase; namespace camera2 { @@ -37,23 +35,25 @@ namespace camera2 { */ class ProFrameProcessor: public Thread { public: - ProFrameProcessor(wp<ProCamera2Client> client); - ~ProFrameProcessor(); + ProFrameProcessor(wp<CameraDeviceBase> device); + virtual ~ProFrameProcessor(); struct FilteredListener: virtual public RefBase { virtual void onFrameAvailable(int32_t frameId, - const CameraMetadata &frame) = 0; + const CameraMetadata &frame) = 0; }; // Register a listener for a range of IDs [minId, maxId). Multiple listeners // can be listening to the same range - status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); - status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); + status_t registerListener(int32_t minId, int32_t maxId, + wp<FilteredListener> listener); + status_t removeListener(int32_t minId, int32_t maxId, + wp<FilteredListener> listener); void dump(int fd, const Vector<String16>& args); - private: + protected: static const nsecs_t kWaitDuration = 10000000; // 10 ms - wp<ProCamera2Client> mClient; + wp<CameraDeviceBase> mDevice; virtual bool threadLoop(); @@ -66,10 +66,13 @@ class ProFrameProcessor: public Thread { }; List<RangeListener> mRangeListeners; - void processNewFrames(sp<ProCamera2Client> &client); + void processNewFrames(const sp<CameraDeviceBase> &device); + + virtual bool processSingleFrame(CameraMetadata &frame, + const sp<CameraDeviceBase> &device); status_t processListeners(const CameraMetadata &frame, - sp<ProCamera2Client> &client); + const sp<CameraDeviceBase> &device); CameraMetadata mLastFrame; }; diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp index 6a4b95d..fbc5b93 100644 --- a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp @@ -556,9 +556,9 @@ void StreamingProcessor::onFrameAvailable() { } // Call outside locked parameters to allow re-entrancy from notification - Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); - if (l.mCameraClient != 0) { - l.mCameraClient->dataCallbackTimestamp(timestamp, + Camera2Client::SharedCameraCallbacks::Lock l(client->mSharedCameraCallbacks); + if (l.mRemoteCallback != 0) { + l.mRemoteCallback->dataCallbackTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, recordingHeap->mBuffers[heapIdx]); } diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk deleted file mode 100644 index 41b6f63..0000000 --- a/services/camera/tests/CameraServiceTest/Android.mk +++ /dev/null @@ -1,26 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= CameraServiceTest.cpp - -LOCAL_MODULE:= CameraServiceTest - -LOCAL_MODULE_TAGS := tests - -LOCAL_C_INCLUDES += \ - frameworks/av/libs - -LOCAL_CFLAGS := - -LOCAL_SHARED_LIBRARIES += \ - libbinder \ - libcutils \ - libutils \ - libui \ - libcamera_client \ - libgui - -# Disable it because the ISurface interface may change, and before we have a -# chance to fix this test, we don't want to break normal builds. -#include $(BUILD_EXECUTABLE) diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp deleted file mode 100644 index e417b79..0000000 --- a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp +++ /dev/null @@ -1,924 +0,0 @@ -/* - * Copyright (C) 2010 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 "CameraServiceTest" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <camera/Camera.h> -#include <camera/CameraParameters.h> -#include <ui/GraphicBuffer.h> -#include <camera/ICamera.h> -#include <camera/ICameraClient.h> -#include <camera/ICameraService.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <utils/KeyedVector.h> -#include <utils/Log.h> -#include <utils/Vector.h> -#include <utils/threads.h> - -using namespace android; - -// -// Assertion and Logging utilities -// -#define INFO(...) \ - do { \ - printf(__VA_ARGS__); \ - printf("\n"); \ - ALOGD(__VA_ARGS__); \ - } while(0) - -void assert_fail(const char *file, int line, const char *func, const char *expr) { - INFO("assertion failed at file %s, line %d, function %s:", - file, line, func); - INFO("%s", expr); - abort(); -} - -void assert_eq_fail(const char *file, int line, const char *func, - const char *expr, int actual) { - INFO("assertion failed at file %s, line %d, function %s:", - file, line, func); - INFO("(expected) %s != (actual) %d", expr, actual); - abort(); -} - -#define ASSERT(e) \ - do { \ - if (!(e)) \ - assert_fail(__FILE__, __LINE__, __func__, #e); \ - } while(0) - -#define ASSERT_EQ(expected, actual) \ - do { \ - int _x = (actual); \ - if (_x != (expected)) \ - assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \ - } while(0) - -// -// Holder service for pass objects between processes. -// -class IHolder : public IInterface { -protected: - enum { - HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION, - HOLDER_GET, - HOLDER_CLEAR - }; -public: - DECLARE_META_INTERFACE(Holder); - - virtual void put(sp<IBinder> obj) = 0; - virtual sp<IBinder> get() = 0; - virtual void clear() = 0; -}; - -class BnHolder : public BnInterface<IHolder> { - virtual status_t onTransact(uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -class BpHolder : public BpInterface<IHolder> { -public: - BpHolder(const sp<IBinder>& impl) - : BpInterface<IHolder>(impl) { - } - - virtual void put(sp<IBinder> obj) { - Parcel data, reply; - data.writeStrongBinder(obj); - remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual sp<IBinder> get() { - Parcel data, reply; - remote()->transact(HOLDER_GET, data, &reply); - return reply.readStrongBinder(); - } - - virtual void clear() { - Parcel data, reply; - remote()->transact(HOLDER_CLEAR, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder"); - -status_t BnHolder::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch(code) { - case HOLDER_PUT: { - put(data.readStrongBinder()); - return NO_ERROR; - } break; - case HOLDER_GET: { - reply->writeStrongBinder(get()); - return NO_ERROR; - } break; - case HOLDER_CLEAR: { - clear(); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -class HolderService : public BnHolder { - virtual void put(sp<IBinder> obj) { - mObj = obj; - } - virtual sp<IBinder> get() { - return mObj; - } - virtual void clear() { - mObj.clear(); - } -private: - sp<IBinder> mObj; -}; - -// -// A mock CameraClient -// -class MCameraClient : public BnCameraClient { -public: - virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); - virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); - virtual void dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, const sp<IMemory>& data); - - // new functions - void clearStat(); - enum OP { EQ, GE, LE, GT, LT }; - void assertNotify(int32_t msgType, OP op, int count); - void assertData(int32_t msgType, OP op, int count); - void waitNotify(int32_t msgType, OP op, int count); - void waitData(int32_t msgType, OP op, int count); - void assertDataSize(int32_t msgType, OP op, int dataSize); - - void setReleaser(ICamera *releaser) { - mReleaser = releaser; - } -private: - Mutex mLock; - Condition mCond; - DefaultKeyedVector<int32_t, int> mNotifyCount; - DefaultKeyedVector<int32_t, int> mDataCount; - DefaultKeyedVector<int32_t, int> mDataSize; - bool test(OP op, int v1, int v2); - void assertTest(OP op, int v1, int v2); - - ICamera *mReleaser; -}; - -void MCameraClient::clearStat() { - Mutex::Autolock _l(mLock); - mNotifyCount.clear(); - mDataCount.clear(); - mDataSize.clear(); -} - -bool MCameraClient::test(OP op, int v1, int v2) { - switch (op) { - case EQ: return v1 == v2; - case GT: return v1 > v2; - case LT: return v1 < v2; - case GE: return v1 >= v2; - case LE: return v1 <= v2; - default: ASSERT(0); break; - } - return false; -} - -void MCameraClient::assertTest(OP op, int v1, int v2) { - if (!test(op, v1, v2)) { - ALOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2); - ASSERT(0); - } -} - -void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { - Mutex::Autolock _l(mLock); - int v = mNotifyCount.valueFor(msgType); - assertTest(op, v, count); -} - -void MCameraClient::assertData(int32_t msgType, OP op, int count) { - Mutex::Autolock _l(mLock); - int v = mDataCount.valueFor(msgType); - assertTest(op, v, count); -} - -void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { - Mutex::Autolock _l(mLock); - int v = mDataSize.valueFor(msgType); - assertTest(op, v, dataSize); -} - -void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { - INFO("%s", __func__); - Mutex::Autolock _l(mLock); - ssize_t i = mNotifyCount.indexOfKey(msgType); - if (i < 0) { - mNotifyCount.add(msgType, 1); - } else { - ++mNotifyCount.editValueAt(i); - } - mCond.signal(); -} - -void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { - INFO("%s", __func__); - int dataSize = data->size(); - INFO("data type = %d, size = %d", msgType, dataSize); - Mutex::Autolock _l(mLock); - ssize_t i = mDataCount.indexOfKey(msgType); - if (i < 0) { - mDataCount.add(msgType, 1); - mDataSize.add(msgType, dataSize); - } else { - ++mDataCount.editValueAt(i); - mDataSize.editValueAt(i) = dataSize; - } - mCond.signal(); - - if (msgType == CAMERA_MSG_VIDEO_FRAME) { - ASSERT(mReleaser != NULL); - mReleaser->releaseRecordingFrame(data); - } -} - -void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& data) { - dataCallback(msgType, data); -} - -void MCameraClient::waitNotify(int32_t msgType, OP op, int count) { - INFO("waitNotify: %d, %d, %d", msgType, op, count); - Mutex::Autolock _l(mLock); - while (true) { - int v = mNotifyCount.valueFor(msgType); - if (test(op, v, count)) { - break; - } - mCond.wait(mLock); - } -} - -void MCameraClient::waitData(int32_t msgType, OP op, int count) { - INFO("waitData: %d, %d, %d", msgType, op, count); - Mutex::Autolock _l(mLock); - while (true) { - int v = mDataCount.valueFor(msgType); - if (test(op, v, count)) { - break; - } - mCond.wait(mLock); - } -} - -// -// A mock Surface -// -class MSurface : public BnSurface { -public: - virtual status_t registerBuffers(const BufferHeap& buffers); - virtual void postBuffer(ssize_t offset); - virtual void unregisterBuffers(); - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); - virtual status_t setBufferCount(int bufferCount); - - // new functions - void clearStat(); - void waitUntil(int c0, int c1, int c2); - -private: - // check callback count - Condition mCond; - Mutex mLock; - int registerBuffersCount; - int postBufferCount; - int unregisterBuffersCount; -}; - -status_t MSurface::registerBuffers(const BufferHeap& buffers) { - INFO("%s", __func__); - Mutex::Autolock _l(mLock); - ++registerBuffersCount; - mCond.signal(); - return NO_ERROR; -} - -void MSurface::postBuffer(ssize_t offset) { - // INFO("%s", __func__); - Mutex::Autolock _l(mLock); - ++postBufferCount; - mCond.signal(); -} - -void MSurface::unregisterBuffers() { - INFO("%s", __func__); - Mutex::Autolock _l(mLock); - ++unregisterBuffersCount; - mCond.signal(); -} - -sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { - INFO("%s", __func__); - return NULL; -} - -status_t MSurface::setBufferCount(int bufferCount) { - INFO("%s", __func__); - return NULL; -} - -void MSurface::clearStat() { - Mutex::Autolock _l(mLock); - registerBuffersCount = 0; - postBufferCount = 0; - unregisterBuffersCount = 0; -} - -void MSurface::waitUntil(int c0, int c1, int c2) { - INFO("waitUntil: %d %d %d", c0, c1, c2); - Mutex::Autolock _l(mLock); - while (true) { - if (registerBuffersCount >= c0 && - postBufferCount >= c1 && - unregisterBuffersCount >= c2) { - break; - } - mCond.wait(mLock); - } -} - -// -// Utilities to use the Holder service -// -sp<IHolder> getHolder() { - sp<IServiceManager> sm = defaultServiceManager(); - ASSERT(sm != 0); - sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder")); - ASSERT(binder != 0); - sp<IHolder> holder = interface_cast<IHolder>(binder); - ASSERT(holder != 0); - return holder; -} - -void putTempObject(sp<IBinder> obj) { - INFO("%s", __func__); - getHolder()->put(obj); -} - -sp<IBinder> getTempObject() { - INFO("%s", __func__); - return getHolder()->get(); -} - -void clearTempObject() { - INFO("%s", __func__); - getHolder()->clear(); -} - -// -// Get a Camera Service -// -sp<ICameraService> getCameraService() { - sp<IServiceManager> sm = defaultServiceManager(); - ASSERT(sm != 0); - sp<IBinder> binder = sm->getService(String16("media.camera")); - ASSERT(binder != 0); - sp<ICameraService> cs = interface_cast<ICameraService>(binder); - ASSERT(cs != 0); - return cs; -} - -int getNumberOfCameras() { - sp<ICameraService> cs = getCameraService(); - return cs->getNumberOfCameras(); -} - -// -// Various Connect Tests -// -void testConnect(int cameraId) { - INFO("%s", __func__); - sp<ICameraService> cs = getCameraService(); - sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc, cameraId); - ASSERT(c != 0); - c->disconnect(); -} - -void testAllowConnectOnceOnly(int cameraId) { - INFO("%s", __func__); - sp<ICameraService> cs = getCameraService(); - // Connect the first client. - sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc, cameraId); - ASSERT(c != 0); - // Same client -- ok. - ASSERT(cs->connect(cc, cameraId) != 0); - // Different client -- not ok. - sp<MCameraClient> cc2 = new MCameraClient(); - ASSERT(cs->connect(cc2, cameraId) == 0); - c->disconnect(); -} - -void testReconnectFailed() { - INFO("%s", __func__); - sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - sp<MCameraClient> cc = new MCameraClient(); - ASSERT(c->connect(cc) != NO_ERROR); -} - -void testReconnectSuccess() { - INFO("%s", __func__); - sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - sp<MCameraClient> cc = new MCameraClient(); - ASSERT(c->connect(cc) == NO_ERROR); - c->disconnect(); -} - -void testLockFailed() { - INFO("%s", __func__); - sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - ASSERT(c->lock() != NO_ERROR); -} - -void testLockUnlockSuccess() { - INFO("%s", __func__); - sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - ASSERT(c->lock() == NO_ERROR); - ASSERT(c->unlock() == NO_ERROR); -} - -void testLockSuccess() { - INFO("%s", __func__); - sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - ASSERT(c->lock() == NO_ERROR); - c->disconnect(); -} - -// -// Run the connect tests in another process. -// -const char *gExecutable; - -struct FunctionTableEntry { - const char *name; - void (*func)(); -}; - -FunctionTableEntry function_table[] = { -#define ENTRY(x) {#x, &x} - ENTRY(testReconnectFailed), - ENTRY(testReconnectSuccess), - ENTRY(testLockUnlockSuccess), - ENTRY(testLockFailed), - ENTRY(testLockSuccess), -#undef ENTRY -}; - -void runFunction(const char *tag) { - INFO("runFunction: %s", tag); - int entries = sizeof(function_table) / sizeof(function_table[0]); - for (int i = 0; i < entries; i++) { - if (strcmp(function_table[i].name, tag) == 0) { - (*function_table[i].func)(); - return; - } - } - ASSERT(0); -} - -void runInAnotherProcess(const char *tag) { - pid_t pid = fork(); - if (pid == 0) { - execlp(gExecutable, gExecutable, tag, NULL); - ASSERT(0); - } else { - int status; - ASSERT_EQ(pid, wait(&status)); - ASSERT_EQ(0, status); - } -} - -void testReconnect(int cameraId) { - INFO("%s", __func__); - sp<ICameraService> cs = getCameraService(); - sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc, cameraId); - ASSERT(c != 0); - // Reconnect to the same client -- ok. - ASSERT(c->connect(cc) == NO_ERROR); - // Reconnect to a different client (but the same pid) -- ok. - sp<MCameraClient> cc2 = new MCameraClient(); - ASSERT(c->connect(cc2) == NO_ERROR); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); -} - -void testLockUnlock(int cameraId) { - sp<ICameraService> cs = getCameraService(); - sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc, cameraId); - ASSERT(c != 0); - // We can lock as many times as we want. - ASSERT(c->lock() == NO_ERROR); - ASSERT(c->lock() == NO_ERROR); - // Lock from a different process -- not ok. - putTempObject(c->asBinder()); - runInAnotherProcess("testLockFailed"); - // Unlock then lock from a different process -- ok. - ASSERT(c->unlock() == NO_ERROR); - runInAnotherProcess("testLockUnlockSuccess"); - // Unlock then lock from a different process -- ok. - runInAnotherProcess("testLockSuccess"); - clearTempObject(); -} - -void testReconnectFromAnotherProcess(int cameraId) { - INFO("%s", __func__); - - sp<ICameraService> cs = getCameraService(); - sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc, cameraId); - ASSERT(c != 0); - // Reconnect from a different process -- not ok. - putTempObject(c->asBinder()); - runInAnotherProcess("testReconnectFailed"); - // Unlock then reconnect from a different process -- ok. - ASSERT(c->unlock() == NO_ERROR); - runInAnotherProcess("testReconnectSuccess"); - clearTempObject(); -} - -// We need to flush the command buffer after the reference -// to ICamera is gone. The sleep is for the server to run -// the destructor for it. -static void flushCommands() { - IPCThreadState::self()->flushCommands(); - usleep(200000); // 200ms -} - -// Run a test case -#define RUN(class_name, cameraId) do { \ - { \ - INFO(#class_name); \ - class_name instance; \ - instance.init(cameraId); \ - instance.run(); \ - } \ - flushCommands(); \ -} while(0) - -// Base test case after the the camera is connected. -class AfterConnect { -public: - void init(int cameraId) { - cs = getCameraService(); - cc = new MCameraClient(); - c = cs->connect(cc, cameraId); - ASSERT(c != 0); - } - -protected: - sp<ICameraService> cs; - sp<MCameraClient> cc; - sp<ICamera> c; - - ~AfterConnect() { - c->disconnect(); - c.clear(); - cc.clear(); - cs.clear(); - } -}; - -class TestSetPreviewDisplay : public AfterConnect { -public: - void run() { - sp<MSurface> surface = new MSurface(); - ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestStartPreview : public AfterConnect { -public: - void run() { - sp<MSurface> surface = new MSurface(); - ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); - - ASSERT(c->startPreview() == NO_ERROR); - ASSERT(c->previewEnabled() == true); - - surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer - surface->clearStat(); - - sp<MSurface> another_surface = new MSurface(); - c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers - // is called. - surface->waitUntil(0, 0, 1); // needs unregisterBuffers - - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestStartPreviewWithoutDisplay : public AfterConnect { -public: - void run() { - ASSERT(c->startPreview() == NO_ERROR); - ASSERT(c->previewEnabled() == true); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -// Base test case after the the camera is connected and the preview is started. -class AfterStartPreview : public AfterConnect { -public: - void init(int cameraId) { - AfterConnect::init(cameraId); - surface = new MSurface(); - ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); - ASSERT(c->startPreview() == NO_ERROR); - } - -protected: - sp<MSurface> surface; - - ~AfterStartPreview() { - surface.clear(); - } -}; - -class TestAutoFocus : public AfterStartPreview { -public: - void run() { - cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0); - c->autoFocus(); - cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestStopPreview : public AfterStartPreview { -public: - void run() { - ASSERT(c->previewEnabled() == true); - c->stopPreview(); - ASSERT(c->previewEnabled() == false); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestTakePicture: public AfterStartPreview { -public: - void run() { - ASSERT(c->takePicture() == NO_ERROR); - cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); - cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); - cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); - c->stopPreview(); - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestTakeMultiplePictures: public AfterStartPreview { -public: - void run() { - for (int i = 0; i < 10; i++) { - cc->clearStat(); - ASSERT(c->takePicture() == NO_ERROR); - cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); - cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); - cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); - } - c->disconnect(); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } -}; - -class TestGetParameters: public AfterStartPreview { -public: - void run() { - String8 param_str = c->getParameters(); - INFO("%s", static_cast<const char*>(param_str)); - } -}; - -static bool getNextSize(const char **ptrS, int *w, int *h) { - const char *s = *ptrS; - - // skip over ',' - if (*s == ',') s++; - - // remember start position in p - const char *p = s; - while (*s != '\0' && *s != 'x') { - s++; - } - if (*s == '\0') return false; - - // get the width - *w = atoi(p); - - // skip over 'x' - ASSERT(*s == 'x'); - p = s + 1; - while (*s != '\0' && *s != ',') { - s++; - } - - // get the height - *h = atoi(p); - *ptrS = s; - return true; -} - -class TestPictureSize : public AfterStartPreview { -public: - void checkOnePicture(int w, int h) { - const float rate = 0.9; // byte per pixel limit - int pixels = w * h; - - CameraParameters param(c->getParameters()); - param.setPictureSize(w, h); - // disable thumbnail to get more accurate size. - param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0); - param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0); - c->setParameters(param.flatten()); - - cc->clearStat(); - ASSERT(c->takePicture() == NO_ERROR); - cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); - //cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); - cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); - cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT, - int(pixels * rate)); - cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0); - cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - } - - void run() { - CameraParameters param(c->getParameters()); - int w, h; - const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); - while (getNextSize(&s, &w, &h)) { - ALOGD("checking picture size %dx%d", w, h); - checkOnePicture(w, h); - } - } -}; - -class TestPreviewCallbackFlag : public AfterConnect { -public: - void run() { - sp<MSurface> surface = new MSurface(); - ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); - - // Try all flag combinations. - for (int v = 0; v < 8; v++) { - ALOGD("TestPreviewCallbackFlag: flag=%d", v); - usleep(100000); // sleep a while to clear the in-flight callbacks. - cc->clearStat(); - c->setPreviewCallbackFlag(v); - ASSERT(c->previewEnabled() == false); - ASSERT(c->startPreview() == NO_ERROR); - ASSERT(c->previewEnabled() == true); - sleep(2); - c->stopPreview(); - if ((v & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) { - cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0); - } else { - if ((v & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) { - cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10); - } else { - cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1); - } - } - } - } -}; - -class TestRecording : public AfterConnect { -public: - void run() { - ASSERT(c->recordingEnabled() == false); - sp<MSurface> surface = new MSurface(); - ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); - c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); - cc->setReleaser(c.get()); - c->startRecording(); - ASSERT(c->recordingEnabled() == true); - sleep(2); - c->stopRecording(); - usleep(100000); // sleep a while to clear the in-flight callbacks. - cc->setReleaser(NULL); - cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10); - } -}; - -class TestPreviewSize : public AfterStartPreview { -public: - void checkOnePicture(int w, int h) { - int size = w*h*3/2; // should read from parameters - - c->stopPreview(); - - CameraParameters param(c->getParameters()); - param.setPreviewSize(w, h); - c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); - c->setParameters(param.flatten()); - - c->startPreview(); - - cc->clearStat(); - cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1); - cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size); - } - - void run() { - CameraParameters param(c->getParameters()); - int w, h; - const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); - while (getNextSize(&s, &w, &h)) { - ALOGD("checking preview size %dx%d", w, h); - checkOnePicture(w, h); - } - } -}; - -void runHolderService() { - defaultServiceManager()->addService( - String16("CameraServiceTest.Holder"), new HolderService()); - ProcessState::self()->startThreadPool(); -} - -int main(int argc, char **argv) -{ - if (argc != 1) { - runFunction(argv[1]); - return 0; - } - INFO("CameraServiceTest start"); - gExecutable = argv[0]; - runHolderService(); - int n = getNumberOfCameras(); - INFO("%d Cameras available", n); - - for (int id = 0; id < n; id++) { - INFO("Testing camera %d", id); - testConnect(id); flushCommands(); - testAllowConnectOnceOnly(id); flushCommands(); - testReconnect(id); flushCommands(); - testLockUnlock(id); flushCommands(); - testReconnectFromAnotherProcess(id); flushCommands(); - - RUN(TestSetPreviewDisplay, id); - RUN(TestStartPreview, id); - RUN(TestStartPreviewWithoutDisplay, id); - RUN(TestAutoFocus, id); - RUN(TestStopPreview, id); - RUN(TestTakePicture, id); - RUN(TestTakeMultiplePictures, id); - RUN(TestGetParameters, id); - RUN(TestPictureSize, id); - RUN(TestPreviewCallbackFlag, id); - RUN(TestRecording, id); - RUN(TestPreviewSize, id); - } - - INFO("CameraServiceTest finished"); -} diff --git a/services/camera/tests/CameraServiceTest/MODULE_LICENSE_APACHE2 b/services/camera/tests/CameraServiceTest/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/services/camera/tests/CameraServiceTest/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/services/camera/tests/CameraServiceTest/NOTICE b/services/camera/tests/CameraServiceTest/NOTICE deleted file mode 100644 index c5b1efa..0000000 --- a/services/camera/tests/CameraServiceTest/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - |