From a03352353f8e2aadadaaed1d9a942ba07fce3925 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 18 Aug 2016 12:43:25 +0200 Subject: [PATCH] work on sink --- pinos/client/stream.c | 82 ++++--------- pinos/client/stream.h | 10 +- pinos/gst/gstpinossink.c | 13 +- pinos/gst/gstpinossrc.c | 15 +-- pinos/modules/spa/spa-alsa-sink.c | 194 ++++++++++-------------------- pinos/tests/test-client.c | 34 ++---- spa/plugins/alsa/alsa-sink.c | 4 + spa/plugins/alsa/alsa-utils.c | 98 ++++++++------- spa/plugins/v4l2/v4l2-utils.c | 36 +++--- 9 files changed, 180 insertions(+), 306 deletions(-) diff --git a/pinos/client/stream.c b/pinos/client/stream.c index 51bffbd7f..07303554f 100644 --- a/pinos/client/stream.c +++ b/pinos/client/stream.c @@ -123,8 +123,6 @@ enum static guint signals[LAST_SIGNAL] = { 0 }; -static void unhandle_socket (PinosStream *stream); - static void pinos_stream_get_property (GObject *_object, guint prop_id, @@ -674,15 +672,12 @@ parse_control (PinosStream *stream, case SPA_CONTROL_CMD_REMOVE_MEM: { SpaControlCmdRemoveMem p; - SpaMemory *mem; if (spa_control_iter_parse_cmd (&it, &p) < 0) break; - g_debug ("stream %p: stop", stream); - mem = spa_memory_find (&p.mem); - if (--mem->refcount == 0) - mem->notify (mem); + g_debug ("stream %p: remove mem", stream); + spa_memory_unref (&p.mem); break; } case SPA_CONTROL_CMD_ADD_BUFFER: @@ -813,23 +808,11 @@ handle_socket (PinosStream *stream, gint fd) if (priv->socket == NULL) goto socket_failed; - switch (priv->mode) { - case PINOS_STREAM_MODE_SOCKET: - g_object_notify (G_OBJECT (stream), "socket"); - break; + priv->fd = g_socket_get_fd (priv->socket); + priv->socket_source = g_socket_create_source (priv->socket, G_IO_IN, NULL); + g_source_set_callback (priv->socket_source, (GSourceFunc) on_socket_condition, stream, NULL); + g_source_attach (priv->socket_source, priv->context->priv->context); - case PINOS_STREAM_MODE_BUFFER: - { - priv->fd = g_socket_get_fd (priv->socket); - priv->socket_source = g_socket_create_source (priv->socket, G_IO_IN, NULL); - g_source_set_callback (priv->socket_source, (GSourceFunc) on_socket_condition, stream, NULL); - g_source_attach (priv->socket_source, priv->context->priv->context); - break; - } - - default: - break; - } return; /* ERRORS */ @@ -846,21 +829,9 @@ unhandle_socket (PinosStream *stream) { PinosStreamPrivate *priv = stream->priv; - switch (priv->mode) { - case PINOS_STREAM_MODE_SOCKET: - g_clear_object (&priv->socket); - g_object_notify (G_OBJECT (stream), "socket"); - break; - - case PINOS_STREAM_MODE_BUFFER: - if (priv->socket_source) { - g_source_destroy (priv->socket_source); - g_clear_pointer (&priv->socket_source, g_source_unref); - } - break; - - default: - break; + if (priv->socket_source) { + g_source_destroy (priv->socket_source); + g_clear_pointer (&priv->socket_source, g_source_unref); } } @@ -876,8 +847,8 @@ do_node_init (PinosStream *stream) control_builder_init (stream, &builder); nu.change_mask = SPA_CONTROL_CMD_NODE_UPDATE_MAX_INPUTS | SPA_CONTROL_CMD_NODE_UPDATE_MAX_OUTPUTS; - nu.max_input_ports = 1; - nu.max_output_ports = 0; + nu.max_input_ports = priv->direction == PINOS_DIRECTION_INPUT ? 1 : 0; + nu.max_output_ports = priv->direction == PINOS_DIRECTION_OUTPUT ? 1 : 0; nu.props = NULL; spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_NODE_UPDATE, &nu); @@ -964,9 +935,10 @@ on_node_created (GObject *source_object, goto fd_failed; priv->fd = fd; - g_object_unref (fd_list); + handle_socket (stream, priv->fd); + pinos_subscribe_get_proxy (context->priv->subscribe, PINOS_DBUS_SERVICE, node_path, @@ -1026,17 +998,23 @@ do_connect (PinosStream *stream) * pinos_stream_connect: * @stream: a #PinosStream * @direction: the stream direction + * @mode: a #PinosStreamMode * @port_path: the port path to connect to or %NULL to get the default port * @flags: a #PinosStreamFlags * @possible_formats: (transfer full): a #GPtrArray with possible accepted formats * * Connect @stream for input or output on @port_path. * + * When @mode is #PINOS_STREAM_MODE_BUFFER, you should connect to the new-buffer + * signal and use pinos_stream_capture_buffer() to get the latest metadata and + * data. + * * Returns: %TRUE on success. */ gboolean pinos_stream_connect (PinosStream *stream, PinosDirection direction, + PinosStreamMode mode, const gchar *port_path, PinosStreamFlags flags, GPtrArray *possible_formats) @@ -1053,6 +1031,7 @@ pinos_stream_connect (PinosStream *stream, g_return_val_if_fail (pinos_stream_get_state (stream) == PINOS_STREAM_STATE_UNCONNECTED, FALSE); priv->direction = direction; + priv->mode = mode; g_free (priv->path); priv->path = g_strdup (port_path); priv->flags = flags; @@ -1077,8 +1056,6 @@ do_start (PinosStream *stream) SpaControlCmdStateChange sc; SpaControl control; - handle_socket (stream, priv->fd); - control_builder_init (stream, &builder); sc.state = SPA_NODE_STATE_CONFIGURE; spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_STATE_CHANGE, &sc); @@ -1097,24 +1074,14 @@ do_start (PinosStream *stream) /** * pinos_stream_start: * @stream: a #PinosStream - * @format: (transfer full): a #SpaFormat with format - * @mode: a #PinosStreamMode * - * Start capturing from @stream in @format. + * Start capturing from @stream. * - * When @mode is #PINOS_STREAM_MODE_SOCKET, you should connect to the notify::socket - * signal to obtain a readable socket with metadata and data. - * - * When @mode is #PINOS_STREAM_MODE_BUFFER, you should connect to the new-buffer - * signal and use pinos_stream_capture_buffer() to get the latest metadata and - * data. * * Returns: %TRUE on success. */ gboolean -pinos_stream_start (PinosStream *stream, - SpaFormat *format, - PinosStreamMode mode) +pinos_stream_start (PinosStream *stream) { PinosStreamPrivate *priv; @@ -1123,9 +1090,6 @@ pinos_stream_start (PinosStream *stream, priv = stream->priv; g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_READY, FALSE); - priv->mode = mode; - priv->format = format; - stream_set_state (stream, PINOS_STREAM_STATE_STARTING, NULL); g_main_context_invoke (priv->context->priv->context, @@ -1193,6 +1157,8 @@ on_node_removed (GObject *source_object, g_variant_unref (ret); + unhandle_socket (stream); + stream_set_state (stream, PINOS_STREAM_STATE_UNCONNECTED, NULL); g_object_unref (stream); return; diff --git a/pinos/client/stream.h b/pinos/client/stream.h index 87d50d603..d9a1eae9d 100644 --- a/pinos/client/stream.h +++ b/pinos/client/stream.h @@ -60,9 +60,8 @@ typedef enum { } PinosStreamFlags; typedef enum { - PINOS_STREAM_MODE_SOCKET = 0, - PINOS_STREAM_MODE_BUFFER = 1, - PINOS_STREAM_MODE_RINGBUFFER = 2, + PINOS_STREAM_MODE_BUFFER = 0, + PINOS_STREAM_MODE_RINGBUFFER = 1, } PinosStreamMode; /** @@ -98,14 +97,13 @@ const GError * pinos_stream_get_error (PinosStream *stream); gboolean pinos_stream_connect (PinosStream *stream, PinosDirection direction, + PinosStreamMode mode, const gchar *port_path, PinosStreamFlags flags, GPtrArray *possible_formats); gboolean pinos_stream_disconnect (PinosStream *stream); -gboolean pinos_stream_start (PinosStream *stream, - SpaFormat *format, - PinosStreamMode mode); +gboolean pinos_stream_start (PinosStream *stream); gboolean pinos_stream_stop (PinosStream *stream); SpaBuffer * pinos_stream_peek_buffer (PinosStream *stream); diff --git a/pinos/gst/gstpinossink.c b/pinos/gst/gstpinossink.c index 928a5a2a0..2b464b719 100644 --- a/pinos/gst/gstpinossink.c +++ b/pinos/gst/gstpinossink.c @@ -414,16 +414,12 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) { GstPinosSink *pinossink; GPtrArray *possible; - SpaFormat *format; PinosStreamState state; gboolean res = FALSE; pinossink = GST_PINOS_SINK (bsink); - format = gst_caps_to_format (caps, 0); - possible = g_ptr_array_new (); - spa_format_ref (format); - g_ptr_array_insert (possible, -1, format); + possible = gst_caps_to_format_all (caps); pinos_main_loop_lock (pinossink->loop); state = pinos_stream_get_state (pinossink->stream); @@ -439,6 +435,7 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) pinos_stream_connect (pinossink->stream, PINOS_DIRECTION_OUTPUT, + PINOS_STREAM_MODE_BUFFER, pinossink->path, flags, possible); @@ -457,9 +454,7 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) } if (state != PINOS_STREAM_STATE_STREAMING) { - res = pinos_stream_start (pinossink->stream, - format, - PINOS_STREAM_MODE_BUFFER); + res = pinos_stream_start (pinossink->stream); while (TRUE) { state = pinos_stream_get_state (pinossink->stream); @@ -483,7 +478,7 @@ start_error: { GST_ERROR ("could not start stream"); pinos_main_loop_unlock (pinossink->loop); - spa_format_unref (format); + g_ptr_array_unref (possible); return FALSE; } } diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index 41eeae503..ed5d8c2ef 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -484,19 +484,14 @@ parse_stream_properties (GstPinosSrc *pinossrc, PinosProperties *props) static gboolean -gst_pinos_src_stream_start (GstPinosSrc *pinossrc, GstCaps * caps) +gst_pinos_src_stream_start (GstPinosSrc *pinossrc) { SpaFormat *format; gboolean res; PinosProperties *props; - if (caps) - format = gst_caps_to_format (caps, 0); - else - format = NULL; - pinos_main_loop_lock (pinossrc->loop); - res = pinos_stream_start (pinossrc->stream, format, PINOS_STREAM_MODE_BUFFER); + res = pinos_stream_start (pinossrc->stream); while (TRUE) { PinosStreamState state = pinos_stream_get_state (pinossrc->stream); @@ -515,8 +510,9 @@ gst_pinos_src_stream_start (GstPinosSrc *pinossrc, GstCaps * caps) pinos_main_loop_unlock (pinossrc->loop); if (format) { - caps = gst_caps_from_format (format); + GstCaps *caps = gst_caps_from_format (format); gst_base_src_set_caps (GST_BASE_SRC (pinossrc), caps); + gst_caps_unref (caps); spa_format_unref (format); } @@ -609,6 +605,7 @@ gst_pinos_src_negotiate (GstBaseSrc * basesrc) GST_DEBUG_OBJECT (basesrc, "connect capture with path %s", pinossrc->path); pinos_stream_connect (pinossrc->stream, PINOS_DIRECTION_INPUT, + PINOS_STREAM_MODE_BUFFER, pinossrc->path, PINOS_STREAM_FLAG_AUTOCONNECT, possible); @@ -626,7 +623,7 @@ gst_pinos_src_negotiate (GstBaseSrc * basesrc) } pinos_main_loop_unlock (pinossrc->loop); - result = gst_pinos_src_stream_start (pinossrc, NULL); + result = gst_pinos_src_stream_start (pinossrc); pinossrc->negotiated = result; diff --git a/pinos/modules/spa/spa-alsa-sink.c b/pinos/modules/spa/spa-alsa-sink.c index 7dc72c574..91bb66381 100644 --- a/pinos/modules/spa/spa-alsa-sink.c +++ b/pinos/modules/spa/spa-alsa-sink.c @@ -53,8 +53,6 @@ typedef struct { struct _PinosSpaAlsaSinkPrivate { - SpaNode *sink; - PinosProperties *props; PinosRingbuffer *ringbuffer; @@ -121,6 +119,39 @@ make_node (SpaNode **node, const char *lib, const char *name) return SPA_RESULT_ERROR; } +static void * +loop (void *user_data) +{ + PinosSpaAlsaSink *this = user_data; + PinosSpaAlsaSinkPrivate *priv = this->priv; + int r; + + g_debug ("spa-alsa-sink %p: enter thread", this); + while (priv->running) { + SpaPollNotifyData ndata; + + r = poll ((struct pollfd *) priv->fds, priv->n_fds, -1); + if (r < 0) { + if (errno == EINTR) + continue; + break; + } + if (r == 0) { + g_debug ("spa-alsa-sink %p: select timeout", this); + break; + } + if (priv->poll.after_cb) { + ndata.fds = priv->poll.fds; + ndata.n_fds = priv->poll.n_fds; + ndata.user_data = priv->poll.user_data; + priv->poll.after_cb (&ndata); + } + } + g_debug ("spa-alsa-sink %p: leave thread", this); + + return NULL; +} + static void on_sink_event (SpaNode *node, SpaEvent *event, void *user_data) { @@ -167,7 +198,7 @@ on_sink_event (SpaNode *node, SpaEvent *event, void *user_data) iinfo.size = total; g_debug ("push sink %d", iinfo.buffer_id); - if ((res = spa_node_port_push_input (priv->sink, 1, &iinfo)) < 0) + if ((res = spa_node_port_push_input (node, 1, &iinfo)) < 0) g_debug ("got error %d", res); break; } @@ -175,12 +206,21 @@ on_sink_event (SpaNode *node, SpaEvent *event, void *user_data) case SPA_EVENT_TYPE_ADD_POLL: { SpaPollItem *poll = event->data; + int err; g_debug ("add poll"); priv->poll = *poll; priv->fds[0] = poll->fds[0]; priv->n_fds = 1; priv->poll.fds = priv->fds; + + if (!priv->running) { + priv->running = true; + if ((err = pthread_create (&priv->thread, NULL, loop, this)) != 0) { + g_debug ("spa-v4l2-source %p: can't create thread", strerror (err)); + priv->running = false; + } + } break; } @@ -191,20 +231,16 @@ on_sink_event (SpaNode *node, SpaEvent *event, void *user_data) } static void -create_pipeline (PinosSpaAlsaSink *this) +setup_node (PinosSpaAlsaSink *this) { - PinosSpaAlsaSinkPrivate *priv = this->priv; + PinosNode *node = PINOS_NODE (this); SpaResult res; SpaProps *props; SpaPropValue value; - if ((res = make_node (&priv->sink, "spa/build/plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) { - g_error ("can't create alsa-sink: %d", res); - return; - } - spa_node_set_event_callback (priv->sink, on_sink_event, this); + spa_node_set_event_callback (node->node, on_sink_event, this); - if ((res = spa_node_get_props (priv->sink, &props)) < 0) + if ((res = spa_node_get_props (node->node, &props)) < 0) g_debug ("got get_props error %d", res); value.type = SPA_PROP_TYPE_STRING; @@ -212,67 +248,14 @@ create_pipeline (PinosSpaAlsaSink *this) value.size = strlen (value.value)+1; spa_props_set_prop (props, spa_props_index_for_name (props, "device"), &value); - if ((res = spa_node_set_props (priv->sink, props)) < 0) + if ((res = spa_node_set_props (node->node, props)) < 0) g_debug ("got set_props error %d", res); } -static void * -loop (void *user_data) -{ - PinosSpaAlsaSink *this = user_data; - PinosSpaAlsaSinkPrivate *priv = this->priv; - int r; - - g_debug ("spa-alsa-sink %p: enter thread", this); - while (priv->running) { - SpaPollNotifyData ndata; - - r = poll ((struct pollfd *) priv->fds, priv->n_fds, -1); - if (r < 0) { - if (errno == EINTR) - continue; - break; - } - if (r == 0) { - g_debug ("spa-alsa-sink %p: select timeout", this); - break; - } - if (priv->poll.after_cb) { - ndata.fds = priv->poll.fds; - ndata.n_fds = priv->poll.n_fds; - ndata.user_data = priv->poll.user_data; - priv->poll.after_cb (&ndata); - } - } - g_debug ("spa-alsa-sink %p: leave thread", this); - - return NULL; -} - -static void -start_pipeline (PinosSpaAlsaSink *sink) -{ - PinosSpaAlsaSinkPrivate *priv = sink->priv; - SpaResult res; - SpaCommand cmd; - int err; - - g_debug ("spa-alsa-sink %p: starting pipeline", sink); - - cmd.type = SPA_COMMAND_START; - if ((res = spa_node_send_command (priv->sink, &cmd)) < 0) - g_debug ("got error %d", res); - - priv->running = true; - if ((err = pthread_create (&priv->thread, NULL, loop, sink)) != 0) { - g_debug ("spa-v4l2-source %p: can't create thread", strerror (err)); - priv->running = false; - } -} - static void stop_pipeline (PinosSpaAlsaSink *sink) { + PinosNode *node = PINOS_NODE (sink); PinosSpaAlsaSinkPrivate *priv = sink->priv; SpaResult res; SpaCommand cmd; @@ -285,7 +268,7 @@ stop_pipeline (PinosSpaAlsaSink *sink) } cmd.type = SPA_COMMAND_STOP; - if ((res = spa_node_send_command (priv->sink, &cmd)) < 0) + if ((res = spa_node_send_command (node->node, &cmd)) < 0) g_debug ("got error %d", res); } @@ -315,7 +298,6 @@ set_state (PinosNode *node, break; case PINOS_NODE_STATE_RUNNING: - start_pipeline (this); break; case PINOS_NODE_STATE_ERROR: @@ -378,47 +360,6 @@ free_sink_port_data (SinkPortData *data) g_slice_free (SinkPortData, data); } -static SpaResult -negotiate_formats (PinosSpaAlsaSink *this) -{ - PinosSpaAlsaSinkPrivate *priv = this->priv; - SpaResult res; - SpaFormat *format; - SpaProps *props; - uint32_t val; - SpaPropValue value; - void *state = NULL; - - if ((res = spa_node_port_enum_formats (priv->sink, 0, &format, NULL, &state)) < 0) - return res; - - props = &format->props; - - value.type = SPA_PROP_TYPE_UINT32; - value.size = sizeof (uint32_t); - value.value = &val; - - val = SPA_AUDIO_FORMAT_S16LE; - if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_FORMAT), &value)) < 0) - return res; - val = SPA_AUDIO_LAYOUT_INTERLEAVED; - if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_LAYOUT), &value)) < 0) - return res; - val = 44100; - if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_RATE), &value)) < 0) - return res; - val = 2; - if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0) - return res; - - if ((res = spa_node_port_set_format (priv->sink, 0, 0, format)) < 0) - return res; - - priv->ringbuffer = pinos_ringbuffer_new (PINOS_RINGBUFFER_MODE_READ, 64 * 1024); - - return SPA_RESULT_OK; -} - static void free_mem_block (MemBlock *b) { @@ -475,23 +416,6 @@ on_received_event (PinosPort *port, return TRUE; } -static void -on_format_change (GObject *obj, - GParamSpec *pspec, - gpointer user_data) -{ - SinkPortData *data = user_data; - PinosNode *node = PINOS_NODE (data->sink); - PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node); - GBytes *formats; - - g_object_get (obj, "format", &formats, NULL); - if (formats) { - g_debug ("port %p: format change %s", obj, (gchar*) g_bytes_get_data (formats, NULL)); - negotiate_formats (sink); - } -} - static PinosPort * add_port (PinosNode *node, PinosDirection direction, @@ -501,7 +425,6 @@ add_port (PinosNode *node, PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node); PinosSpaAlsaSinkPrivate *priv = sink->priv; SinkPortData *data; - GBytes *formats; data = g_slice_new0 (SinkPortData); data->sink = sink; @@ -511,15 +434,10 @@ add_port (PinosNode *node, pinos_port_set_received_cb (data->port, on_received_buffer, on_received_event, sink, NULL); - formats = g_bytes_new ("ANY", strlen ("ANY") + 1); - g_object_set (data->port, "possible-formats", formats, NULL); - g_debug ("connecting signals"); g_signal_connect (data->port, "activate", (GCallback) on_activate, data); g_signal_connect (data->port, "deactivate", (GCallback) on_deactivate, data); - g_signal_connect (data->port, "notify::format", (GCallback) on_format_change, data); - priv->ports = g_list_append (priv->ports, data); return data->port; @@ -553,7 +471,7 @@ sink_constructed (GObject * object) { PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object); - create_pipeline (sink); + setup_node (sink); G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->constructed (object); } @@ -605,11 +523,21 @@ pinos_spa_alsa_sink_new (PinosDaemon *daemon, PinosProperties *properties) { PinosNode *node; + SpaNode *n; + SpaResult res; + + if ((res = make_node (&n, + "spa/build/plugins/alsa/libspa-alsa.so", + "alsa-sink")) < 0) { + g_error ("can't create v4l2-source: %d", res); + return NULL; + } node = g_object_new (PINOS_TYPE_SPA_ALSA_SINK, "daemon", daemon, "name", name, "properties", properties, + "node", n, NULL); return node; diff --git a/pinos/tests/test-client.c b/pinos/tests/test-client.c index 570dfc329..556111495 100644 --- a/pinos/tests/test-client.c +++ b/pinos/tests/test-client.c @@ -80,33 +80,8 @@ on_stream_notify (GObject *gobject, break; case PINOS_STREAM_STATE_READY: - { - GPtrArray *possible; - SpaFormat *format; - - g_object_get (s, "possible-formats", &possible, NULL); - - format = g_ptr_array_index (possible, 0); - -#if 0 - /* set some reasonable defaults */ - if (gst_structure_has_field (structure, "width")) - gst_structure_fixate_field_nearest_int (structure, "width", 320); - if (gst_structure_has_field (structure, "height")) - gst_structure_fixate_field_nearest_int (structure, "height", 240); - if (gst_structure_has_field (structure, "framerate")) - gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1); - - /* use random fixation otherwise */ - caps = gst_caps_fixate (caps); - str = gst_caps_to_string (caps); - gst_caps_unref (caps); - format = g_bytes_new_static (str, strlen (str) + 1); -#endif - - pinos_stream_start (s, format, PINOS_STREAM_MODE_SOCKET); + pinos_stream_start (s); break; - } case PINOS_STREAM_STATE_STREAMING: break; @@ -141,7 +116,12 @@ on_state_notify (GObject *gobject, g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream); possible = NULL; - pinos_stream_connect (stream, PINOS_DIRECTION_OUTPUT, NULL, 0, possible); + pinos_stream_connect (stream, + PINOS_DIRECTION_OUTPUT, + PINOS_STREAM_MODE_BUFFER, + NULL, + 0, + possible); break; } default: diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index 9177ceefc..a47072934 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -52,6 +52,7 @@ reset_alsa_sink_props (SpaALSASinkProps *props) typedef struct { bool opened; + bool have_buffers; snd_pcm_t *handle; snd_output_t *output; snd_pcm_sframes_t buffer_size; @@ -391,6 +392,9 @@ spa_alsa_sink_node_port_set_format (SpaNode *node, if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0) return res; + if (alsa_set_format (this, &this->current_format, false) < 0) + return SPA_RESULT_ERROR; + this->have_format = true; return SPA_RESULT_OK; diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 520f92846..b6793713b 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -11,6 +11,48 @@ static int verbose = 0; /* verbose flag */ #define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; } +static int +spa_alsa_open (SpaALSASink *this) +{ + SpaALSAState *state = &this->state; + int err; + SpaALSASinkProps *props = &this->props[1]; + + if (state->opened) + return 0; + + CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed"); + + printf ("Playback device is '%s'\n", props->device); + CHECK (snd_pcm_open (&state->handle, + props->device, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK | + SND_PCM_NO_AUTO_RESAMPLE | + SND_PCM_NO_AUTO_CHANNELS | + SND_PCM_NO_AUTO_FORMAT), "open failed"); + + state->opened = true; + + return 0; +} + +static int +spa_alsa_close (SpaALSASink *this) +{ + SpaALSAState *state = &this->state; + int err = 0; + + if (!state->opened) + return 0; + + CHECK (snd_pcm_close (state->handle), "close failed"); + + state->opened = false; + + return err; +} + static snd_pcm_format_t spa_alsa_format_to_alsa (SpaAudioFormat format) { @@ -63,7 +105,7 @@ spa_alsa_format_to_alsa (SpaAudioFormat format) } static int -set_hwparams (SpaALSASink *this) +alsa_set_format (SpaALSASink *this, SpaAudioRawFormat *fmt, bool try_only) { unsigned int rrate; snd_pcm_uframes_t size; @@ -71,13 +113,17 @@ set_hwparams (SpaALSASink *this) snd_pcm_hw_params_t *params; snd_pcm_format_t format; SpaALSAState *state = &this->state; - SpaAudioRawFormat *fmt = &this->current_format; SpaAudioRawInfo *info = &fmt->info; - snd_pcm_t *handle = state->handle; + snd_pcm_t *handle; unsigned int buffer_time; unsigned int period_time; SpaALSASinkProps *props = &this->props[1]; + if ((err = spa_alsa_open (this)) < 0) + return err; + + handle = state->handle; + snd_pcm_hw_params_alloca (¶ms); /* choose all parameters */ CHECK (snd_pcm_hw_params_any (handle, params), "Broken configuration for playback: no configurations available"); @@ -175,32 +221,6 @@ xrun_recovery (snd_pcm_t *handle, int err) return err; } -static int -spa_alsa_open (SpaALSASink *this) -{ - SpaALSAState *state = &this->state; - int err; - SpaALSASinkProps *props = &this->props[1]; - - if (state->opened) - return 0; - - CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed"); - - printf ("Playback device is '%s'\n", props->device); - CHECK (snd_pcm_open (&state->handle, - props->device, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK | - SND_PCM_NO_AUTO_RESAMPLE | - SND_PCM_NO_AUTO_CHANNELS | - SND_PCM_NO_AUTO_FORMAT), "open failed"); - - state->opened = true; - - return 0; -} - static void pull_input (SpaALSASink *this, void *data, snd_pcm_uframes_t frames) { @@ -303,22 +323,6 @@ alsa_on_fd_events (SpaPollNotifyData *data) return 0; } -static int -spa_alsa_close (SpaALSASink *this) -{ - SpaALSAState *state = &this->state; - int err = 0; - - if (!state->opened) - return 0; - - CHECK (snd_pcm_close (state->handle), "close failed"); - - state->opened = false; - - return err; -} - static int spa_alsa_start (SpaALSASink *this) { @@ -329,7 +333,9 @@ spa_alsa_start (SpaALSASink *this) if (spa_alsa_open (this) < 0) return -1; - CHECK (set_hwparams (this), "hwparams"); + if (!state->have_buffers) + return -1; + CHECK (set_swparams (this), "swparams"); snd_pcm_dump (state->handle, state->output); diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 071ef67cd..fa0af2431 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -66,6 +66,24 @@ spa_v4l2_open (SpaV4l2Source *this) return 0; } +static int +spa_v4l2_close (SpaV4l2Source *this) +{ + SpaV4l2State *state = &this->state[0]; + + if (!state->opened) + return 0; + + fprintf (stderr, "close\n"); + if (close(state->fd)) + perror ("close"); + + state->fd = -1; + state->opened = false; + + return 0; +} + typedef struct { uint32_t fourcc; SpaVideoFormat format; @@ -418,24 +436,6 @@ spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only) return 0; } -static int -spa_v4l2_close (SpaV4l2Source *this) -{ - SpaV4l2State *state = &this->state[0]; - - if (!state->opened) - return 0; - - fprintf (stderr, "close\n"); - if (close(state->fd)) - perror ("close"); - - state->fd = -1; - state->opened = false; - - return 0; -} - static SpaResult mmap_read (SpaV4l2Source *this) {