Hi,
I have tried 3 methods to implement colorspace conversation.
- with cuda
- with nvhost-vic
- with nvtransform
Unfortunately, none of them could process 4 input signals (1280x720) simultaneously in time. Here is a brief report of them.
- cuda
I used unified memroy to store the UYVY and the result(ABGR32).
Input signal |
Output format |
FPS |
1280x720 UYVY |
1280x720 ABGR32 |
50+ |
1280x720 UYVY |
720x576 ABGR32 |
60 |
1920x1080 UYVY |
1920x1080 ABGR32 |
30+ |
1920x1080 UYVY |
720x576 ABR32 |
50+ |
Input signal |
Output format |
FPS |
1280x720 UYVY |
1280x720 ABGR32 |
30+ |
1280x720 UYVY |
720x576 ABGR32 |
50+ |
1920x1080 UYVY |
1920x1080 ABGR32 |
15+, mostly |
1920x1080 UYVY |
720x576 ABR32 |
30+ |
hostvc is not that good. Because the resolution of the input may be changed, so I create/destroy the NvVideoConverter dynamically, sometimes it will result in deadlock, even if I set the flag to O_NONBLOCK. |
|
|
Input signal |
Output format |
FPS |
1280x720 UYVY |
1280x720 ABGR32 |
30+ |
1280x720 UYVY |
720x576 ABGR32 |
60 |
1920x1080 UYVY |
1920x1080 ABGR32 |
15+, mostly |
1920x1080 UYVY |
720x576 ABR32 |
40+ |
Here is my code.
///
/// (C) 2018-2020 CHIV
///
#include "BoogooSource.h"
#include "BoogooInput.h"
#include "domp/Mutex.h"
#include "cuda/cudaYUV.h"
#include <npp.h>
// STL
#include <string.h>
#include <atomic>
#include <list>
#include <string>
namespace input {
class BoogooSource::Resource {
public:
Resource()
{
mState = 0;
mGpuBuffer = nullptr;
mGpuBufferSize = 0;
mUseClipper = false;
// create cuda stream
int device_id;
cudaGetDevice(&device_id);
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, device_id);
if (!prop.deviceOverlap) {
printf("\033[31mWarning:cuda-device does not support overlap\033[0m\n");
}
cudaStreamCreate(&mCudaStream);
}
~Resource()
{
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
delete *it;
}
if (mGpuBuffer) { cudaFree(mGpuBuffer); }
cudaStreamDestroy(mCudaStream);
}
std::atomic<int> mState;
//
std::list<FrameSubscriber*> mSubs;
//
domp::Mutex mLock;
//
FrameSubscriber* AddSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
if (*it == sub) return sub;
}
mSubs.push_back(sub);
//
return sub;
}
FrameSubscriber* TakeSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
FrameSubscriber* ret = nullptr;
for (auto it = mSubs.begin(); it != mSubs.end();) {
if (*it == sub) {
ret = *it;
it = mSubs.erase(it);
break;
} else ++it;
}
return ret;
}
//
void Broadcast(const boogoo::RGBA8Frame& frame, int error = FrameSubscriber::eOk)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
(*it)->Handle(frame, error);
}
}
//
template <typename T>
inline bool CheckAndAlloc(T*& gpu_buffer, int& buffer_size, const int expsz)
{
if (expsz > buffer_size) {
// need to reallocate
if (gpu_buffer) {
cudaFree(gpu_buffer);
gpu_buffer = nullptr;
}
auto er = cudaMalloc((void**)&gpu_buffer, expsz);
if (er != cudaSuccess) {
return false;
}
buffer_size = expsz;
}
return true;
}
// The input format must be YUV 422 8bits with UYVY order.
void Update(unsigned char* frame, int cx, int cy)
{
// check, if we should allocate memory, do it.
if (!CheckAndAlloc(mGpuBuffer, mGpuBufferSize, 4*cx*cy)) {
Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eCUDAError);
return;
}
// clip.
if (mUseClipper && cx != 720 && cy != 576) {
if (mPal.Cx() != 720 || mPal.Cy() != 576) {
mPal = boogoo::RGBA8Frame(720, 576);
}
double affine[2][3] = {
{((double)mPal.Cx())/1160, 0, -40},
{0, ((double)mPal.Cy())/645, -39}
};
//cudaStreamAttachMemAsync(mCudaStream, frame, 0, cudaMemAttachGlobal);
//cudaStreamAttachMemAsync(mCudaStream, mPal.Data(), 0, cudaMemAttachGlobal);
cudaUYVYToRGBA((uchar2*)frame, (uchar4*)mGpuBuffer, cx, cy);
nppiWarpAffine_8u_C4R((Npp8u*)mGpuBuffer, NppiSize{cx, cy}, cx*4, NppiRect{64, 44, 1160, 645},
(Npp8u*)mPal.Data(), mPal.Cx()*4, NppiRect{0,0,mPal.Cx(), mPal.Cy()},
affine, NPPI_INTER_LINEAR);
//cudaStreamAttachMemAsync(mCudaStream, frame, 0, cudaMemAttachHost);
//cudaStreamAttachMemAsync(mCudaStream, mPal.Data(), 0, cudaMemAttachHost);
//cudaStreamSynchronize(mCudaStream);
Broadcast(mPal, FrameSubscriber::eOk);
} else {
if (mFrame.Cx() != cx || mFrame.Cy() != cy) {
mFrame = boogoo::RGBA8Frame(cx, cy);
}
cudaUYVYToRGBA((uchar2*)frame, (uchar4*)mFrame.Data(), cx, cy);
cudaStreamSynchronize(mCudaStream);
Broadcast(mFrame, FrameSubscriber::eOk);
}
}
//
bool mUseClipper;
unsigned char* mGpuBuffer;
int mGpuBufferSize;
boogoo::RGBA8Frame mFrame;
boogoo::RGBA8Frame mPal; // 720x576
cudaStream_t mCudaStream;
};
BoogooSource::BoogooSource(const char* devname)
: mFrame(0)
, mFrameSize(0)
{
mResource = new Resource();
mInput = new BoogooInput(devname);
}
BoogooSource::~BoogooSource()
{
// stop thread.
Stop();
// delete frame buffer
if (mFrame) { cudaFree(mFrame); }
//
delete mInput;
delete mResource;
}
FrameSubscriber* BoogooSource::AddSubscriber(FrameSubscriber* sub)
{
return mResource->AddSubscriber(sub);
}
FrameSubscriber* BoogooSource::TakeSubscriber(FrameSubscriber* sub)
{
return mResource->TakeSubscriber(sub);
}
void BoogooSource::SetUseClipper(bool b)
{
mResource->mUseClipper = b;
}
bool BoogooSource::GetUseClipper() const
{
return mResource->mUseClipper;
}
void BoogooSource::SetResolution(int cx, int cy)
{
if (mResource->mState) {
mInput->SetResolution(cx, cy);
}
}
void BoogooSource::GetResolution(int &cx, int &cy) const
{
if (mResource->mState) {
mInput->GetResolution(cx, cy);
}
else {
cx = 1920;
cy = 1080;
}
}
bool BoogooSource::Grab(void* buffer, int sz)
{
if (mResource->mState) {
memcpy(buffer, mFrame, sz > mFrameSize ? mFrameSize : sz);
return true;
}
else {
memset(buffer, 0, sz);
return false;
}
}
bool BoogooSource::Start()
{
if (mInput->Init()) {
if (mInput->Start()) {
return SimpleThread::Start(); // start the thread.
}
}
return false;
}
#define ARRAYSZ(a) (sizeof(a)/sizeof(a[0]))
int BoogooSource::Run()
{
static const struct _fmtlist {
int cx, cy;
} fmtlist[] = {
{1920, 1080},
{1280, 720},
{720, 576}
};
const int kTimeout = 100;
int sfmt = 0; // always stands for the next fmt to try
while (!ShouldExit()) {
if (mFrameSize < mInput->GetFrameSize()) {
if (mFrame) {
cudaFree(mFrame);
}
if (cudaMallocManaged((void**)&mFrame, mInput->GetFrameSize(), cudaMemAttachHost) != cudaSuccess) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eCUDAError);
break;
} else {
mFrameSize = mInput->GetFrameSize();
}
}
if (!mInput->IsFrameAvailable(kTimeout)) {
// if failed, try another resolution.
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt+1) % ARRAYSZ(fmtlist); // prepare next fmt
}
else {
int ret = mInput->Retrieve(mFrame, mFrameSize, 0);
if (ret < 0) {
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt+1) % ARRAYSZ(fmtlist); // prepare next fmt
mResource->mState = 0; // update state
}
else {
mResource->mState = 1;
int cx, cy;
mInput->GetResolution(cx, cy);
// update
mResource->Update(mFrame, cx, cy);
}
}
}
mResource->mState = 0;
return 0;
}
}
///
/// (C) 2020 CHIV
///
#include "BoogooInput.h"
#include "BoogooSource.h"
#include "domp/Mutex.h"
#include "hostvc/NvVideoConverter.h"
#include "hostvc/nvbuf_utils.h"
#include <fcntl.h>
#include <uuid/uuid.h>
// STL
#include <atomic>
#include <list>
#include <string.h>
#include <string>
namespace input {
class BoogooSource::Resource {
public:
Resource()
{
mState = 0;
mUseClipper = false;
mVc = nullptr;
}
~Resource()
{
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
delete *it;
}
if (mVc) {
mVc->capture_plane.setStreamStatus(false);
mVc->output_plane.setStreamStatus(false);
mVc->capture_plane.stopDQThread();
mVc->output_plane.stopDQThread();
delete mVc;
}
delete mVc;
}
//
std::atomic<int> mState;
/// subscribers
std::list<FrameSubscriber*> mSubs;
/// protect \c mSubs
domp::Mutex mLock;
//
FrameSubscriber* AddSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
if (*it == sub)
return sub;
}
mSubs.push_back(sub);
//
return sub;
}
FrameSubscriber* TakeSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
FrameSubscriber* ret = nullptr;
for (auto it = mSubs.begin(); it != mSubs.end();) {
if (*it == sub) {
ret = *it;
it = mSubs.erase(it);
break;
} else
++it;
}
return ret;
}
//
void Broadcast(const boogoo::RGBA8Frame& frame, int error = FrameSubscriber::eOk)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
(*it)->Handle(frame, error);
}
}
//
static inline void _NvBufferToFrame(NvBuffer* buffer, boogoo::RGBA8Frame& frame)
{
char* data = nullptr;
size_t dptr = 0;
//
NvBuffer::NvBufferPlane& plane = buffer->planes[0];
size_t linesz = plane.fmt.bytesperpixel * plane.fmt.width;
data = (char*)plane.data;
if (plane.fmt.stride == frame.BytesOfLine()) {
memcpy(frame.Data(), data, frame.NumberOfBytes());
} else {
for (uint32_t j = 0; j < plane.fmt.height; j++) {
memcpy(frame.Data() + dptr, data, linesz);
dptr += linesz;
data += plane.fmt.stride;
}
}
}
// Hint: dq thread
static bool HostVcDQCallback(struct v4l2_buffer* v4l2_buf,
NvBuffer* buffer, NvBuffer* shared_buffer,
void* arg)
{
(void)shared_buffer;
auto rc = static_cast<BoogooSource::Resource*>(arg);
//
if (!v4l2_buf) { return true; }
if (v4l2_buf->m.planes[0].bytesused >= rc->mFrame.NumberOfBytes()) {
_NvBufferToFrame(buffer, rc->mFrame);
rc->Broadcast(rc->mFrame, FrameSubscriber::eOk);
}
if (rc->mVc->capture_plane.qBuffer(*v4l2_buf, buffer) < 0) {
printf("qBuffer failed, %s:%d\n", __FILE__, __LINE__);
abort();
return false;
}
return true;
}
/// YUV422 8bits UYVY order
bool CheckAndCreate(int cx, int cy)
{
bool rebuild = false;
// 2 conditions result in rebuilding.
if (mUseClipper && (mFrame.Cx() != 720 || mFrame.Cy() != 576)) {
rebuild = true;
} else if (!mUseClipper) {
if (mFrame.Cx() != cx || mFrame.Cy() != cy) {
rebuild = true;
}
}
// rebuild the hostvc if needed.
if (rebuild || !mVc) {
printf("[boogoo] rebuild hostvc\n");
if (mVc) { delete mVc; }
if (mUseClipper) {
if (mFrame.Cx() != 720 || mFrame.Cy() != 576) {
mFrame = boogoo::RGBA8Frame(720, 576);
}
} else {
if (mFrame.Cx() != cx || mFrame.Cy() != cy) {
mFrame = boogoo::RGBA8Frame(cx, cy);
}
}
//
uuid_t uuid_; char uuid_str_[64];
uuid_generate_random(uuid_);
uuid_unparse(uuid_, uuid_str_);
mVc = NvVideoConverter::createVideoConverter(uuid_str_, O_NONBLOCK);
if (mVc->isInError()) {
printf("can't create hostvc\n");
delete mVc; mVc = nullptr;
return false;
}
int ret = 0;
ret = mVc->setOutputPlaneFormat(V4L2_PIX_FMT_UYVY, cx, cy, V4L2_NV_BUFFER_LAYOUT_PITCH);
if (mUseClipper) {
ret = mVc->setCropRect(64, 44, 1160, 645);
ret = mVc->setCapturePlaneFormat(V4L2_PIX_FMT_ABGR32,
720, 576, V4L2_NV_BUFFER_LAYOUT_PITCH);
} else {
ret = mVc->setCapturePlaneFormat(V4L2_PIX_FMT_ABGR32,
cx, cy, V4L2_NV_BUFFER_LAYOUT_PITCH);
}
// init queue
mVc->output_plane.setupPlane(V4L2_MEMORY_USERPTR, 2, false, true);
mVc->output_plane.setStreamStatus(true);
mVc->capture_plane.setupPlane(V4L2_MEMORY_USERPTR, 2, false, true);
mVc->capture_plane.setStreamStatus(true);
mVc->capture_plane.setDQThreadCallback(HostVcDQCallback);
mVc->capture_plane.startDQThread(this);
//
for (uint32_t i = 0; i < mVc->capture_plane.getNumBuffers(); i++) {
struct v4l2_buffer v4l2_buf;
struct v4l2_plane planes[MAX_PLANES];
memset(&v4l2_buf, 0, sizeof(v4l2_buf));
memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
v4l2_buf.index = i;
v4l2_buf.m.planes = planes;
int ret = mVc->capture_plane.qBuffer(v4l2_buf, NULL);
if (ret < 0) {
printf("qBuffer failed, %s:%d\n", __FILE__, __LINE__);
abort();
}
}
//
for (uint32_t i = 0; i < mVc->output_plane.getNumBuffers(); i++) {
struct v4l2_buffer v4l2_buf;
struct v4l2_plane planes[MAX_PLANES];
memset(&v4l2_buf, 0, sizeof(v4l2_buf));
memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));
v4l2_buf.index = i;
v4l2_buf.m.planes = planes;
int ret = mVc->output_plane.qBuffer(v4l2_buf, NULL);
if (ret < 0) {
printf("qBuffer failed, %s:%d\n", __FILE__, __LINE__);
abort();
}
}
}
return true;
}
// The input format must be YUV 422 8bits with UYVY order.
int Update(BoogooInput* input, int cx, int cy)
{
int ret = -1;
// Hint: grabber thread
if (CheckAndCreate(cx, cy)) {
struct v4l2_buffer v4l2_buf;
struct v4l2_plane planes[MAX_PLANES];
NvBuffer *buffer = nullptr;
memset(&v4l2_buf, 0, sizeof(v4l2_buf));
memset(planes, 0, sizeof(planes));
v4l2_buf.m.planes = planes;
uint32_t dptr = 0;
if (mVc->output_plane.dqBuffer(v4l2_buf, &buffer, NULL, 1) == 0) {
for (int i = 0; i < MAX_PLANES; ++i) {
buffer->planes[i].bytesused = 0;
}
// The input is UYVY absolutely, so we could memcpy, or
// you should copy UYVY frame line by line.
NvBuffer::NvBufferPlane& plane = buffer->planes[0];
#if 0
uint32_t linesz = plane.fmt.bytesperpixel * plane.fmt.width;
char* data = (char*)plane.data;
for (uint32_t j = 0; j < plane.fmt.height; j++) {
memcpy(data, frame + dptr, linesz);
data += plane.fmt.stride;
dptr += linesz;
}
#endif
plane.bytesused = plane.fmt.stride * plane.fmt.height;
ret = input->Retrieve(plane.data, (int)plane.bytesused, 0);
//
mVc->output_plane.qBuffer(v4l2_buf, NULL);
} else {
// if hostvc is busy, discard this frame
ret = 0;
}
} else {
Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
}
return ret;
}
//
bool mUseClipper;
boogoo::RGBA8Frame mFrame;
NvVideoConverter* mVc;
};
BoogooSource::BoogooSource(const char* devname)
: mFrame(0) // Not used in hostvc
, mFrameSize(0)
{
mResource = new Resource();
mInput = new BoogooInput(devname);
}
BoogooSource::~BoogooSource()
{
// stop thread.
Stop();
// delete input and source.
delete mResource;
delete mInput;
// delete UYVY frame buffer
if (mFrame) {
delete[] mFrame;
}
}
FrameSubscriber* BoogooSource::AddSubscriber(FrameSubscriber* sub)
{
return mResource->AddSubscriber(sub);
}
FrameSubscriber* BoogooSource::TakeSubscriber(FrameSubscriber* sub)
{
return mResource->TakeSubscriber(sub);
}
void BoogooSource::SetUseClipper(bool b)
{
// Hint: main thread
mResource->mUseClipper = b;
}
bool BoogooSource::GetUseClipper() const
{
return mResource->mUseClipper;
}
void BoogooSource::SetResolution(int cx, int cy)
{
if (mResource->mState) {
mInput->SetResolution(cx, cy);
}
}
void BoogooSource::GetResolution(int& cx, int& cy) const
{
if (mResource->mState) {
mInput->GetResolution(cx, cy);
} else {
cx = 1920;
cy = 1080;
}
}
bool BoogooSource::Grab(void* buffer, int sz)
{
if (mResource->mState) {
memcpy(buffer, mFrame, sz > mFrameSize ? mFrameSize : sz);
return true;
} else {
memset(buffer, 0, sz);
return false;
}
}
bool BoogooSource::Start()
{
if (mInput->Init()) {
if (mInput->Start()) {
return SimpleThread::Start(); // start the thread.
}
}
return false;
}
#define ARRAYSZ(a) (sizeof(a) / sizeof(a[0]))
int BoogooSource::Run()
{
static const struct _fmtlist {
int cx, cy;
} fmtlist[] = {
{ 1920, 1080 },
{ 1280, 720 },
{ 720, 576 }
};
const int kTimeout = 100;
int sfmt = 0; // always stands for the next fmt to try
while (!ShouldExit()) {
if (!mInput->IsFrameAvailable(kTimeout)) {
// if failed, try another resolution.
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt + 1) % ARRAYSZ(fmtlist); // prepare next fmt
} else {
int cx = 1280, cy = 720;
mInput->GetResolution(cx, cy);
int ret = mResource->Update(mInput, cx, cy);
if (ret < 0) {
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt + 1) % ARRAYSZ(fmtlist); // prepare next fmt
mResource->mState = 0; // update state
} else {
mResource->mState = 1;
}
}
}
mResource->mState = 0;
return 0;
}
} // namespace input
///
/// (C) 2020 CHIV
///
#include "BoogooInput.h"
#include "BoogooSource.h"
#include "domp/Mutex.h"
#include "hostvc/nvbuf_utils.h"
// STL
#include <atomic>
#include <list>
#include <string.h>
#include <string>
namespace input {
class BoogooSource::Resource {
public:
Resource()
{
mState = 0;
mUseClipper = false;
mBufferABGR = -1;
mBufferUYVY = -1;
mBufferUYVYp = nullptr;
mSession = NvBufferSessionCreate();
}
~Resource()
{
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
delete *it;
}
if (mBufferUYVY >= 0) { NvBufferDestroy(mBufferUYVY); }
if (mBufferABGR >= 0) { NvBufferDestroy(mBufferABGR); }
NvBufferSessionDestroy(mSession);
}
//
std::atomic<int> mState;
/// subscribers
std::list<FrameSubscriber*> mSubs;
/// protect \c mSubs
domp::Mutex mLock;
//
FrameSubscriber* AddSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
if (*it == sub)
return sub;
}
mSubs.push_back(sub);
//
return sub;
}
FrameSubscriber* TakeSubscriber(FrameSubscriber* sub)
{
domp::AutoLocker _(mLock);
FrameSubscriber* ret = nullptr;
for (auto it = mSubs.begin(); it != mSubs.end();) {
if (*it == sub) {
ret = *it;
it = mSubs.erase(it);
break;
} else
++it;
}
return ret;
}
//
void Broadcast(const boogoo::RGBA8Frame& frame, int error = FrameSubscriber::eOk)
{
domp::AutoLocker _(mLock);
for (auto it = mSubs.begin(); it != mSubs.end(); ++it) {
(*it)->Handle(frame, error);
}
}
/// YUV422 8bits UYVY order
bool CheckAndCreate(int cx, int cy)
{
bool rebuild = false;
// 2 conditions result in rebuilding.
if (mUseClipper && (mFrame.Cx() != 720 || mFrame.Cy() != 576)) {
rebuild = true;
} else if (!mUseClipper) {
if (mFrame.Cx() != cx || mFrame.Cy() != cy) {
rebuild = true;
}
}
// rebuild the hostvc if needed.
if (rebuild) {
if (mBufferUYVY >= 0) { NvBufferMemUnMap(mBufferUYVY, 0, &mBufferUYVYp); NvBufferDestroy(mBufferUYVY); }
if (mBufferABGR >= 0) { NvBufferDestroy(mBufferABGR); }
int ret = 0;
ret = NvBufferCreate(&mBufferUYVY, cx, cy, NvBufferLayout_Pitch, NvBufferColorFormat_UYVY);
if (ret != 0) { mBufferUYVY = -1; return false; }
ret = NvBufferCreate(&mBufferABGR, cx, cy, NvBufferLayout_Pitch, NvBufferColorFormat_ABGR32);
if (ret != 0) { mBufferABGR = -1; return false; } // don't worry, the dtor will release dma buffer
mFrame = mUseClipper ? boogoo::RGBA8Frame(720, 576) : boogoo::RGBA8Frame(cx, cy);
//
NvBufferMemMap(mBufferUYVY, 0, NvBufferMem_Write, &mBufferUYVYp);
}
return true;
}
// The input format must be YUV 422 8bits with UYVY order.
int Update(BoogooInput* input, int cx, int cy)
{
int ret = -1;
// Hint: grabber thread
if (CheckAndCreate(cx, cy)) {
// load to buffer.
ret = input->Retrieve(mBufferUYVYp, 2*cx*cy, 0);
// convert
NvBufferTransformParams tps;
tps.dst_rect = NvBufferRect{0, 0, (unsigned int)mFrame.Cx(), (unsigned int)mFrame.Cy()};
tps.src_rect = mUseClipper ? NvBufferRect{44, 64, 1160, 645} : NvBufferRect{0, 0, (unsigned int)cx, (unsigned int)cy};
tps.session = mSession;
tps.transform_filter = NvBufferTransform_Filter_Bilinear;
tps.transform_flag = NVBUFFER_TRANSFORM_CROP_SRC | NVBUFFER_TRANSFORM_CROP_DST; // crop src only
tps.transform_flip = NvBufferTransform_None;
ret = NvBufferTransform(mBufferUYVY, mBufferABGR, &tps);
if (ret == 0) {
NvBuffer2Raw(mBufferABGR, 0, mFrame.Cx(), mFrame.Cy(), mFrame.Data());
Broadcast(mFrame);
}
//
} else {
Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
}
return ret;
}
//
bool mUseClipper;
boogoo::RGBA8Frame mFrame;
int mBufferUYVY; // input
int mBufferABGR; // output
void* mBufferUYVYp;
NvBufferSession mSession;
};
BoogooSource::BoogooSource(const char* devname)
: mFrame(0) // Not used in hostvc
, mFrameSize(0)
{
mResource = new Resource();
mInput = new BoogooInput(devname);
}
BoogooSource::~BoogooSource()
{
// stop thread.
Stop();
// delete input and source.
delete mResource;
delete mInput;
// delete UYVY frame buffer
if (mFrame) { delete[] mFrame; }
}
FrameSubscriber* BoogooSource::AddSubscriber(FrameSubscriber* sub)
{
return mResource->AddSubscriber(sub);
}
FrameSubscriber* BoogooSource::TakeSubscriber(FrameSubscriber* sub)
{
return mResource->TakeSubscriber(sub);
}
void BoogooSource::SetUseClipper(bool b)
{
// Hint: main thread
mResource->mUseClipper = b;
}
bool BoogooSource::GetUseClipper() const
{
return mResource->mUseClipper;
}
void BoogooSource::SetResolution(int cx, int cy)
{
if (mResource->mState) {
mInput->SetResolution(cx, cy);
}
}
void BoogooSource::GetResolution(int& cx, int& cy) const
{
if (mResource->mState) {
mInput->GetResolution(cx, cy);
} else {
cx = 1920;
cy = 1080;
}
}
bool BoogooSource::Grab(void* buffer, int sz)
{
if (mResource->mState) {
memcpy(buffer, mFrame, sz > mFrameSize ? mFrameSize : sz);
return true;
} else {
memset(buffer, 0, sz);
return false;
}
}
bool BoogooSource::Start()
{
if (mInput->Init()) {
if (mInput->Start()) {
return SimpleThread::Start(); // start the thread.
}
}
return false;
}
#define ARRAYSZ(a) (sizeof(a) / sizeof(a[0]))
int BoogooSource::Run()
{
static const struct _fmtlist {
int cx, cy;
} fmtlist[] = {
{ 1920, 1080 },
{ 1280, 720 },
{ 720, 576 }
};
const int kTimeout = 100;
int sfmt = 0; // always stands for the next fmt to try
while (!ShouldExit()) {
if (!mInput->IsFrameAvailable(kTimeout)) {
// if failed, try another resolution.
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt + 1) % ARRAYSZ(fmtlist); // prepare next fmt
} else {
int cx = 1280, cy = 720;
mInput->GetResolution(cx, cy);
int ret = mResource->Update(mInput, cx, cy);
if (ret < 0) {
mResource->mState = 0;
mInput->SetResolution(fmtlist[sfmt].cx, fmtlist[sfmt].cy);
if (!mInput->Reset()) {
mResource->Broadcast(boogoo::RGBA8Frame(), FrameSubscriber::eSystemFault);
break;
}
sfmt = (sfmt + 1) % ARRAYSZ(fmtlist); // prepare next fmt
mResource->mState = 0; // update state
} else {
mResource->mState = 1;
}
}
}
mResource->mState = 0;
return 0;
}
} // namespace input