PDA

View Full Version : QLabel does not repaint QPixmap



__Emanuel
4th February 2017, 22:26
Hello!

I'm using QT5.7.
I have 2 labels that's used for video frame representation. For each label i pass frames correctly and it works with code below.
I just simply get new frames from my external source, than i convert them to Pixmap and call setPixmap(map);
Code looks like this :

QImage image = QImage(frame.data, frame.width, frame.height, QImage::Format_RGB888);
image = image.scaledToWidth(320, Qt::SmoothTransformation);
QPixmap map = QPixmap::fromImage(image);
QLabel *label = (QLabel *)ui->streamPlayerTable->cellWidget(x,y)->findChild<QLabel*>();
label->setPixmap(map);

Both videos goes correctly for 1min, 5min, sometimes 30min. But sometimes one of those videos just stop. Seemingly with no reason!! I see that new pixmaps are created for both labels. Frames are coming, gdb does not show any suspicious thread exiting messages, memory usage is constant and cpu is not very high at the moment when some video stops, also no console messages.

It seems that pixmap are stuck in some repaint request. But as i don't know a lot of QT framework internals, i please to help me to understand what might cause such behaviour.

Is there any way to force label to repaint pixmap with no crashes ?

If anyone had experienced in such behaviour on QT please share ideas to solve it.

Thanks in advance!

d_stranz
5th February 2017, 01:36
You do not show enough of the context in which this code is running, so it isn't possible to give you an answer.

I don't know how you retrieve the frames from the external source, but you could have a race condition. The fact that your freezes occur sort of randomly implies some timing issue like this.

Instead of calling findChild() every time you want to update the pixmap, why don't you simply save the QLabel's pointer when you create it?

__Emanuel
5th February 2017, 09:36
why don't you simply save the QLabel's pointer when you create it?
Yes, this is a good point! I am going to do this to perform a little better. But i have doubt that it may help. You think it could ?
I also though how to make more optimisation, but unfortunately, i get those buffers in api thread.

Can you tell me more about that race condition, please! I though that all label content internally gets rendered in to one api thread. So it's not like that ?

I get the frames by signal from library that provides me with new frames. I see that those frames are coming smooth, because when i manually just resize the main window, both videos are playing smoothly. But i would like not do that in order to see both videos. :D But if i don't do this, the one who stuck keeps being stuck.

__Emanuel
5th February 2017, 13:02
Yes, but if it's race condition and timing issue, than why it's possible to get smooth playback on both videos if manually change main window resize. As long as i change it, it repaints both label frames and nothing stucks. Once i stop changing size, the same label that was stopped previously, stop again. Though that timing issue should keep blocked forever even if i resize it.

I am definit that the race condition is not in frame receiving. I monitor the frame receiving on the place where setPixmap used. And it shows that frames are coming consequently to both. But highly possible on QT repaint. Maybe it stuck on update specific label. If i set pixmap on one label, is it true, that it can affect the state of other label context ?

d_stranz
5th February 2017, 16:50
You access "frame.data" when creating your QImage. If filling this "frame" with information from your video receiving thread is not protected by a mutex or some other thread synchronization mechanism, then the contents of "frame" could be changing at the exact time that you are accessing the same data to build the image.

But like I said, describing your code in words isn't enough to get you an answer. Insisting that you're doing it right doesn't mean you are. Just because you get smooth playback most of the time doesn't mean you aren't doing something wrong, it just means that most of the time what you've done wrong doesn't affect the playback. But then it freezes.

Changing one label should not affect a different label, but since you don't show any code for that either, who knows?

__Emanuel
8th February 2017, 20:13
// mainwindow.h
std::map<std::string, std::unique_ptr<PGCapture>>captures;

// mycaptures.h
class MyCaptures : public sigc::trackable {
// all other members
sigc::signal<void, Buffer> new_frame; // i create instance of buffer in new_sample method in pipeline.cpp so that i can send frame of pipeline.cpp new_sample to prepare frame on mainwindow.cpp
Pipeline streamer; // this object does all the streaming to new_sample in pipeline.cpp. MyCapture just wrap it with additional logic
}

//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);

Gst::init(argc, argv);

MainWindow w;
w.show();

return a.exec();
}

// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(ui->managePlay, SIGNAL(pressed()), this, SLOT(open_file()));
// alot of other connections
}

