2 * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
4 * gstpluginloader.c: GstPluginLoader helper for loading plugin files
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
28 #include <sys/types.h>
34 #include <gst/gst_private.h>
35 #include <gst/gstconfig.h>
37 #include <gst/gstpoll.h>
38 #include <gst/gstutils.h>
40 #include <gst/gstpluginloader.h>
41 #include <gst/gstregistrychunks.h>
43 #define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
45 static GstPluginLoader *plugin_loader_new (GstRegistry * registry);
46 static gboolean plugin_loader_free (GstPluginLoader * loader);
47 static gboolean plugin_loader_load (GstPluginLoader * loader,
48 const gchar * filename);
50 const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
51 plugin_loader_new, plugin_loader_free, plugin_loader_load
54 struct _GstPluginLoader
56 GstRegistry *registry;
59 gboolean child_started;
65 gboolean got_plugin_details;
81 #define PACKET_LOAD_PLUGIN 2
82 #define PACKET_STARTING_LOAD 3
83 #define PACKET_PLUGIN_DETAILS 4
85 #define BUF_INIT_SIZE 512
86 #define BUF_GROW_EXTRA 512
88 #define ALIGNMENT (sizeof (void *))
91 static gboolean gst_plugin_loader_spawn (GstPluginLoader * loader);
92 static void put_packet (GstPluginLoader * loader, guint type, guint32 tag,
93 const guint8 * payload, guint32 payload_len);
94 static gboolean exchange_packets (GstPluginLoader * l);
96 static GstPluginLoader *
97 plugin_loader_new (GstRegistry * registry)
99 GstPluginLoader *l = g_new0 (GstPluginLoader, 1);
102 l->registry = gst_object_ref (registry);
103 l->fdset = gst_poll_new (FALSE);
104 gst_poll_fd_init (&l->fd_w);
105 gst_poll_fd_init (&l->fd_r);
107 l->tx_buf_size = BUF_INIT_SIZE;
108 l->tx_buf = g_malloc (BUF_INIT_SIZE);
112 l->rx_buf_size = BUF_INIT_SIZE;
113 l->rx_buf = g_malloc (BUF_INIT_SIZE);
119 plugin_loader_free (GstPluginLoader * loader)
121 gboolean got_plugin_details;
123 fsync (loader->fd_w.fd);
125 if (loader->child_started) {
126 put_packet (loader, PACKET_EXIT, 0, NULL, 0);
128 /* Swap packets with the child until it exits */
129 while (!loader->rx_done && exchange_packets (loader)) {
132 close (loader->fd_w.fd);
133 close (loader->fd_r.fd);
136 GST_LOG ("waiting for child process to exit");
137 waitpid (loader->child_pid, NULL, 0);
139 g_warning ("FIXME: Implement child process shutdown for Win32");
141 g_spawn_close_pid (loader->child_pid);
143 close (loader->fd_w.fd);
144 close (loader->fd_r.fd);
147 gst_poll_free (loader->fdset);
149 g_free (loader->rx_buf);
150 g_free (loader->tx_buf);
152 if (loader->registry)
153 gst_object_unref (loader->registry);
155 got_plugin_details = loader->got_plugin_details;
159 return got_plugin_details;
163 plugin_loader_load (GstPluginLoader * loader, const gchar * filename)
165 if (!loader->child_started) {
166 if (!gst_plugin_loader_spawn (loader))
170 /* Send a packet to the child requesting that it load the given file */
171 GST_LOG_OBJECT (loader->registry,
172 "Sending file %s to child. tag %u", filename, loader->next_tag);
174 put_packet (loader, PACKET_LOAD_PLUGIN, loader->next_tag,
175 (guint8 *) filename, strlen (filename) + 1);
179 if (!exchange_packets (loader))
186 gst_plugin_loader_spawn (GstPluginLoader * loader)
189 "/home/jan/devel/gstreamer/head/gstreamer/libs/gst/helpers/plugin-scanner";
190 char *argv[] = { helper_bin, "-l", NULL };
192 if (!g_spawn_async_with_pipes (NULL, argv, NULL,
193 G_SPAWN_DO_NOT_REAP_CHILD /* | G_SPAWN_STDERR_TO_DEV_NULL */ ,
194 NULL, NULL, &loader->child_pid, &loader->fd_w.fd, &loader->fd_r.fd,
198 gst_poll_add_fd (loader->fdset, &loader->fd_w);
200 gst_poll_add_fd (loader->fdset, &loader->fd_r);
201 gst_poll_fd_ctl_read (loader->fdset, &loader->fd_r, TRUE);
203 loader->child_started = TRUE;
209 _gst_plugin_loader_client_run ()
213 l = plugin_loader_new (NULL);
217 l->fd_w.fd = 1; /* STDOUT */
218 gst_poll_add_fd (l->fdset, &l->fd_w);
220 l->fd_r.fd = 0; /* STDIN */
221 gst_poll_add_fd (l->fdset, &l->fd_r);
222 gst_poll_fd_ctl_read (l->fdset, &l->fd_r, TRUE);
226 GST_DEBUG ("Plugin scanner child running. Waiting for instructions");
228 /* Loop, listening for incoming packets on the fd and writing responses */
229 while (!l->rx_done && exchange_packets (l));
231 plugin_loader_free (l);
237 put_packet (GstPluginLoader * l, guint type, guint32 tag,
238 const guint8 * payload, guint32 payload_len)
241 guint len = payload_len + HEADER_SIZE;
243 if (l->tx_buf_write + len >= l->tx_buf_size) {
244 l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
245 l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
248 out = l->tx_buf + l->tx_buf_write;
251 GST_WRITE_UINT24_BE (out + 1, tag);
252 GST_WRITE_UINT32_BE (out + 4, payload_len);
253 memcpy (out + HEADER_SIZE, payload, payload_len);
255 l->tx_buf_write += len;
256 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
260 put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
266 /* Might need to align the chunk */
267 if (chunk->align && ((*pos) % ALIGNMENT) != 0)
268 padsize = ALIGNMENT - ((*pos) % ALIGNMENT);
270 len = padsize + chunk->size;
272 if (l->tx_buf_write + len >= l->tx_buf_size) {
273 l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
274 l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
277 out = l->tx_buf + l->tx_buf_write;
278 memcpy (out + padsize, chunk->data, chunk->size);
280 l->tx_buf_write += len;
283 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
287 write_one (GstPluginLoader * l)
293 if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
296 out = l->tx_buf + l->tx_buf_read;
297 to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
298 l->tx_buf_read += to_write;
300 GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);
303 res = write (l->fd_w.fd, out, to_write);
308 } while (to_write > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
310 if (l->tx_buf_read == l->tx_buf_write) {
311 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
312 l->tx_buf_read = l->tx_buf_write = 0;
318 do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
320 GstPlugin *newplugin;
321 GList *chunks = NULL;
323 GST_DEBUG ("Plugin scanner loading file %s. tag %u\n", filename, tag);
324 put_packet (l, PACKET_STARTING_LOAD, tag, NULL, 0);
326 newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
331 /* Now serialise the plugin details and send */
332 if (!_priv_gst_registry_chunks_save_plugin (&chunks,
333 gst_registry_get_default (), newplugin))
336 /* Store where the header is, write an empty one, then write
337 * all the payload chunks, then fix up the header size */
338 hdr_pos = l->tx_buf_write;
339 offset = HEADER_SIZE;
340 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
344 for (walk = chunks; walk; walk = g_list_next (walk)) {
345 GstRegistryChunk *cur = walk->data;
346 put_chunk (l, cur, &offset);
348 if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
353 g_list_free (chunks);
355 /* Store the size of the written payload */
356 GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
359 gst_object_unref (newplugin);
361 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
366 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
369 for (walk = chunks; walk; walk = g_list_next (walk)) {
370 GstRegistryChunk *cur = walk->data;
372 if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
377 g_list_free (chunks);
384 handle_rx_packet (GstPluginLoader * l,
385 guint pack_type, guint32 tag, guint8 * payload, guint payload_len)
391 gst_poll_fd_ctl_read (l->fdset, &l->fd_r, FALSE);
393 /* Respond, then we keep looping until the parent closes the fd */
394 put_packet (l, PACKET_EXIT, 0, NULL, 0);
396 l->rx_done = TRUE; /* All done reading from child */
399 case PACKET_LOAD_PLUGIN:{
404 /* Payload is the filename to load */
405 res = do_plugin_load (l, (gchar *) payload, tag);
409 case PACKET_STARTING_LOAD:
410 GST_LOG_OBJECT (l->registry,
411 "child started loading plugin w/ tag %u", tag);
413 case PACKET_PLUGIN_DETAILS:{
414 gchar *tmp = (gchar *) payload;
416 GST_DEBUG_OBJECT (l->registry,
417 "child loaded plugin w/ tag %u. %d bytes info", tag, payload_len);
419 if (payload_len > 0) {
420 GstPlugin *newplugin;
421 _priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
422 tmp + payload_len, &newplugin);
423 newplugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
424 GST_LOG_OBJECT (l->registry,
425 "marking plugin %p as registered as %s", newplugin,
426 newplugin->filename);
427 newplugin->registered = TRUE;
429 /* We got a set of plugin details - remember it for later */
430 l->got_plugin_details = TRUE;
436 return FALSE; /* Invalid packet -> something is wrong */
443 read_one (GstPluginLoader * l)
445 guint32 to_read, packet_len, tag;
449 to_read = HEADER_SIZE;
452 res = read (l->fd_r.fd, in, to_read);
457 } while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
460 GST_LOG ("Failed reading packet header");
464 packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
466 if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
467 l->rx_buf_size = packet_len + HEADER_SIZE + BUF_GROW_EXTRA;
468 l->rx_buf = g_realloc (l->rx_buf, l->rx_buf_size);
471 in = l->rx_buf + HEADER_SIZE;
472 to_read = packet_len;
474 res = read (l->fd_r.fd, in, to_read);
479 } while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
482 GST_ERROR ("Packet payload read failed");
486 tag = GST_READ_UINT24_BE (l->rx_buf + 1);
488 return handle_rx_packet (l, l->rx_buf[0], tag,
489 l->rx_buf + HEADER_SIZE, packet_len);
493 exchange_packets (GstPluginLoader * l)
497 /* Wait for activity on our FDs */
500 res = gst_poll_wait (l->fdset, GST_CLOCK_TIME_NONE);
501 } while (res == -1 && (errno == EINTR || errno == EAGAIN));
506 GST_DEBUG ("Poll res = %d. %d bytes pending for write", res,
507 l->tx_buf_write - l->tx_buf_read);
510 if (gst_poll_fd_has_error (l->fdset, &l->fd_r) ||
511 gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
512 GST_LOG ("read fd %d closed/errored", l->fd_r.fd);
516 if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
522 if (l->tx_buf_read < l->tx_buf_write) {
523 if (gst_poll_fd_has_error (l->fdset, &l->fd_w) ||
524 gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
525 GST_ERROR ("write fd %d closed/errored", l->fd_w.fd);
528 if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
533 } while (l->tx_buf_read < l->tx_buf_write);