I have integrated the NDI SDK from NewTek in the current version 5 into my Qt6.3 widget project.
I copied and included the required DLLs and header files from the NDI SDK installation directory into my project.
To test my build environment I tried to compile a simple test program based on the example from "..\NDI 5 SDK\Examples\C++\NDIlib_Recv".
That was also successful.
I was therefore able to receive or access data from my NDI source.
There is therefore a valid frame in the video_frame of the type NDIlib_video_frame_v2_t. Within the structure I can also query correct data of the frame such as the size (.xres and .yres).
The pointer p_data points to the actual data.
So far so good.
Of course, I now want to display this frame on the Qt6 GUI. In other words, the only thing missing now is the conversion into an appropriate format so that I can display the frame with QImage, QPixmap, QLabel, etc.
But how?
So far I've tried calls like this:
curFrame = QImage(video_frame.p_data, video_frame.xres, video_frame.yres, QImage::Format::Format_RGB888);
curFrame.save("out.jpg");
I'm not sure if the format is correct either.
Here's a closer look at the mentioned frame structure within the Qt debug session:
my NDI video frame in the Qt Debug session, after receiving
Within "video_frame" you can see the specification video_type_UYVY.
This may really be the format as it appears at the source!?
Fine, but how do I get this converted now?
Many thanks and best regards
You mean something like this? :)
https://github.com/NightVsKnight/QtNdiMonitorCapture
Specifically:
https://github.com/NightVsKnight/QtNdiMonitorCapture/blob/main/lib/ndireceiverworker.cpp
Assuming you connect using NDIlib_recv_color_format_best:
NDIlib_recv_create_v3_t recv_desc;
recv_desc.p_ndi_recv_name = "QtNdiMonitorCapture";
recv_desc.source_to_connect_to = ...;
recv_desc.color_format = NDIlib_recv_color_format_best;
recv_desc.bandwidth = NDIlib_recv_bandwidth_highest;
recv_desc.allow_video_fields = true;
pNdiRecv = NDIlib_recv_create_v3(&recv_desc);
Then when you receive a NDIlib_video_frame_v2_t:
void NdiReceiverWorker::processVideo(
NDIlib_video_frame_v2_t *pNdiVideoFrame,
QList<QVideoSink*> *videoSinks)
{
auto ndiWidth = pNdiVideoFrame->xres;
auto ndiHeight = pNdiVideoFrame->yres;
auto ndiLineStrideInBytes = pNdiVideoFrame->line_stride_in_bytes;
auto ndiPixelFormat = pNdiVideoFrame->FourCC;
auto pixelFormat = NdiWrapper::ndiPixelFormatToPixelFormat(ndiPixelFormat);
if (pixelFormat == QVideoFrameFormat::PixelFormat::Format_Invalid)
{
qDebug().nospace() << "Unsupported pNdiVideoFrame->FourCC " << NdiWrapper::ndiFourCCToString(ndiPixelFormat) << "; return;";
return;
}
QSize videoFrameSize(ndiWidth, ndiHeight);
QVideoFrameFormat videoFrameFormat(videoFrameSize, pixelFormat);
QVideoFrame videoFrame(videoFrameFormat);
if (!videoFrame.map(QVideoFrame::WriteOnly))
{
qWarning() << "videoFrame.map(QVideoFrame::WriteOnly) failed; return;";
return;
}
auto pDstY = videoFrame.bits(0);
auto pSrcY = pNdiVideoFrame->p_data;
auto pDstUV = videoFrame.bits(1);
auto pSrcUV = pSrcY + (ndiLineStrideInBytes * ndiHeight);
for (int line = 0; line < ndiHeight; ++line)
{
memcpy(pDstY, pSrcY, ndiLineStrideInBytes);
pDstY += ndiLineStrideInBytes;
pSrcY += ndiLineStrideInBytes;
if (pDstUV)
{
// For now QVideoFrameFormat/QVideoFrame does not support P216. :(
// I have started the conversation to have it added, but that may take awhile. :(
// Until then, copying only every other UV line is a cheap way to downsample P216's 4:2:2 to P016's 4:2:0 chroma sampling.
// There are still a few visible artifacts on the screen, but it is passable.
if (line % 2)
{
memcpy(pDstUV, pSrcUV, ndiLineStrideInBytes);
pDstUV += ndiLineStrideInBytes;
}
pSrcUV += ndiLineStrideInBytes;
}
}
videoFrame.unmap();
foreach(QVideoSink *videoSink, *videoSinks)
{
videoSink->setVideoFrame(videoFrame);
}
}
QVideoFrameFormat::PixelFormat NdiWrapper::ndiPixelFormatToPixelFormat(enum NDIlib_FourCC_video_type_e ndiFourCC)
{
switch(ndiFourCC)
{
case NDIlib_FourCC_video_type_UYVY:
return QVideoFrameFormat::PixelFormat::Format_UYVY;
case NDIlib_FourCC_video_type_UYVA:
return QVideoFrameFormat::PixelFormat::Format_UYVY;
break;
// Result when requesting NDIlib_recv_color_format_best
case NDIlib_FourCC_video_type_P216:
return QVideoFrameFormat::PixelFormat::Format_P016;
//case NDIlib_FourCC_video_type_PA16:
// return QVideoFrameFormat::PixelFormat::?;
case NDIlib_FourCC_video_type_YV12:
return QVideoFrameFormat::PixelFormat::Format_YV12;
//case NDIlib_FourCC_video_type_I420:
// return QVideoFrameFormat::PixelFormat::?
case NDIlib_FourCC_video_type_NV12:
return QVideoFrameFormat::PixelFormat::Format_NV12;
case NDIlib_FourCC_video_type_BGRA:
return QVideoFrameFormat::PixelFormat::Format_BGRA8888;
case NDIlib_FourCC_video_type_BGRX:
return QVideoFrameFormat::PixelFormat::Format_BGRX8888;
case NDIlib_FourCC_video_type_RGBA:
return QVideoFrameFormat::PixelFormat::Format_RGBA8888;
case NDIlib_FourCC_video_type_RGBX:
return QVideoFrameFormat::PixelFormat::Format_RGBX8888;
default:
return QVideoFrameFormat::PixelFormat::Format_Invalid;
}
}
I have a recursive function which iterates though directory trees listing the file names located in them.
Here is the function:
void WINAPI SearchFile(PSTR Directory)
{
HANDLE hFind;
WIN32_FIND_DATA FindData;
char SearchName[1024],FullPath[1024];
memset(SearchName,0,sizeof(SearchName));
memset(&FindData,0,sizeof(WIN32_FIND_DATA));
sprintf(SearchName,"%s\\*",Directory);
hFind=FindFirstFile(SearchName,&FindData);
if(hFind!=INVALID_HANDLE_VALUE)
{
while(FindNextFile(hFind,&FindData))
{
if(FindData.cFileName[0]=='.')
{
continue;
}
memset(FullPath,0,sizeof(FullPath));
sprintf(FullPath,"%s\\%s",Directory,FindData.cFileName);
if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
MessageBoxA(NULL, FullPath, "Directory", MB_OK);
SearchFile(FullPath);
}
else
{
MessageBoxA(NULL, FullPath, "File", MB_OK);
}
}
FindClose(hFind);
}
}
There are obviously differences between both functions but I don't understand what's making them act differently. Does anyone know why I am having this problem?
for fast understand error need look for line
goto label;
//SearchFile(FullPath);
at this point hFind containing valid data and FindClose(hFind); need be called for it. but after goto label; executed - your overwrite hFind with hFind = FindFirstFile(SearchName, &FindData); - so you already never close original hFind, never can return to iterate folder after such go to sub-folder. this is key point - need save original hFind before go to sub directory and restore it after. when you do recursive function call - this is done auto - because every sub directory in this case enumerated in self stack frame, which have separate hFind. this is native solution use recursion here.
but possible convert recursion to loop here because we call self always from the single place and as result to this single place. so we can not save return address in stack but do unconditional jump (goto) to known place.
then code have some extra errors, you never check for string buffers overflow, why 1024 as max length is hard-coded when file path can be up to 32768 chars, you not check for reparse point as result can enter to infinite loop, use FindFirstFile instead FindFirstFileEx, etc.
correct code for enumerate sub-folder in loop can be next
void DoEnum(PCWSTR pcszRoot)
{
SIZE_T FileNameLength = wcslen(pcszRoot);
// initial check for . and ..
switch (FileNameLength)
{
case 2:
if (pcszRoot[1] != '.') break;
case 1:
if (pcszRoot[0] == '.') return;
}
static const WCHAR mask[] = L"\\*";
WCHAR FileName[MAXSHORT + 1];
if (_countof(FileName) < FileNameLength + _countof(mask))
{
return;
}
ULONG dwError;
HANDLE hFindFile = 0;
WIN32_FIND_DATA FindData{};
enum { MaxDeep = 0x200 };
//++ stack
HANDLE hFindFileV[MaxDeep];
PWSTR pszV[MaxDeep];
char prefix[MaxDeep+1];
//--stack
ULONG Level = MaxDeep;
memset(prefix, '\t', MaxDeep);
prefix[MaxDeep] = 0;
PWSTR psz = FileName;
goto __enter;
__loop:
hFindFile = FindFirstFileEx(FileName, FindExInfoBasic, &FindData, FindExSearchNameMatch, 0, FIND_FIRST_EX_LARGE_FETCH);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
pcszRoot = FindData.cFileName;
// skip . and ..
switch (FileNameLength = wcslen(pcszRoot))
{
case 2:
if (pcszRoot[1] != '.') break;
case 1:
if (pcszRoot[0] == '.') continue;
}
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((SIZE_T)(FileName + _countof(FileName) - psz) < FileNameLength + _countof(mask))
{
continue;
}
__enter:
memcpy(psz, pcszRoot, (1 + FileNameLength) * sizeof(WCHAR));
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
DbgPrint("%sreparse point: <%S>\n", prefix + Level, pcszRoot);
}
else
{
if (Level)
{
DbgPrint("%s<%S>\n", prefix + Level, psz);
hFindFileV[--Level] = hFindFile;
pszV[Level] = psz;
memcpy(psz += FileNameLength, mask, sizeof(mask));
psz++;
goto __loop;
__return:
*--psz = 0;
psz = pszV[Level];
hFindFile = hFindFileV[Level++];
DbgPrint("%s</%S>\n", prefix + Level, psz);
}
}
}
else
{
DbgPrint("%s[%u%u] %S\n", prefix + Level, FindData.nFileSizeLow, FindData.nFileSizeHigh, pcszRoot);
}
if (!hFindFile)
{
// top level exit
return ;
}
} while (FindNextFile(hFindFile, &FindData));
if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES)
{
dwError = NOERROR;
}
FindClose(hFindFile);
}
else
{
dwError = GetLastError();
}
if (dwError)
{
DbgPrint("<%S> err = %u\n", FileName, dwError);
}
goto __return;
}
The reason for the difference is actually the confusion brought to you by goto label.If you are using the recursive version, after the recursive execution is completed, it will return to the recursive place to continue execution.
In your code, you continue to execute while (FindNextFile(hFind, &FindData)), but when you use goto label, it will jump out of the original loop and restart the program from the label, which leads to what you said list a single directory tree before ending.
If you modify the modified code to the following iterative version, you can understand why there is such a problem.
void fun()
{
char* Directory = "D:\\test";
HANDLE hFind;
WIN32_FIND_DATA FindData;
char SearchName[1024], FullPath[1024];
char LastName[1024] = "";
while (1)
{
memset(SearchName, 0, sizeof(SearchName));
memset(&FindData, 0, sizeof(WIN32_FIND_DATA));
sprintf(SearchName, "%s\\*", Directory);
if (strcmp(SearchName, LastName) == 0)
{
return;
}
strcpy(LastName, SearchName);
hFind = FindFirstFile(SearchName, &FindData);
if (hFind != INVALID_HANDLE_VALUE)
{
while (FindNextFile(hFind, &FindData))
{
if (FindData.cFileName[0] == '.')
{
continue;
}
memset(FullPath, 0, sizeof(FullPath));
sprintf(FullPath, "%s\\%s", Directory, FindData.cFileName);
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
MessageBoxA(NULL, Directory, "Directory", MB_OK);
char cArray[1024];
memset(cArray, 0, sizeof(cArray));
sprintf(cArray, "%s", FullPath);
Directory = cArray;
break;
}
else
{
MessageBoxA(NULL, FullPath, "File", MB_OK);
}
}
FindClose(hFind);
}
}
}
So you cannot achieve the same purpose as recursion by using goto, here you can only use recursion. Of course, I have provided a way to traverse directories non-recursively using queues, which is a more scientific way.
One of the key things that you obtain from recursion is a separate set of local variables for each call to the recursive function. When a function calls itself, and in the recursive call modifies local variables, those local-variable changes do not (directly) affect the local variables of the caller. In your original program, this applies to variables hFind, FindData, SearchName, and FullPath.
If you want similar behavior in a non-recursive version of the function then you need to manually preserve the state of your traversal of one level of the tree when you descend to another level. The goto statement doesn't do any such thing -- it just redirects the control flow of your program. Although there are a few good use cases for goto in C, they are uncommon, and yours is not one of them.
There are several ways to implement manually preserving state, but I would suggest
creating a structure type in which to store those data that characterize the state of your traversal of a particular level. Those appear to be only hFind and FindData -- it looks like the other locals don't need to be preserved. Maybe something like this, then:
struct dir_state {
HANDLE hFind;
WIN32_FIND_DATA FindData;
};
Dynamically allocating an array of structures of that type.
unsigned depth_limit = DEFAULT_DEPTH_LIMIT;
struct dir_state *traversal_states
= malloc(depth_limit * sizeof(*traversal_states));
if (traversal_states == NULL) // ... handle allocation error ...
Tracking the depth of your tree traversal, and for each directory you process, using the array element whose index is the relative depth of that directory.
// For example:
traversal_states[depth].hFind
= FindFirstFile(SearchName, &traversal_states[depth].FindData);
// etc.
Remembering the size of the array, so as to be able to reallocate it larger if the traversal descends too deep for its current size.
// For example:
if (depth >= depth_limit) {
depth_limit = depth_limit * 3 / 2;
struct dir_state *temp
= realloc(traversal_states, depth_limit * sizeof(*traversal_states));
if (temp == NULL) {
// handle error, discontinuing traversal
}
traversal_states = temp;
}
Also, use an ordinary for, while, or do loop instead of a backward-jumping goto. There will be a few details to work out to track when to use FindFirstFile and when FindNextFile (which you would still have with goto), but I'm sure you can sort it out.
Details are left as an exercise.
Unless necessary due to memory or processing constraints or infinite recursion tail conditions that would be complication to introduce there really isn't much need to not use recursion here, since it leads to a readable and elegant solution.
I also want to point out that in "modern" C, any solution using a GOTO is likely not a solution you want since they are so often confusing to use and leads to memory issues (we have loops now to make all of that so much simpler).
Instead of the GOTOs I would suggest implementing a stack of the directories. Wrap the printing logic a while or do-while, and as you are iterating over the files add any directories to the stack. At every new iteration pop and walk the directory at the head of the stack. The loop condition just needs to check if the directory stack is empty, before continuing its block.
I'm using a canvas object inside my component to generate a chart. In order for it animate i'm calling the method recursively. I keep getting an error saying that the method is not defined. Not sure how I need to structure it.
any assistance appreciated.
// Animate function
protected animate(draw_to) {
// Clear off the canvas
this.ctx.clearRect(0, 0, this.width, this.height);
// Start over
this.ctx.beginPath();
// arc(x, y, radius, startAngle, endAngle, anticlockwise)
// Re-draw from the very beginning each time so there isn't tiny line spaces between each section (the browser paint rendering will probably be smoother too)
this.ctx.arc(this.x, this.y, this.radius, this.start, draw_to, false);
// Draw
this.ctx.stroke();
// Increment percent
this.curr++;
// Animate until end
if (this.curr < this.finish + 1) {
// Recursive repeat this function until the end is reached
requestAnimationFrame(function () {
error happens here >>> this.animate(this.circum * this.curr / 100 + this.start);
});
}
}
You need to use an arrow function to keep the same context in the function you give to requestAnimationFrame.
requestAnimationFrame(() => {
error happens here >>> this.animate(this.circum * this.curr / 100 + this.start);
});
Another option is:
requestAnimationFrame(this.animate.bind(this, this.circum * this.curr / 100 + this.start));
You are passing a reference to this.animate which is already bound to the correct this along with the parameters.
I have a 2D array structure to represent a grid of tiles that is a part of the game I am making. One aspect of the game is that the grid is filled in in a somewhat random fashion, based on analysis of a text file. Right from the outset though, I already realised that just leaving it be pretty much randomly done like this without sticking in some kind of validity checks or prevention mechanism, to stop really badly configured grid from forming, would not work out. The main problem I want to avoid is too many tiles that would be untraversable being close together, potentially severing chunks of the grid from the rest.
The idea I came up with to try avoid some really bad grids is to check when assigning a tile value to each "grid square" during generation with logic like this
if (tileBeingInserted.isTraversable()) {
//all is well
return true;
} else {
//we may have a problem, are there too many untraversables nearby?
//Proceed to check all squares "around" the current one.
}
To be clear, checking around the current square means checking the square immediately adjacent in each of the 8 cardinal directions. Now, my problem is that I am trying to reason out how to code this so that it will certainly not give a RangeErrorat any point or at least catch it and recover if it must. As an example, you could clearly take one of the corner squares to be the worst scenario in the sense that only 2 of the squares the algorithm would want to check are within the array's bounds. Naturally, if a RangeErrorhappens for this reason I just want the program to progress onward without issue so the structure
try {
//check1
//check2...8
} catch (RangeError e) {
}
is unacceptable because as soon as a single out of range square is tested the code falls out of the check block. An alternative I thought of, but do not like because of its messiness, would be to individually wrap each check in a try-catch and yes that would work I guess but that's some horrid looking code...so can anyone help me out here? Is there perhaps a different angle from which to come at this problem of avoiding the RangeErrors that I am not seeing?
So my code for testing whether another untraversable tile should be placed has shaped up like this:
bool _tileFitsWell(int tileTypeInt, int row, int col)
{
//...initialise some things, set stuff up
...
if (tile.traversable == true) {
//In this case a new traversable tile is being put in, so no problems.
return true;
} else {
//begin testing what tiles are around the current tile
//Test NW adjacent
if (row > 0 && col > 0) {
temp = tileAt(row - 1, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test N adjacent
if (row > 0) {
temp = tileAt(row - 1, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test NE adjacent
if (row > 0 && col < _grid[0].length - 2) {
temp = tileAt(row - 1, col 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test W adjacent
if (col > 0) {
temp = tileAt(row, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
}
return strikeCount < 2;
}
The code inside each "initial" if-statement (the ones that check row and col) is a bit pseudocode-ish for simplicity's sake. As I explained in a previous comment, the reason why I don't need to check tiles in the other 4 cardinal directions is since these checks are done while filling the map, tiles in those positions will always be either uninitialised or just out of bounds, depending on what tile the function is called to check at a given time.
i have this code that gives me run time error in the line :
body->CreateFixture(&boxDef)
im using cocos2d-x 2.1.5 with box2d 2.2.1 in windows
CCSprite *sprite = CCSprite::create(imageName.c_str());
this->addChild(sprite,1);
b2BodyDef bodyDef;
bodyDef.type = isStatic?b2_staticBody:b2_dynamicBody;
bodyDef.position.Set((position.x+sprite->getContentSize().width/2.0f)/PTM_RATIO,
(position.y+sprite->getContentSize().height/2.0f)/PTM_RATIO);
bodyDef.angle = CC_DEGREES_TO_RADIANS(rotation);
bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
b2FixtureDef boxDef;
if (isCircle)
{
b2CircleShape circle;
circle.m_radius = sprite->getContentSize().width/2.0f/PTM_RATIO;
boxDef.shape = &circle;
}
else
{
b2PolygonShape box;
box.SetAsBox(sprite->getContentSize().width/2.0f/PTM_RATIO, sprite->getContentSize().height/2.0f/PTM_RATIO);
boxDef.shape = &box;
}
if (isEnemy)
{
boxDef.userData = (void*)1;
enemies->insert(body);
}
boxDef.density = 0.5f;
body->CreateFixture(&boxDef) //<-- HERE IS THE RUN TIME ERROR
;
when i debug the box2d code im getting to b2Fixture.cpp
in the method :
void b2Fixture::Create(b2BlockAllocator* allocator, b2Body* body, const b2FixtureDef* def)
in the line :
m_shape = def->shape->Clone(allocator);
getting the runtime error :
R6025 pure virtual function call
Tricky one. I ran into this myself a couple times. It has to do with variable scope.
The boxDef.shape is the problem. You create the shapes as local variables in the if/else blocks and then assign them to boxDef. As soon as execution leaves the if/else block scope those local variables will be garbage. The boxDef.shape now points to freed memory.
The solution is to keep the shape variables in scope by moving the circle and box shape declarations before the if/else block.