void MainWindow::open_player() {
ui->stackedWidget->setCurrentWidget(ui->playerMenu);
}

void MainWindow::open_file() {
std::map<std::string, std::string> inst_settings = {
{"rtspsrc.location", "rtsp://192.xxx.x.yy:554/H.264/media.smp"}
};

this->set_capture(file, "cam00", "h264ip_appsink", inst_settings);
inst_settings["rtspsrc.location"] = "rtsp://192.xxx.x.xx:554/H.264/media.smp";
this->set_capture(file, "cam01", "h264ip_appsink", inst_settings);
}

void MainWindow::set_capture(... // settings) {
this->captures[cap_name] = std::unique_ptr<MyCapture>(new MyCapture());
this->captures[cap_name]->setup_stream(this->get_default_settings(file, cap_name, stream_config, settings));
this->captures[cap_name]->new_frame.connect(sigc::mem_fun(*this, &MainWindow::prepare_frame)); //sigc signal included to connect new frame and prepare_frame
}

// here comes buffers from all concurent streams
void MainWindow::prepare_frame(gstpg::PGBuffer buffer) {
QImage image = QImage(buffer.data, buffer.width, buffer.height, QImage::Format_RGB888);
image = image.scaledToWidth(320, Qt::SmoothTransformation);
if (!image.isNull()) {
if (buffer.id == "cam00") {
this->show_frame(buffer.id, image,0,0);
}
if (buffer.id == "cam01") {
this->show_frame(buffer.id,image,0,1);
}
}

void MainWindow::show_frame(const std::string &cap_name, const QImage & image, const int x, const int y)
{
QPixmap map = QPixmap::fromImage(image);
QLabel *pixmap = (QLabel *)ui->streamPlayerTable->cellWidget(x,y)->findChild<QLabel*>();
pixmap->setPixmap(map);
}

// pipeline.cpp

void Pipeline::set_appsink(const Glib::ustring &name)
{
g_signal_connect(appsink->gobj(), "new-sample", G_CALLBACK(&Pipeline::on_new_sample), (gpointer)this);
}

GstFlowReturn Pipeline::on_new_sample(GstAppSink* sink, gpointer gSelf)
{
auto sample = gst_app_sink_pull_sample(GST_APP_SINK(sink));
if (sample != NULL) {
auto self = reinterpret_cast<Pipeline*>(gSelf);
if (self->sample != NULL) {
gst_sample_unref(self->sample);
}
self->sample = sample;
GstBuffer* gst_buffer = gst_sample_get_buffer(self->sample);
if (gst_buffer != NULL) {
GstCaps *caps = gst_sample_get_caps(sample);
if (GST_IS_CAPS (caps) && gst_caps_is_fixed (caps)) {
GstMapInfo info;
GstStructure *structure = gst_caps_get_structure (caps, 0);

int w = 0;
int h = 0;

// // add all possible stuff here. Check if they exists
gst_structure_get_int (structure, "width", &w);
gst_structure_get_int (structure, "height", &h);

gst_buffer_map (gst_buffer, &info, GST_MAP_READ);
self->buffer.width = w;
self->buffer.height = h;
self->buffer.data = info.data;
self->buffer.id = self->settings.stream_id;

gst_buffer_unmap(gst_buffer, &info);
self->frame_formated.emit(self->buffer);
}
}
}

return GST_FLOW_OK;
}


I cant provide even more code. As a result result i have a stackview of multiple streaming labels. Each of them shows frames of separate streams. All the other story you know. I got random stalls of some window.

anda_skoa
9th February 2017, 08:57
My guess would be a threading issue.

Have you checked that MainWindow::show_frame() is executed by the main thread?

If it is not then prepare_frame() could call show_frame() via QMetaObject::invokeMethod() with Qt::QueuedConnection as the connection type.

Cheers,
_

__Emanuel
11th February 2017, 15:36
Unless i did not find the way to determine if show_frame executed in main thread, i made the way you told me to do.
I am testing 4 concurrent videos and 30min cant see any stall of any video.
And there is no strange crashes. All is smooth.

If i will got some problems, i will come back. But i hope it wont be necessary.

d_stranz && anda_skoa, i am sending virtual candies :)
Many, thanks!