summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/Camera.cpp41
-rw-r--r--camera/CameraBase.cpp35
-rw-r--r--camera/ICamera.cpp19
-rw-r--r--camera/IProCameraCallbacks.cpp60
-rw-r--r--camera/IProCameraUser.cpp26
-rw-r--r--camera/ProCamera.cpp22
-rw-r--r--camera/tests/ProCameraTests.cpp14
-rw-r--r--include/camera/Camera.h3
-rw-r--r--include/camera/CameraBase.h8
-rw-r--r--include/camera/ICamera.h3
-rw-r--r--include/camera/IProCameraCallbacks.h18
-rw-r--r--include/camera/IProCameraUser.h3
-rw-r--r--include/camera/ProCamera.h33
-rw-r--r--include/media/IMediaRecorder.h2
-rw-r--r--include/media/IStreamSource.h3
-rw-r--r--include/media/MediaRecorderBase.h2
-rw-r--r--include/media/mediarecorder.h2
-rw-r--r--include/media/stagefright/CameraSource.h6
-rw-r--r--include/media/stagefright/CameraSourceTimeLapse.h4
-rw-r--r--media/libmedia/IMediaRecorder.cpp6
-rw-r--r--media/libmedia/mediarecorder.cpp2
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp2
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h2
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp2
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h4
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp9
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp1
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp22
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h7
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h4
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.h2
-rw-r--r--media/libstagefright/CameraSource.cpp6
-rw-r--r--media/libstagefright/CameraSourceTimeLapse.cpp4
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp31
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp13
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.h4
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.cpp128
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.h6
-rw-r--r--media/libstagefright/omx/OMXNodeInstance.cpp3
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.cpp29
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.h1
-rw-r--r--media/libstagefright/wifi-display/Android.mk1
-rw-r--r--media/libstagefright/wifi-display/MediaReceiver.cpp14
-rw-r--r--media/libstagefright/wifi-display/MediaReceiver.h2
-rw-r--r--media/libstagefright/wifi-display/MediaSender.cpp16
-rw-r--r--media/libstagefright/wifi-display/MediaSender.h2
-rw-r--r--media/libstagefright/wifi-display/TimeSyncer.cpp332
-rw-r--r--media/libstagefright/wifi-display/TimeSyncer.h109
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPAssembler.cpp5
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPReceiver.cpp44
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPReceiver.h2
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.cpp21
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.h2
-rw-r--r--media/libstagefright/wifi-display/sink/DirectRenderer.cpp598
-rw-r--r--media/libstagefright/wifi-display/sink/DirectRenderer.h48
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.cpp40
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.h6
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp110
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.h19
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp18
-rw-r--r--media/libstagefright/wifi-display/source/Converter.h5
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp10
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp17
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h3
-rw-r--r--media/libstagefright/wifi-display/udptest.cpp283
-rw-r--r--media/libstagefright/wifi-display/wfd.cpp5
-rw-r--r--services/audioflinger/FastMixer.h2
-rw-r--r--services/camera/libcameraservice/Android.mk1
-rw-r--r--services/camera/libcameraservice/Camera2Client.cpp172
-rw-r--r--services/camera/libcameraservice/Camera2Client.h45
-rw-r--r--services/camera/libcameraservice/Camera2ClientBase.cpp329
-rw-r--r--services/camera/libcameraservice/Camera2ClientBase.h128
-rw-r--r--services/camera/libcameraservice/Camera2Device.cpp4
-rw-r--r--services/camera/libcameraservice/Camera2Device.h1
-rw-r--r--services/camera/libcameraservice/Camera3Device.cpp4
-rw-r--r--services/camera/libcameraservice/Camera3Device.h1
-rw-r--r--services/camera/libcameraservice/CameraClient.cpp27
-rw-r--r--services/camera/libcameraservice/CameraDeviceBase.h5
-rw-r--r--services/camera/libcameraservice/CameraService.cpp339
-rw-r--r--services/camera/libcameraservice/CameraService.h59
-rw-r--r--services/camera/libcameraservice/ProCamera2Client.cpp233
-rw-r--r--services/camera/libcameraservice/ProCamera2Client.h87
-rw-r--r--services/camera/libcameraservice/camera2/CallbackProcessor.cpp7
-rw-r--r--services/camera/libcameraservice/camera2/CaptureSequencer.cpp20
-rw-r--r--services/camera/libcameraservice/camera2/FrameProcessor.cpp185
-rw-r--r--services/camera/libcameraservice/camera2/FrameProcessor.h45
-rw-r--r--services/camera/libcameraservice/camera2/ProFrameProcessor.cpp49
-rw-r--r--services/camera/libcameraservice/camera2/ProFrameProcessor.h27
-rw-r--r--services/camera/libcameraservice/camera2/StreamingProcessor.cpp6
-rw-r--r--services/camera/tests/CameraServiceTest/Android.mk26
-rw-r--r--services/camera/tests/CameraServiceTest/CameraServiceTest.cpp924
-rw-r--r--services/camera/tests/CameraServiceTest/MODULE_LICENSE_APACHE20
-rw-r--r--services/camera/tests/CameraServiceTest/NOTICE190
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> &notify)
+ const sp<AMessage> &notify,
+ 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> &notify);
+ const sp<AMessage> &notify,
+ 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> &notify)
+ : 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> &notify);
+
+ 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> &notify);
-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> &notify)
+ : 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> &notify)
: 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> &notify = 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 &params, 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 &params, 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 &params, 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 &params,
}
{
- 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
-