mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Video Emulated Stream
Test: Build and Run on CVD Bug: 277861838 Change-Id: I5a1db063cae505a7e7d75b97bea7f14857da5a1e
This commit is contained in:
@@ -24,6 +24,9 @@ package {
|
||||
cc_defaults {
|
||||
name: "android.hardware.automotive.evs-aidl-default-service-default",
|
||||
defaults: ["EvsHalDefaults"],
|
||||
header_libs: [
|
||||
"libstagefright_headers",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.graphics.bufferqueue@1.0",
|
||||
"android.hardware.graphics.bufferqueue@2.0",
|
||||
@@ -35,6 +38,7 @@ cc_defaults {
|
||||
"libcamera_metadata",
|
||||
"libhardware_legacy",
|
||||
"libhidlbase",
|
||||
"libmediandk",
|
||||
"libnativewindow",
|
||||
"libtinyxml2",
|
||||
"libui",
|
||||
|
||||
@@ -26,20 +26,27 @@
|
||||
#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
|
||||
#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
|
||||
#include <aidl/android/hardware/automotive/evs/Stream.h>
|
||||
#include <media/NdkMediaExtractor.h>
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
class EvsVideoEmulatedCamera : public EvsCamera {
|
||||
private:
|
||||
using Base = EvsCamera;
|
||||
|
||||
public:
|
||||
EvsVideoEmulatedCamera(Sigil sigil, const char* deviceName,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
|
||||
|
||||
~EvsVideoEmulatedCamera() override;
|
||||
~EvsVideoEmulatedCamera() override = default;
|
||||
|
||||
// Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
|
||||
ndk::ScopedAStatus forcePrimaryClient(
|
||||
@@ -60,6 +67,9 @@ class EvsVideoEmulatedCamera : public EvsCamera {
|
||||
ndk::ScopedAStatus setPrimaryClient() override;
|
||||
ndk::ScopedAStatus unsetPrimaryClient() override;
|
||||
|
||||
// Methods from EvsCameraBase follow.
|
||||
void shutdown() override;
|
||||
|
||||
const evs::CameraDesc& getDesc() { return mDescription; }
|
||||
|
||||
static std::shared_ptr<EvsVideoEmulatedCamera> Create(const char* deviceName);
|
||||
@@ -81,8 +91,18 @@ class EvsVideoEmulatedCamera : public EvsCamera {
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
bool initialize();
|
||||
|
||||
void generateFrames();
|
||||
|
||||
void renderOneFrame();
|
||||
|
||||
void initializeParameters();
|
||||
|
||||
void onCodecInputAvailable(const int32_t index);
|
||||
|
||||
void onCodecOutputAvailable(const int32_t index, const AMediaCodecBufferInfo& info);
|
||||
|
||||
::android::status_t allocateOneFrame(buffer_handle_t* handle) override;
|
||||
|
||||
bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
|
||||
@@ -92,9 +112,42 @@ class EvsVideoEmulatedCamera : public EvsCamera {
|
||||
bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
|
||||
std::unique_lock<std::mutex>& lck) override;
|
||||
|
||||
bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
|
||||
std::unique_lock<std::mutex>& lck) override;
|
||||
|
||||
// The properties of this camera.
|
||||
CameraDesc mDescription = {};
|
||||
|
||||
std::thread mCaptureThread;
|
||||
|
||||
// The callback used to deliver each frame
|
||||
std::shared_ptr<evs::IEvsCameraStream> mStream;
|
||||
|
||||
std::string mVideoFileName;
|
||||
// Media decoder resources - Owned by mDecoderThead when thread is running.
|
||||
int mVideoFd = 0;
|
||||
|
||||
struct AMediaExtractorDeleter {
|
||||
void operator()(AMediaExtractor* extractor) const { AMediaExtractor_delete(extractor); }
|
||||
};
|
||||
struct AMediaCodecDeleter {
|
||||
void operator()(AMediaCodec* codec) const { AMediaCodec_delete(codec); }
|
||||
};
|
||||
|
||||
std::unique_ptr<AMediaExtractor, AMediaExtractorDeleter> mVideoExtractor;
|
||||
std::unique_ptr<AMediaCodec, AMediaCodecDeleter> mVideoCodec;
|
||||
|
||||
// Horizontal pixel count in the buffers
|
||||
int32_t mWidth = 0;
|
||||
// Vertical pixel count in the buffers
|
||||
int32_t mHeight = 0;
|
||||
// Values from android_pixel_format_t
|
||||
uint32_t mFormat = 0;
|
||||
// Values from from Gralloc.h
|
||||
uint64_t mUsage = 0;
|
||||
// Bytes per line in the buffers
|
||||
uint32_t mStride = 0;
|
||||
|
||||
// Camera parameters.
|
||||
std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
|
||||
|
||||
|
||||
@@ -18,17 +18,35 @@
|
||||
|
||||
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
|
||||
|
||||
#include <aidlcommonsupport/NativeHandle.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <media/stagefright/MediaCodecConstants.h>
|
||||
#include <ui/GraphicBufferAllocator.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace aidl::android::hardware::automotive::evs::implementation {
|
||||
|
||||
namespace {
|
||||
struct FormatDeleter {
|
||||
void operator()(AMediaFormat* format) const { AMediaFormat_delete(format); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName,
|
||||
std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
|
||||
: mCameraInfo(camInfo) {
|
||||
mDescription.id = deviceName;
|
||||
: mVideoFileName(deviceName), mCameraInfo(camInfo) {
|
||||
mDescription.id = mVideoFileName;
|
||||
|
||||
/* set camera metadata */
|
||||
if (camInfo) {
|
||||
@@ -40,6 +58,256 @@ EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName,
|
||||
initializeParameters();
|
||||
}
|
||||
|
||||
bool EvsVideoEmulatedCamera::initialize() {
|
||||
// Open file.
|
||||
mVideoFd = open(mVideoFileName.c_str(), 0, O_RDONLY);
|
||||
if (mVideoFd < 0) {
|
||||
PLOG(ERROR) << __func__ << ": Failed to open video file \"" << mVideoFileName << "\".";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize Media Extractor.
|
||||
{
|
||||
mVideoExtractor.reset(AMediaExtractor_new());
|
||||
off64_t filesize = lseek64(mVideoFd, 0, SEEK_END);
|
||||
lseek(mVideoFd, 0, SEEK_SET);
|
||||
const media_status_t status =
|
||||
AMediaExtractor_setDataSourceFd(mVideoExtractor.get(), mVideoFd, 0, filesize);
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error when initializing media extractor. Error code: "
|
||||
<< status << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Media Codec and file format.
|
||||
std::unique_ptr<AMediaFormat, FormatDeleter> format;
|
||||
const char* mime;
|
||||
bool selected = false;
|
||||
int numTracks = AMediaExtractor_getTrackCount(mVideoExtractor.get());
|
||||
for (int i = 0; i < numTracks; i++) {
|
||||
format.reset(AMediaExtractor_getTrackFormat(mVideoExtractor.get(), i));
|
||||
if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
|
||||
LOG(ERROR) << __func__ << ": Error in fetching format string";
|
||||
continue;
|
||||
}
|
||||
if (!::android::base::StartsWith(mime, "video/")) {
|
||||
continue;
|
||||
}
|
||||
const media_status_t status = AMediaExtractor_selectTrack(mVideoExtractor.get(), i);
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Media extractor returned error to select track. Error Code: " << status
|
||||
<< ".";
|
||||
return false;
|
||||
}
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
if (!selected) {
|
||||
LOG(ERROR) << __func__ << ": No video track in video file \"" << mVideoFileName << "\".";
|
||||
return false;
|
||||
}
|
||||
|
||||
mVideoCodec.reset(AMediaCodec_createDecoderByType(mime));
|
||||
if (!mVideoCodec) {
|
||||
LOG(ERROR) << __func__ << ": Unable to create decoder.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
|
||||
mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
|
||||
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
|
||||
mFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
|
||||
AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
|
||||
{
|
||||
const media_status_t status =
|
||||
AMediaCodec_configure(mVideoCodec.get(), format.get(), nullptr, nullptr, 0);
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error in configuring mCodec. Error code: " << status << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
format.reset(AMediaCodec_getOutputFormat(mVideoCodec.get()));
|
||||
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &mWidth);
|
||||
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &mHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EvsVideoEmulatedCamera::generateFrames() {
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard lock(mMutex);
|
||||
if (mStreamState != StreamState::RUNNING) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
renderOneFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void EvsVideoEmulatedCamera::onCodecInputAvailable(const int32_t index) {
|
||||
const size_t sampleSize = AMediaExtractor_getSampleSize(mVideoExtractor.get());
|
||||
const int64_t presentationTime = AMediaExtractor_getSampleTime(mVideoExtractor.get());
|
||||
size_t bufferSize = 0;
|
||||
uint8_t* const codecInputBuffer =
|
||||
AMediaCodec_getInputBuffer(mVideoCodec.get(), index, &bufferSize);
|
||||
if (sampleSize > bufferSize) {
|
||||
LOG(ERROR) << __func__ << ": Buffer is not large enough.";
|
||||
}
|
||||
if (presentationTime < 0) {
|
||||
AMediaCodec_queueInputBuffer(mVideoCodec.get(), index, /* offset = */ 0,
|
||||
/* size = */ 0, presentationTime,
|
||||
AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
|
||||
LOG(INFO) << __func__ << ": Reaching the end of stream.";
|
||||
return;
|
||||
}
|
||||
const size_t readSize =
|
||||
AMediaExtractor_readSampleData(mVideoExtractor.get(), codecInputBuffer, sampleSize);
|
||||
const media_status_t status = AMediaCodec_queueInputBuffer(
|
||||
mVideoCodec.get(), index, /*offset = */ 0, readSize, presentationTime, /* flags = */ 0);
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error in queueing input buffer. Error code: " << status;
|
||||
}
|
||||
}
|
||||
|
||||
void EvsVideoEmulatedCamera::onCodecOutputAvailable(const int32_t index,
|
||||
const AMediaCodecBufferInfo& info) {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::nanoseconds;
|
||||
using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
|
||||
using ::aidl::android::hardware::graphics::common::BufferUsage;
|
||||
|
||||
size_t decodedOutSize = 0;
|
||||
uint8_t* const codecOutputBuffer =
|
||||
AMediaCodec_getOutputBuffer(mVideoCodec.get(), index, &decodedOutSize) + info.offset;
|
||||
|
||||
std::size_t renderBufferId = static_cast<std::size_t>(-1);
|
||||
buffer_handle_t renderBufferHandle = nullptr;
|
||||
{
|
||||
std::lock_guard lock(mMutex);
|
||||
if (mStreamState != StreamState::RUNNING) {
|
||||
return;
|
||||
}
|
||||
std::tie(renderBufferId, renderBufferHandle) = useBuffer_unsafe();
|
||||
}
|
||||
if (!renderBufferHandle) {
|
||||
LOG(ERROR) << __func__ << ": Camera failed to get an available render buffer.";
|
||||
return;
|
||||
}
|
||||
std::vector<BufferDesc> renderBufferDescs;
|
||||
renderBufferDescs.push_back({
|
||||
.buffer =
|
||||
{
|
||||
.description =
|
||||
{
|
||||
.width = static_cast<int32_t>(mWidth),
|
||||
.height = static_cast<int32_t>(mHeight),
|
||||
.layers = 1,
|
||||
.format = static_cast<AidlPixelFormat>(mFormat),
|
||||
.usage = static_cast<BufferUsage>(mUsage),
|
||||
.stride = static_cast<int32_t>(mStride),
|
||||
},
|
||||
.handle = ::android::dupToAidl(renderBufferHandle),
|
||||
},
|
||||
.bufferId = static_cast<int32_t>(renderBufferId),
|
||||
.deviceId = mDescription.id,
|
||||
.timestamp = duration_cast<microseconds>(nanoseconds(::android::elapsedRealtimeNano()))
|
||||
.count(),
|
||||
});
|
||||
|
||||
// Lock our output buffer for writing
|
||||
uint8_t* pixels = nullptr;
|
||||
int32_t bytesPerStride = 0;
|
||||
auto& mapper = ::android::GraphicBufferMapper::get();
|
||||
mapper.lock(renderBufferHandle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
|
||||
::android::Rect(mWidth, mHeight), (void**)&pixels, nullptr, &bytesPerStride);
|
||||
|
||||
// If we failed to lock the pixel buffer, we're about to crash, but log it first
|
||||
if (!pixels) {
|
||||
LOG(ERROR) << __func__ << ": Camera failed to gain access to image buffer for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t ySize = mHeight * mStride;
|
||||
std::size_t uvSize = ySize / 4;
|
||||
|
||||
std::memcpy(pixels, codecOutputBuffer, ySize);
|
||||
pixels += ySize;
|
||||
|
||||
uint8_t* u_head = codecOutputBuffer + ySize;
|
||||
uint8_t* v_head = u_head + uvSize;
|
||||
|
||||
for (size_t i = 0; i < uvSize; ++i) {
|
||||
*(pixels++) = *(u_head++);
|
||||
*(pixels++) = *(v_head++);
|
||||
}
|
||||
|
||||
const auto status =
|
||||
AMediaCodec_releaseOutputBuffer(mVideoCodec.get(), index, /* render = */ false);
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error in releasing output buffer. Error code: " << status;
|
||||
}
|
||||
|
||||
// Release our output buffer
|
||||
mapper.unlock(renderBufferHandle);
|
||||
|
||||
// Issue the (asynchronous) callback to the client -- can't be holding the lock
|
||||
if (mStream && mStream->deliverFrame(renderBufferDescs).isOk()) {
|
||||
LOG(DEBUG) << __func__ << ": Delivered " << renderBufferHandle
|
||||
<< ", id = " << renderBufferId;
|
||||
} else {
|
||||
// This can happen if the client dies and is likely unrecoverable.
|
||||
// To avoid consuming resources generating failing calls, we stop sending
|
||||
// frames. Note, however, that the stream remains in the "STREAMING" state
|
||||
// until cleaned up on the main thread.
|
||||
LOG(ERROR) << __func__ << ": Frame delivery call failed in the transport layer.";
|
||||
doneWithFrame(renderBufferDescs);
|
||||
}
|
||||
}
|
||||
|
||||
void EvsVideoEmulatedCamera::renderOneFrame() {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// push to codec input
|
||||
while (true) {
|
||||
int codecInputBufferIdx =
|
||||
AMediaCodec_dequeueInputBuffer(mVideoCodec.get(), /* timeoutUs = */ 0);
|
||||
if (codecInputBufferIdx < 0) {
|
||||
if (codecInputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error in AMediaCodec_dequeueInputBuffer. Error code: "
|
||||
<< codecInputBufferIdx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
onCodecInputAvailable(codecInputBufferIdx);
|
||||
AMediaExtractor_advance(mVideoExtractor.get());
|
||||
}
|
||||
|
||||
// pop from codec output
|
||||
|
||||
AMediaCodecBufferInfo info;
|
||||
int codecOutputputBufferIdx = AMediaCodec_dequeueOutputBuffer(
|
||||
mVideoCodec.get(), &info, /* timeoutUs = */ duration_cast<microseconds>(1ms).count());
|
||||
if (codecOutputputBufferIdx < 0) {
|
||||
if (codecOutputputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": Received error in AMediaCodec_dequeueOutputBuffer. Error code: "
|
||||
<< codecOutputputBufferIdx;
|
||||
}
|
||||
return;
|
||||
}
|
||||
onCodecOutputAvailable(codecOutputputBufferIdx, info);
|
||||
}
|
||||
|
||||
void EvsVideoEmulatedCamera::initializeParameters() {
|
||||
mParams.emplace(
|
||||
CameraParam::BRIGHTNESS,
|
||||
@@ -52,22 +320,54 @@ void EvsVideoEmulatedCamera::initializeParameters() {
|
||||
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
|
||||
}
|
||||
|
||||
::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* /* handle */) {
|
||||
LOG(FATAL) << __func__ << ": Not implemented yet.";
|
||||
return ::android::UNKNOWN_ERROR;
|
||||
::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* handle) {
|
||||
static auto& alloc = ::android::GraphicBufferAllocator::get();
|
||||
unsigned pixelsPerLine = 0;
|
||||
const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
|
||||
0, "EvsVideoEmulatedCamera");
|
||||
if (mStride == 0) {
|
||||
// Gralloc defines stride in terms of pixels per line
|
||||
mStride = pixelsPerLine;
|
||||
} else if (mStride != pixelsPerLine) {
|
||||
LOG(ERROR) << "We did not expect to get buffers with different strides!";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EvsVideoEmulatedCamera::startVideoStreamImpl_locked(
|
||||
const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
|
||||
ndk::ScopedAStatus& /* status */, std::unique_lock<std::mutex>& /* lck */) {
|
||||
LOG(FATAL) << __func__ << ": Not implemented yet.";
|
||||
return false;
|
||||
const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& /* status */,
|
||||
std::unique_lock<std::mutex>& /* lck */) {
|
||||
mStream = receiver;
|
||||
|
||||
const media_status_t status = AMediaCodec_start(mVideoCodec.get());
|
||||
if (status != AMEDIA_OK) {
|
||||
LOG(ERROR) << __func__ << ": Received error in starting decoder. Error code: " << status
|
||||
<< ".";
|
||||
return false;
|
||||
}
|
||||
mCaptureThread = std::thread([this]() { generateFrames(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EvsVideoEmulatedCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
|
||||
std::unique_lock<std::mutex>& /* lck */) {
|
||||
LOG(FATAL) << __func__ << ": Not implemented yet.";
|
||||
return false;
|
||||
std::unique_lock<std::mutex>& lck) {
|
||||
const media_status_t status = AMediaCodec_stop(mVideoCodec.get());
|
||||
lck.unlock();
|
||||
if (mCaptureThread.joinable()) {
|
||||
mCaptureThread.join();
|
||||
}
|
||||
lck.lock();
|
||||
return status == AMEDIA_OK;
|
||||
}
|
||||
|
||||
bool EvsVideoEmulatedCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status,
|
||||
std::unique_lock<std::mutex>& lck) {
|
||||
if (!Base::postVideoStreamStop_locked(status, lck)) {
|
||||
return false;
|
||||
}
|
||||
mStream = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus EvsVideoEmulatedCamera::forcePrimaryClient(
|
||||
@@ -189,10 +489,19 @@ std::shared_ptr<EvsVideoEmulatedCamera> EvsVideoEmulatedCamera::Create(
|
||||
LOG(ERROR) << "Failed to instantiate EvsVideoEmulatedCamera.";
|
||||
return nullptr;
|
||||
}
|
||||
c->mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
|
||||
if (!c->initialize()) {
|
||||
LOG(ERROR) << "Failed to initialize EvsVideoEmulatedCamera.";
|
||||
return nullptr;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
EvsVideoEmulatedCamera::~EvsVideoEmulatedCamera() {}
|
||||
void EvsVideoEmulatedCamera::shutdown() {
|
||||
mVideoCodec.reset();
|
||||
mVideoExtractor.reset();
|
||||
close(mVideoFd);
|
||||
mVideoFd = 0;
|
||||
Base::shutdown();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::automotive::evs::implementation
|
||||
|
||||
Reference in New Issue
Block a user