PDA

View Full Version : QSocketNotifier--is errno still valid in slot connected to activate(int)



davethomaspilot
12th January 2015, 13:43
I have an application that's streaming frames from a webcam. After a few minutes of streaming, the QSocketNotifier associated with camera fd stops generating activated events.

This might be a camera hardware, firmware, or RPI kernel issue, but I want to make sure my use of a QSocketNotifier instead of the exact code in the Vl42 example isn't part of the problem



In examples mainLoop() function there is a blocking select:


r = select(fd + 1, &fds, NULL, NULL, &tv);
if (-1 == r) {
if (EINTR == errno)
continue;

I have these lines of code instead:



fd = open(dev_name.toAscii().data(), O_RDWR /* required */ | O_NONBLOCK, 0);
notify_fd = new QSocketNotifier(fd, QSocketNotifier::Read);
connect(notify_fd, SIGNAL(activated(int)), this, SLOT(read_frame(int)), Qt::QueuedConnection );


So, what happens when the fd associated the QSocketNotifier breaks out of the select with EINTR==errno? I've been assuming that's a QSocketNotifier::Exception event, so creating the QSocketNotifier using a QSocketNotifier::Read type would result in exceptions like the EINTR==errno being ignored. That's the equivalent of the example code.

Is this true? Can I check errno in the read_frame slot, or will it likely be reset before the slot code executes?

Next step will be to redo the code without the QtSocketNotifier, but that will take a few hours to get working, I think.

Thanks!

anda_skoa
12th January 2015, 15:22
Your approach looks valid to me.
Have you tried a second notifier for the Exception type?

Cheers,
_

davethomaspilot
12th January 2015, 15:36
Thanks for the reply.

We think alike--yes, I added this since my post:

exception_fd = new QSocketNotifier(fd, QSocketNotifier::Exception);
connect(exception_fd, SIGNAL(activated(int)), this, SLOT(printException(int)));

void printException(int _fd){qDebug() << "errno: " << errno;}

I reproduce the hang, but printException never gets called.

anda_skoa
12th January 2015, 16:05
Strange, the Qt event loop, or rather the unix event dispatcher works quite similar to the select() call.

Cheers,
_

yeye_olive
12th January 2015, 16:34
What do you do in the read_frame(int) slot? Are you certain that you read all the bytes available for reading? The name of the slot suggests that it reads exactly one frame. Perhaps, at some point, two frames (or one frame and a fraction of another, etc.) are available for reading by the time the slot has a chance to run, then the slot only reads one frame, and activated(int) is never emitted again since there are some bytes still waiting to be read.

davethomaspilot
12th January 2015, 18:08
While that's not the cause of this issue (camera quits streaming), your note got me looking at the read_frame code.

I'd had a code fragment that just ignored the frame (by re-queuing without processing it) when it didn't match the expected frame size. However, that code was for a specific camera resolution and needed changing for the camera I'm testing with now.

But, the hang occurs immediately after receiving frames of the expected size. I fixedthe code that ignores frames with unexpected buffer sizes, but it never executes even when I see the hangs. So, that's not what's causing this issue.

But thanks for the good suggestion! It lead me to fix what would have been a nasty bug.

Code for read_frame:



int CameraInterface::read_frame(int)
{
struct v4l2_buffer buf;
unsigned int i;
//qDebug() << frames_read++;
if (testing_only) return 0;


if (!stream_forever && frames_read >= frame_count)
{
qDebug() << "frame count exceeded";
emit frames_done();
return 0;
}

switch (io)
{
.....

case IO_METHOD_MMAP:
CLEAR(buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;

if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf))
{
switch (errno)
{
case EAGAIN:
return 0;

case EIO:
/* Could ignore EIO, see spec. */

/* fall through */

default:
errno_exit("VIDIOC_DQBUF");
}
}
#if 0
qDebug() << "frames read: " << frames_read++;
if (frames_read % 2)
{
qDebug() << "skipping frame";
xioctl(fd, VIDIOC_QBUF, &buf);
return 1;
}
#endif
frameQueued = false;
assert(buf.index < n_buffers);

qDebug() << "\t\t\tframe DQ'ed, bytes " << buf.bytesused << "buffers = " << n_buffers;
if (buf.bytesused != 921600)
{
qDebug() << "partial frame";
xioctl(fd, VIDIOC_QBUF, &buf);
frameQueued = true;
qDebug() << "Unexpected framesize" << buf.bytesused;
//exit(-1);
}
/*
if (!colorCamera && buf.bytesused < 1843200)
{
xioctl(fd, VIDIOC_QBUF, &buf);
frameQueued = true;
qDebug() << "Partial frame" << buf.bytesused;
}
*/
else
{

emit frameReady(buffers[buf.index].start,buf.bytesused);

// when streaming expect client to send message to queue next frame
// otherwise, we know there's been seconds for client to process frame
// setup = false;
if (!setup)
{
xioctl(fd, VIDIOC_QBUF, &buf);
frameQueued=true;
}
}

break;

...

davethomaspilot
12th January 2015, 23:58
The problem probably isn't related to QSocketNotifier.

I hacked my code to immediately enqueue after each dequeue. With this change, it streamed for several hours without hanging. This is using the same code that binds the activated (type = read) signal from a QTSocketNotifier to a slot to do the xioctl to dequeue the frame.

So, I conclude that the issue isn't related to my use of QSocketNotifier.

I also tried sleeping two seconds after each dequeue, then enqueing. So far,no hang, but it will have to run much longer to get a lot of frames through.