I need to call SDL_UpdateYUVTexture from a thread, the manual says that it only has to be called from the main thread.
Does anyone know why the manual says this and if it is possible to bypass this limitation?
It says this because many renderers are not thread safe. You should instead create a queue of texture updates and process that queue in the main thread. You can use SDL_RunOnMainThread() to make this easier.
I had a similar need.
I had a gstreamer pipeline launched (by default runs in it’s own thread) and was grabbing the frame buffers in a different context (appsink sample callback).
The was I solved it was by wrapping the gst_app_sink_pull_sample (that gets the frame sample data) in a mutex, and keep the reference to that sample.
Then, at rendering stage, I again, wrap the access to the sample with a mutex and call the SDL_UpdateYUVTexture().
Not sure if this is the right way to do it, but it seems to work fine.
I can play 16 x H264 streams with no issues.
static GstFlowReturn new_sample_cb (GstAppSink *appsink, gpointer data)
{
Viewport* vwp = data;
...
g_mutex_lock (&vwp->new_sample_mutex);
clear_last_sample_if_exists(vwp);
vwp->frame_cb_latest_sample = gst_app_sink_pull_sample (appsink);
...
g_mutex_unlock (&vwp->new_sample_mutex);
return GST_FLOW_OK;
}
Then, in the rendering loop:
if (viewport->frame_cb_latest_sample != NULL) {
g_mutex_lock (&viewport->new_sample_mutex);
buf = gst_sample_get_buffer (viewport->frame_cb_latest_sample);
g_assert_nonnull (buf);
caps = gst_sample_get_caps (viewport->frame_cb_latest_sample);
g_assert_nonnull (caps);
g_assert (gst_video_info_from_caps (&v_info, caps));
g_assert (gst_video_frame_map (&v_frame, &v_info, buf, GST_MAP_READ));
gst_video_frame_unmap (&v_frame);
... // get frame pixel data
if (SDL_UpdateYUVTexture(viewport->texture, NULL, pixels_y, stride_y, pixels_u, stride_u, pixels_v, stride_v)) {
g_warning("Failed to update texture: %s\n", SDL_GetError());
}
if (SDL_RenderCopy (renderer, viewport->texture, NULL, &vwprect)) {
g_warning("Failed to copy texture: %s\n", SDL_GetError());
}
gst_video_frame_unmap (&v_frame);
g_mutex_unlock (&viewport->new_sample_mutex);
}
Thanks for the answers.
After a lot of testing, I found that this wasn’t the problem; now it’s working correctly.
I run ffmpeg in multiple threads, each updating a texture with SDL_UpdateYUVTexture.
When the main thread decides to draw on the screen, it simply draws with that texture.
On one machine, it worked fine; on another, it didn’t.
I found that adding a critical section in the library initialization and enabling software rendering on the machine that wasn’t working worked like a charm.
Slouken gave me the clue