blob: 50301e6e0c05a8feec9453c04b0b3693ced4e0c2 [file] [log] [blame]
/*
* Copyright (C) 2015-2020 Intel Corporation.
*
* 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 "CaptureUnit"
#include <poll.h>
#include "iutils/CameraLog.h"
#include "iutils/CameraDump.h"
#include "iutils/Utils.h"
#include "PlatformData.h"
#include "MediaControl.h"
#include "CaptureUnit.h"
using std::vector;
using std::map;
namespace icamera {
CaptureUnit::CaptureUnit(int cameraId, int memType) :
StreamSource(memType),
mCameraId(cameraId),
mDevice(nullptr),
mMaxBufferNum(PlatformData::getMaxRawDataNum(cameraId)),
mState(CAPTURE_UNINIT),
mExitPending(false)
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
mPollThread = new PollThread(this);
mMaxBuffersInDevice = PlatformData::getExposureLag(mCameraId) + 1;
if (mMaxBuffersInDevice < 2) {
mMaxBuffersInDevice = 2;
}
}
CaptureUnit::~CaptureUnit()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
delete mPollThread;
}
int CaptureUnit::init()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
mState = CAPTURE_INIT;
return OK;
}
void CaptureUnit::deinit()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
if (mState == CAPTURE_UNINIT) {
LOG1("%s: deinit without init", __func__);
return;
}
destroyDevices();
mPollThread->join();
mState = CAPTURE_UNINIT;
}
int CaptureUnit::createDevices()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
destroyDevices();
// Default INVALID_PORT means the device isn't associated with any outside consumers.
const Port kDefaultPort = INVALID_PORT;
Port portOfMainDevice = findDefaultPort(mOutputFrameInfo);
// Use the config for main port as the default one.
const stream_t& kDefaultStream = mOutputFrameInfo.at(portOfMainDevice);
// Use VIDEO_GENERIC by default.
VideoNodeType nodeType = VIDEO_GENERIC;
mDevice = new MainDevice(mCameraId, nodeType, this);
// Open and configure the device. The stream and port that are used by the device is
// decided by whether consumer has provided such info, use the default one if not.
int ret = mDevice->openDevice();
CheckError(ret != OK, ret, "Open device(%s) failed:%d", mDevice->getName(), ret);
bool hasPort = mOutputFrameInfo.find(portOfMainDevice) != mOutputFrameInfo.end();
const stream_t& stream = hasPort ? mOutputFrameInfo.at(portOfMainDevice) : kDefaultStream;
ret = mDevice->configure(hasPort ? portOfMainDevice : kDefaultPort, stream, mMaxBufferNum);
CheckError(ret != OK, ret, "Configure device(%s) failed:%d", mDevice->getName(), ret);
return OK;
}
void CaptureUnit::destroyDevices()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
if (mDevice) {
mDevice->closeDevice();
delete mDevice;
mDevice = nullptr;
}
}
/**
* Find the device that can handle the given port.
*/
DeviceBase* CaptureUnit::findDeviceByPort(Port port)
{
if (mDevice && mDevice->getPort() == port) {
return mDevice;
}
return nullptr;
}
int CaptureUnit::streamOn()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
if (mDevice) {
int ret = mDevice->streamOn();
CheckError(ret < 0, INVALID_OPERATION, "Device:%s stream on failed.", mDevice->getName());
}
return OK;
}
int CaptureUnit::start()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
AutoMutex l(mLock);
CheckWarning(mState == CAPTURE_START, OK, "@%s: device already started", __func__);
int ret = streamOn();
if (ret != OK) {
streamOff();
LOGE("Devices stream on failed:%d", ret);
return ret;
}
mPollThread->run("CaptureUnit", PRIORITY_URGENT_AUDIO);
mState = CAPTURE_START;
mExitPending = false;
LOG2("@%s: automation checkpoint: flag: poll_started", __func__);
return OK;
}
void CaptureUnit::streamOff()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
if (mDevice) {
mDevice->streamOff();
}
}
int CaptureUnit::stop()
{
PERF_CAMERA_ATRACE();
LOG1("%s, mCameraId:%d", __func__, mCameraId);
if (mState != CAPTURE_START) {
LOGW("@%s: device not started", __func__);
return OK;
}
mExitPending = true;
mPollThread->requestExit();
streamOff();
mPollThread->requestExitAndWait();
AutoMutex l(mLock);
mState = CAPTURE_STOP;
if (mDevice) {
mDevice->resetBuffers();
}
LOG2("@%s: automation checkpoint: flag: poll_stopped", __func__);
mExitPending = false; // It's already stopped.
return OK;
}
/**
* Check if the given outputFrames are different from the previous one.
* Only return false when the config for each port is exactly same.
*/
bool CaptureUnit::isNewConfiguration(const map<Port, stream_t>& outputFrames)
{
for (const auto& item : outputFrames) {
if (mOutputFrameInfo.find(item.first) == mOutputFrameInfo.end()) {
return true;
}
const stream_t& oldStream = mOutputFrameInfo[item.first];
const stream_t& newStream = item.second;
bool isNewConfig = (oldStream.width != newStream.width || oldStream.height != newStream.height
|| oldStream.format != newStream.format || oldStream.field != newStream.field
|| oldStream.memType != newStream.memType);
if (isNewConfig) {
return true;
}
}
return false;
}
int CaptureUnit::configure(const map<Port, stream_t>& outputFrames,
const vector<ConfigMode>& configModes)
{
PERF_CAMERA_ATRACE();
CheckError(outputFrames.empty(), BAD_VALUE, "No frame info configured.");
CheckError(mState != CAPTURE_CONFIGURE && mState != CAPTURE_INIT && mState != CAPTURE_STOP,
INVALID_OPERATION, "@%s: Configure in wrong state %d", __func__, mState);
Port port = findDefaultPort(outputFrames);
const stream_t& mainStream = outputFrames.at(port);
for (const auto& item : outputFrames) {
LOG1("%s, mCameraId:%d, port:%d, w:%d, h:%d, f:%s", __func__, mCameraId, item.first,
item.second.width, item.second.height,
CameraUtils::format2string(item.second.format).c_str());
}
mConfigModes = configModes;
mOutputFrameInfo = outputFrames;
/* media ctl setup */
MediaCtlConf *mc = PlatformData::getMediaCtlConf(mCameraId);
CheckError(!mc, BAD_VALUE, "get format configuration failed for %s (%dx%d)",
CameraUtils::format2string(mainStream.format).c_str(),
mainStream.width, mainStream.height);
int status = MediaControl::getInstance()->mediaCtlSetup(mCameraId, mc,
mainStream.width, mainStream.height, mainStream.field);
CheckError(status != OK, status, "set up mediaCtl failed");
// Create, open, and configure all of needed devices.
status = createDevices();
CheckError(status != OK, status, "Create devices failed:%d", status);
mState = CAPTURE_CONFIGURE;
// mExitPending should also be set false in configure to make buffers queued before start
mExitPending = false;
return OK;
}
Port CaptureUnit::findDefaultPort(const map<Port, stream_t>& frames) const
{
Port availablePorts[] = {MAIN_PORT, SECOND_PORT, THIRD_PORT, FORTH_PORT};
for (unsigned int i = 0; i < ARRAY_SIZE(availablePorts); i++) {
if (frames.find(availablePorts[i]) != frames.end()) {
return availablePorts[i];
}
}
return INVALID_PORT;
}
int CaptureUnit::allocateMemory(Port port, const std::shared_ptr<CameraBuffer> &camBuffer)
{
const struct v4l2_buffer* v = camBuffer->getV4L2Buffer().Get();
CheckError(v->index >= mMaxBufferNum, -1
,"index %d is larger than max count %d", v->index, mMaxBufferNum);
CheckError(v->memory != V4L2_MEMORY_MMAP, -1
,"Allocating Memory Capture device only supports MMAP mode.");
DeviceBase* device = findDeviceByPort(port);
CheckError(!device, BAD_VALUE, "No device available for port:%d", port);
int ret = camBuffer->allocateMemory(device->getV4l2Device());
CheckError(ret < 0, ret, "Failed to allocate memory ret(%d) for port:%d", ret, port);
return OK;
}
int CaptureUnit::qbuf(Port port, const std::shared_ptr<CameraBuffer> &camBuffer)
{
CheckError(camBuffer == nullptr, BAD_VALUE, "Camera buffer is null");
CheckError((mState == CAPTURE_INIT || mState == CAPTURE_UNINIT), INVALID_OPERATION,
"@%s: qbuf in wrong state %d", __func__, mState);
DeviceBase* device = findDeviceByPort(port);
CheckError(!device, BAD_VALUE, "No device available for port:%d", port);
LOG2("@%s, mCameraId:%d, queue CameraBuffer: %p to port:%d",
__func__, mCameraId, camBuffer.get(), port);
device->addPendingBuffer(camBuffer);
return processPendingBuffers();
}
int CaptureUnit::queueAllBuffers()
{
PERF_CAMERA_ATRACE();
if (mExitPending) return OK;
if (mDevice) {
int ret = mDevice->queueBuffer(-1);
if (mExitPending) return OK;
CheckError(ret != OK, ret, "queueBuffer fails, dev:%s, ret:%d", mDevice->getName(), ret);
mDevice->getPredictSequence();
}
return OK;
}
void CaptureUnit::onDequeueBuffer()
{
processPendingBuffers();
}
int CaptureUnit::processPendingBuffers()
{
if (mDevice && mDevice->getBufferNumInDevice() < mMaxBuffersInDevice) {
LOG2("%s: buffers in device:%d", __func__, mDevice->getBufferNumInDevice());
if (!mDevice->hasPendingBuffer()) {
return OK;
}
int ret = queueAllBuffers();
if (mExitPending) return OK;
CheckError(ret != OK, ret, "Failed to queue buffers, ret=%d", ret);
}
return OK;
}
int CaptureUnit::poll()
{
PERF_CAMERA_ATRACE();
int ret = 0;
const int poll_timeout_count = 10;
const int poll_timeout = gSlowlyRunRatio ? (gSlowlyRunRatio * 1000000) : 1000;
LOG2("@%s, mCameraId:%d", __func__, mCameraId);
CheckError((mState != CAPTURE_CONFIGURE && mState != CAPTURE_START), INVALID_OPERATION,
"@%s: poll buffer in wrong state %d", __func__, mState);
int timeOutCount = poll_timeout_count;
std::vector<V4L2Device*> pollDevs, readyDevices;
if (mDevice) {
pollDevs.push_back(mDevice->getV4l2Device());
LOG2("@%s: device:%s has %d buffers queued.", __func__,
mDevice->getName(), mDevice->getBufferNumInDevice());
}
while (timeOutCount-- && ret == 0) {
// If stream off, no poll needed.
if (mExitPending) {
LOG2("%s: mExitPending is true, exit", __func__);
//Exiting, no error
return -1;
}
V4L2DevicePoller poller {pollDevs, -1};
ret = poller.Poll(poll_timeout, POLLPRI | POLLIN | POLLOUT | POLLERR, &readyDevices);
LOG2("@%s: automation checkpoint: flag: poll_buffer, ret:%d", __func__, ret);
}
//In case poll error after stream off
if (mExitPending) {
LOG2("%s: mExitPending is true, exit", __func__);
//Exiting, no error
return -1;
}
CheckError(ret < 0, UNKNOWN_ERROR, "%s: Poll error, ret:%d", __func__, ret);
if (ret == 0) {
LOG1("%s, cameraId: %d: timeout happens, wait recovery", __func__, mCameraId);
return OK;
}
for (const auto& readyDevice : readyDevices) {
if (mDevice && mDevice->getV4l2Device() == readyDevice) {
int ret = mDevice->dequeueBuffer();
if (mExitPending) return -1;
if (ret != OK) {
LOGE("Device:%s grab frame failed:%d", mDevice->getName(), ret);
}
break;
}
}
return OK;
}
void CaptureUnit::addFrameAvailableListener(BufferConsumer *listener)
{
LOG1("%s camera id:%d", __func__, mCameraId);
AutoMutex l(mLock);
if (mDevice) {
mDevice->addFrameListener(listener);
}
}
void CaptureUnit::removeFrameAvailableListener(BufferConsumer *listener)
{
LOG1("%s camera id:%d", __func__, mCameraId);
AutoMutex l(mLock);
if (mDevice) {
mDevice->removeFrameListener(listener);
}
}
void CaptureUnit::removeAllFrameAvailableListener()
{
LOG1("%s camera id:%d", __func__, mCameraId);
AutoMutex l(mLock);
if (mDevice) {
mDevice->removeAllFrameListeners();
}
}
void CaptureUnit::registerListener(EventType eventType, EventListener* eventListener)
{
if (mDevice) {
mDevice->registerListener(eventType, eventListener);
}
}
void CaptureUnit::removeListener(EventType eventType, EventListener* eventListener)
{
if (mDevice) {
mDevice->removeListener(eventType, eventListener);
}
}
} // namespace icamera