| /* |
| * Copyright (C) 2009 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. |
| */ |
| |
| #include <sys/time.h> |
| |
| #include <OMX_Component.h> |
| |
| #include <binder/IServiceManager.h> |
| #include <cutils/properties.h> // for property_get |
| #include <media/stagefright/CameraSource.h> |
| #include <media/stagefright/MediaDebug.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/MetaData.h> |
| #include <camera/Camera.h> |
| #include <camera/CameraParameters.h> |
| #include <ui/GraphicBuffer.h> |
| #include <ui/Overlay.h> |
| #include <surfaceflinger/ISurface.h> |
| #include <utils/String8.h> |
| |
| namespace android { |
| |
| static int64_t getNowUs() { |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| |
| return (int64_t)tv.tv_usec + tv.tv_sec * 1000000; |
| } |
| |
| struct DummySurface : public BnSurface { |
| DummySurface() {} |
| |
| virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) { |
| return NULL; |
| } |
| |
| virtual status_t registerBuffers(const BufferHeap &buffers) { |
| return OK; |
| } |
| |
| virtual void postBuffer(ssize_t offset) {} |
| virtual void unregisterBuffers() {} |
| |
| virtual sp<OverlayRef> createOverlay( |
| uint32_t w, uint32_t h, int32_t format, int32_t orientation) { |
| return NULL; |
| } |
| |
| protected: |
| virtual ~DummySurface() {} |
| |
| DummySurface(const DummySurface &); |
| DummySurface &operator=(const DummySurface &); |
| }; |
| |
| struct CameraSourceListener : public CameraListener { |
| CameraSourceListener(const sp<CameraSource> &source); |
| |
| virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); |
| virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr); |
| |
| virtual void postDataTimestamp( |
| nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); |
| |
| protected: |
| virtual ~CameraSourceListener(); |
| |
| private: |
| wp<CameraSource> mSource; |
| |
| CameraSourceListener(const CameraSourceListener &); |
| CameraSourceListener &operator=(const CameraSourceListener &); |
| }; |
| |
| CameraSourceListener::CameraSourceListener(const sp<CameraSource> &source) |
| : mSource(source) { |
| } |
| |
| CameraSourceListener::~CameraSourceListener() { |
| } |
| |
| void CameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) { |
| LOGV("notify(%d, %d, %d)", msgType, ext1, ext2); |
| } |
| |
| void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) { |
| LOGV("postData(%d, ptr:%p, size:%d)", |
| msgType, dataPtr->pointer(), dataPtr->size()); |
| |
| sp<CameraSource> source = mSource.promote(); |
| if (source.get() != NULL) { |
| source->dataCallback(msgType, dataPtr); |
| } |
| } |
| |
| void CameraSourceListener::postDataTimestamp( |
| nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) { |
| LOGV("postDataTimestamp(%lld, %d, ptr:%p, size:%d)", |
| timestamp, msgType, dataPtr->pointer(), dataPtr->size()); |
| } |
| |
| // static |
| CameraSource *CameraSource::Create() { |
| sp<Camera> camera = Camera::connect(); |
| |
| if (camera.get() == NULL) { |
| return NULL; |
| } |
| |
| return new CameraSource(camera); |
| } |
| |
| // static |
| CameraSource *CameraSource::CreateFromICamera(const sp<ICamera> &icamera) { |
| sp<Camera> camera = Camera::create(icamera); |
| |
| if (camera.get() == NULL) { |
| return NULL; |
| } |
| |
| return new CameraSource(camera); |
| } |
| |
| CameraSource::CameraSource(const sp<Camera> &camera) |
| : mCamera(camera), |
| mWidth(0), |
| mHeight(0), |
| mFirstFrameTimeUs(0), |
| mNumFrames(0), |
| mStarted(false) { |
| char value[PROPERTY_VALUE_MAX]; |
| if (property_get("ro.hardware", value, NULL) && !strcmp(value, "sholes")) { |
| // The hardware encoder(s) do not support yuv420, but only YCbYCr, |
| // fortunately the camera also supports this, so we needn't transcode. |
| mCamera->setParameters(String8("preview-format=yuv422i-yuyv")); |
| } |
| |
| String8 s = mCamera->getParameters(); |
| printf("params: \"%s\"\n", s.string()); |
| |
| CameraParameters params(s); |
| params.getPreviewSize(&mWidth, &mHeight); |
| } |
| |
| CameraSource::~CameraSource() { |
| if (mStarted) { |
| stop(); |
| } |
| } |
| |
| void CameraSource::setPreviewSurface(const sp<ISurface> &surface) { |
| mPreviewSurface = surface; |
| } |
| |
| status_t CameraSource::start(MetaData *) { |
| CHECK(!mStarted); |
| |
| mCamera->setListener(new CameraSourceListener(this)); |
| |
| status_t err = |
| mCamera->setPreviewDisplay( |
| mPreviewSurface != NULL ? mPreviewSurface : new DummySurface); |
| CHECK_EQ(err, OK); |
| |
| mCamera->setPreviewCallbackFlags( |
| FRAME_CALLBACK_FLAG_ENABLE_MASK |
| | FRAME_CALLBACK_FLAG_COPY_OUT_MASK); |
| |
| err = mCamera->startPreview(); |
| CHECK_EQ(err, OK); |
| |
| mStarted = true; |
| |
| return OK; |
| } |
| |
| status_t CameraSource::stop() { |
| CHECK(mStarted); |
| |
| mCamera->stopPreview(); |
| |
| mStarted = false; |
| |
| return OK; |
| } |
| |
| sp<MetaData> CameraSource::getFormat() { |
| sp<MetaData> meta = new MetaData; |
| meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); |
| meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar); |
| meta->setInt32(kKeyWidth, mWidth); |
| meta->setInt32(kKeyHeight, mHeight); |
| |
| return meta; |
| } |
| |
| status_t CameraSource::read( |
| MediaBuffer **buffer, const ReadOptions *options) { |
| CHECK(mStarted); |
| |
| *buffer = NULL; |
| |
| int64_t seekTimeUs; |
| if (options && options->getSeekTo(&seekTimeUs)) { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| sp<IMemory> frame; |
| int64_t frameTime; |
| |
| { |
| Mutex::Autolock autoLock(mLock); |
| while (mFrames.empty()) { |
| mFrameAvailableCondition.wait(mLock); |
| } |
| |
| frame = *mFrames.begin(); |
| mFrames.erase(mFrames.begin()); |
| |
| frameTime = *mFrameTimes.begin(); |
| mFrameTimes.erase(mFrameTimes.begin()); |
| } |
| |
| *buffer = new MediaBuffer(frame->size()); |
| memcpy((*buffer)->data(), frame->pointer(), frame->size()); |
| (*buffer)->set_range(0, frame->size()); |
| |
| (*buffer)->meta_data()->clear(); |
| (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); |
| |
| return OK; |
| } |
| |
| void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) { |
| Mutex::Autolock autoLock(mLock); |
| |
| int64_t nowUs = getNowUs(); |
| if (mNumFrames == 0) { |
| mFirstFrameTimeUs = nowUs; |
| } |
| ++mNumFrames; |
| |
| mFrames.push_back(data); |
| mFrameTimes.push_back(nowUs - mFirstFrameTimeUs); |
| mFrameAvailableCondition.signal(); |
| } |
| |
| } // namespace android |