diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index cf39f4e73..25d677d0b 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -854,7 +854,11 @@ gst_pinos_src_create (GstPushSrc * psrc, GstBuffer ** buffer) } pinos_main_loop_unlock (pinossrc->loop); - base_time = GST_ELEMENT_CAST (psrc)->base_time; + if (pinossrc->is_live) + base_time = GST_ELEMENT_CAST (psrc)->base_time; + else + base_time = 0; + pts = GST_BUFFER_PTS (*buffer); dts = GST_BUFFER_DTS (*buffer); diff --git a/pinos/server/link.c b/pinos/server/link.c index eb2eb9ae9..572d2d596 100644 --- a/pinos/server/link.c +++ b/pinos/server/link.c @@ -327,7 +327,10 @@ again: if (out_state == SPA_NODE_STATE_CONFIGURE) { g_debug ("link %p: doing set format on output", this); - if ((res = spa_node_port_set_format (this->output_node->node, this->output_port, 0, format)) < 0) { + if ((res = spa_node_port_set_format (this->output_node->node, + this->output_port, + SPA_PORT_FORMAT_FLAG_NEAREST, + format)) < 0) { g_set_error (&error, PINOS_ERROR, PINOS_ERROR_FORMAT_NEGOTIATION, @@ -336,7 +339,10 @@ again: } } else if (in_state == SPA_NODE_STATE_CONFIGURE) { g_debug ("link %p: doing set format on input", this); - if ((res = spa_node_port_set_format (this->input_node->node, this->input_port, 0, format)) < 0) { + if ((res = spa_node_port_set_format (this->input_node->node, + this->input_port, + SPA_PORT_FORMAT_FLAG_NEAREST, + format)) < 0) { g_set_error (&error, PINOS_ERROR, PINOS_ERROR_FORMAT_NEGOTIATION, diff --git a/spa/include/spa/defs.h b/spa/include/spa/defs.h index c9c7d2be9..beb1bc16f 100644 --- a/spa/include/spa/defs.h +++ b/spa/include/spa/defs.h @@ -76,10 +76,17 @@ typedef void (*SpaNotify) (void *data); #define SPA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) #define SPA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) -#define SPA_TIME_INVALID ((uint64_t)-1) +#define SPA_TIME_INVALID ((int64_t)INT64_MIN) #define SPA_IDX_INVALID ((unsigned int)-1) #define SPA_ID_INVALID ((uint32_t)0xffffffff) +#define SPA_NSEC_PER_SEC (1000000000ll) +#define SPA_USEC_PER_SEC (1000000ll) +#define SPA_MSEC_PER_SEC (1000ll) + +#define SPA_TIMESPEC_TO_TIME(ts) ((ts)->tv_sec * SPA_NSEC_PER_SEC + (ts)->tv_nsec) +#define SPA_TIMEVAL_TO_TIME(tv) ((tv)->tv_sec * SPA_NSEC_PER_SEC + (tv)->tv_usec * 1000ll) + #ifdef __cplusplus } /* extern "C" */ diff --git a/spa/lib/audio-raw.c b/spa/lib/audio-raw.c index 51acfb06f..73ee1e6bb 100644 --- a/spa/lib/audio-raw.c +++ b/spa/lib/audio-raw.c @@ -223,7 +223,7 @@ spa_format_audio_init (SpaMediaType type, SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FLAG_NONE, SPA_AUDIO_LAYOUT_INTERLEAVED, - 32000, + 44100, 2, 0 }; diff --git a/spa/plugins/alsa/alsa-monitor.c b/spa/plugins/alsa/alsa-monitor.c new file mode 100644 index 000000000..cc30e5951 --- /dev/null +++ b/spa/plugins/alsa/alsa-monitor.c @@ -0,0 +1,437 @@ +/* Spa ALSA Monitor + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +extern const SpaHandleFactory spa_alsa_source_factory; + +typedef struct _SpaALSAMonitor SpaALSAMonitor; + +typedef struct { + SpaMonitorItem item; + struct udev_device *udevice; + SpaDict info; + SpaDictItem info_items[32]; +} ALSAItem; + +struct _SpaALSAMonitor { + SpaHandle handle; + SpaMonitor monitor; + + SpaMonitorEventCallback event_cb; + void *user_data; + + struct udev* udev; + struct udev_monitor *umonitor; + struct udev_enumerate *enumerate; + + ALSAItem uitem; + + int fd; + SpaPollFd fds[1]; + SpaPollItem poll; +}; + +static SpaResult +alsa_udev_open (SpaALSAMonitor *this) +{ + if (this->udev != NULL) + return SPA_RESULT_OK; + + this->udev = udev_new (); + + return SPA_RESULT_OK; +} + +static const char * +path_get_card_id (const char *path) +{ + const char *e; + + if (!path) + return NULL; + + if (!(e = strrchr(path, '/'))) + return NULL; + + if (strlen (e) <= 5 || strncmp (e, "/card", 5) != 0) + return NULL; + + return e + 5; +} + +static int +fill_item (ALSAItem *item, struct udev_device *udevice) +{ + unsigned int i; + const char *str; + + if (item->udevice) + udev_device_unref (item->udevice); + item->udevice = udevice; + if (udevice == NULL) + return -1; + + if (udev_device_get_property_value (udevice, "PULSE_IGNORE")) + return -1; + + if ((str = udev_device_get_property_value (udevice, "SOUND_CLASS")) && + strcmp (str, "modem") == 0) + return -1; + + if ((str = path_get_card_id (udev_device_get_property_value (udevice, "DEVPATH"))) == NULL) + return -1; + + item->item.id = udev_device_get_syspath (item->udevice); + item->item.flags = 0; + item->item.state = SPA_MONITOR_ITEM_STATE_AVAILABLE; + item->item.klass = "Audio/Device"; + item->item.info = &item->info; + item->item.factory = &spa_alsa_source_factory; + + item->info.items = item->info_items; + i = 0; + item->info_items[i].key = "alsa.card"; + item->info_items[i++].value = str; + item->info_items[i].key = "udev-probed"; + item->info_items[i++].value = "1"; + item->info_items[i].key = "device.api"; + item->info_items[i++].value = "alsa"; + + if ((str = udev_device_get_property_value (udevice, "SOUND_CLASS")) && *str) { + item->info_items[i].key = "device.class"; + item->info_items[i++].value = str; + } + + str = udev_device_get_property_value (item->udevice, "ID_PATH"); + if (!(str && *str)) + str = udev_device_get_syspath (item->udevice); + if (str && *str) { + item->info_items[i].key = "device.bus_path"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_syspath (item->udevice)) && *str) { + item->info_items[i].key = "sysfs.path"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_ID")) && *str) { + item->info_items[i].key = "udev.id"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_BUS")) && *str) { + item->info_items[i].key = "device.bus"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "SUBSYSTEM")) && *str) { + item->info_items[i].key = "device.subsystem"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_VENDOR_ID")) && *str) { + item->info_items[i].key = "device.vendor.id"; + item->info_items[i++].value = str; + } + str = udev_device_get_property_value (item->udevice, "ID_VENDOR_FROM_DATABASE"); + if (!(str && *str)) { + str = udev_device_get_property_value (item->udevice, "ID_VENDOR_ENC"); + if (!(str && *str)) { + str = udev_device_get_property_value (item->udevice, "ID_VENDOR"); + } + } + if (str && *str) { + item->info_items[i].key = "device.vendor.name"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_MODEL_ID")) && *str) { + item->info_items[i].key = "device.product.id"; + item->info_items[i++].value = str; + } + str = udev_device_get_property_value (item->udevice, "ID_V4L_PRODUCT"); + if (!(str && *str)) { + str = udev_device_get_property_value (item->udevice, "ID_MODEL_FROM_DATABASE"); + if (!(str && *str)) { + str = udev_device_get_property_value (item->udevice, "ID_MODEL_ENC"); + if (!(str && *str)) { + str = udev_device_get_property_value (item->udevice, "ID_MODEL"); + } + } + } + if (str && *str) { + item->info_items[i].key = "device.product.name"; + item->info_items[i++].value = str; + item->item.name = str; + } else { + item->item.name = "Unknown"; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_SERIAL")) && *str) { + item->info_items[i].key = "device.serial"; + item->info_items[i++].value = str; + } + if ((str = udev_device_get_property_value (item->udevice, "ID_V4L_CAPABILITIES")) && *str) { + item->info_items[i].key = "device.capabilities"; + item->info_items[i++].value = str; + } + item->info.n_items = i; + + return 0; +} + +static int +alsa_on_fd_events (SpaPollNotifyData *data) +{ + SpaALSAMonitor *this = data->user_data; + struct udev_device *dev; + const char *str; + SpaMonitorEvent event; + + dev = udev_monitor_receive_device (this->umonitor); + if (fill_item (&this->uitem, dev) < 0) + return 0; + + if ((str = udev_device_get_action (dev)) == NULL) + str = "change"; + + if (strcmp (str, "add") == 0) { + event.type = SPA_MONITOR_EVENT_TYPE_ADDED; + } else if (strcmp (str, "change") == 0) { + event.type = SPA_MONITOR_EVENT_TYPE_CHANGED; + } else if (strcmp (str, "remove") == 0) { + event.type = SPA_MONITOR_EVENT_TYPE_REMOVED; + } + event.data = &this->uitem.item; + event.size = sizeof (this->uitem); + this->event_cb (&this->monitor, &event, this->user_data); + + return 0; +} + +static SpaResult +spa_alsa_monitor_set_event_callback (SpaMonitor *monitor, + SpaMonitorEventCallback callback, + void *user_data) +{ + SpaResult res; + SpaALSAMonitor *this; + SpaMonitorEvent event; + + if (monitor == NULL || monitor->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSAMonitor *) monitor->handle; + + this->event_cb = callback; + this->user_data = user_data; + + if (callback) { + if ((res = alsa_udev_open (this)) < 0) + return res; + + this->umonitor = udev_monitor_new_from_netlink (this->udev, "udev"); + if (!this->umonitor) + return SPA_RESULT_ERROR; + + udev_monitor_filter_add_match_subsystem_devtype (this->umonitor, + "video4linux", + NULL); + + udev_monitor_enable_receiving (this->umonitor); + this->fd = udev_monitor_get_fd (this->umonitor);; + + event.type = SPA_MONITOR_EVENT_TYPE_ADD_POLL; + event.data = &this->poll; + event.size = sizeof (this->poll); + + this->fds[0].fd = this->fd; + this->fds[0].events = POLLIN | POLLPRI | POLLERR; + this->fds[0].revents = 0; + + this->poll.id = 0; + this->poll.enabled = true; + this->poll.fds = this->fds; + this->poll.n_fds = 1; + this->poll.idle_cb = NULL; + this->poll.before_cb = NULL; + this->poll.after_cb = alsa_on_fd_events; + this->poll.user_data = this; + this->event_cb (&this->monitor, &event, this->user_data); + } else { + event.type = SPA_MONITOR_EVENT_TYPE_REMOVE_POLL; + event.data = &this->poll; + event.size = sizeof (this->poll); + this->event_cb (&this->monitor, &event, this->user_data); + } + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_monitor_enum_items (SpaMonitor *monitor, + SpaMonitorItem **item, + void **state) +{ + SpaResult res; + SpaALSAMonitor *this; + struct udev_list_entry *devices; + struct udev_device *dev; + + if (monitor == NULL || monitor->handle == NULL || item == NULL || state == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSAMonitor *) monitor->handle; + + if ((res = alsa_udev_open (this)) < 0) + return res; + +again: + if (*state == NULL) { + if (this->enumerate) + udev_enumerate_unref (this->enumerate); + this->enumerate = udev_enumerate_new (this->udev); + + udev_enumerate_add_match_subsystem (this->enumerate, "sound"); + udev_enumerate_scan_devices (this->enumerate); + + devices = udev_enumerate_get_list_entry (this->enumerate); + if (devices == NULL) + return SPA_RESULT_ENUM_END; + } else { + devices = *state; + } + + if (*state == (void*)1) { + fill_item (&this->uitem, NULL); + return SPA_RESULT_ENUM_END; + } + + dev = udev_device_new_from_syspath (this->udev, + udev_list_entry_get_name (devices)); + + if ((*state = udev_list_entry_get_next (devices)) == NULL) + *state = (void*)1; + + if (fill_item (&this->uitem, dev) < 0) + goto again; + + *item = &this->uitem.item; + + return SPA_RESULT_OK; +} + +static const SpaMonitor alsamonitor = { + NULL, + NULL, + sizeof (SpaMonitor), + spa_alsa_monitor_set_event_callback, + spa_alsa_monitor_enum_items, +}; + +static SpaResult +spa_alsa_monitor_get_interface (SpaHandle *handle, + uint32_t interface_id, + void **interface) +{ + SpaALSAMonitor *this; + + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSAMonitor *) handle; + + switch (interface_id) { + case SPA_INTERFACE_ID_MONITOR: + *interface = &this->monitor; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +static SpaResult +alsa_monitor_clear (SpaHandle *handle) +{ + return SPA_RESULT_OK; +} + +static SpaResult +alsa_monitor_init (const SpaHandleFactory *factory, + SpaHandle *handle, + const SpaDict *info) +{ + SpaALSAMonitor *this; + + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + handle->get_interface = spa_alsa_monitor_get_interface; + handle->clear = alsa_monitor_clear, + + this = (SpaALSAMonitor *) handle; + this->monitor = alsamonitor; + this->monitor.handle = handle; + + return SPA_RESULT_OK; +} + +static const SpaInterfaceInfo alsa_monitor_interfaces[] = +{ + { SPA_INTERFACE_ID_MONITOR, + SPA_INTERFACE_ID_MONITOR_NAME, + SPA_INTERFACE_ID_MONITOR_DESCRIPTION, + }, +}; + +static SpaResult +alsa_monitor_enum_interface_info (const SpaHandleFactory *factory, + const SpaInterfaceInfo **info, + void **state) +{ + int index; + + if (factory == NULL || info == NULL || state == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + index = (*state == NULL ? 0 : *(int*)state); + + if (index < 0 || index >= SPA_N_ELEMENTS (alsa_monitor_interfaces)) + return SPA_RESULT_ENUM_END; + + *info = &alsa_monitor_interfaces[index]; + *(int*)state = ++index; + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_alsa_monitor_factory = +{ "alsa-monitor", + NULL, + sizeof (SpaALSAMonitor), + alsa_monitor_init, + alsa_monitor_enum_interface_info, +}; diff --git a/spa/plugins/alsa/alsa-source.c b/spa/plugins/alsa/alsa-source.c new file mode 100644 index 000000000..e2baccff4 --- /dev/null +++ b/spa/plugins/alsa/alsa-source.c @@ -0,0 +1,819 @@ +/* Spa ALSA Source + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include +#include +#include + +#include "alsa-utils.h" + +typedef struct _SpaALSAState SpaALSASource; + +static void +update_state (SpaALSASource *this, SpaNodeState state) +{ + SpaNodeEvent event; + SpaNodeEventStateChange sc; + + if (this->node.state == state) + return; + + this->node.state = state; + + event.type = SPA_NODE_EVENT_TYPE_STATE_CHANGE; + event.data = ≻ + event.size = sizeof (sc); + sc.state = state; + this->event_cb (&this->node, &event, this->user_data); +} + +static const char default_device[] = "hw:0"; +static const uint32_t default_buffer_time = 1000; +static const uint32_t default_period_time = 100; +static const bool default_period_event = 0; + +static void +reset_alsa_props (SpaALSAProps *props) +{ + strncpy (props->device, default_device, 64); + props->buffer_time = default_buffer_time; + props->period_time = default_period_time; + props->period_event = default_period_event; + props->props.unset_mask = 0xf; +} + +static const uint32_t min_uint32 = 1; +static const uint32_t max_uint32 = UINT32_MAX; + +static const SpaPropRangeInfo uint32_range[] = { + { "min", "Minimum value", { 4, &min_uint32 } }, + { "max", "Maximum value", { 4, &max_uint32 } }, +}; + +enum { + PROP_ID_DEVICE, + PROP_ID_DEVICE_NAME, + PROP_ID_CARD_NAME, + PROP_ID_BUFFER_TIME, + PROP_ID_PERIOD_TIME, + PROP_ID_PERIOD_EVENT, + PROP_ID_LAST, +}; + +static const SpaPropInfo prop_info[] = +{ + { PROP_ID_DEVICE, offsetof (SpaALSAProps, device), + "device", "ALSA device, as defined in an asound configuration file", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_STRING, 63, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL }, + { PROP_ID_DEVICE_NAME, offsetof (SpaALSAProps, device_name), + "device-name", "Human-readable name of the sound device", + SPA_PROP_FLAG_READABLE, + SPA_PROP_TYPE_STRING, 127, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL }, + { PROP_ID_CARD_NAME, offsetof (SpaALSAProps, card_name), + "card-name", "Human-readable name of the sound card", + SPA_PROP_FLAG_READABLE, + SPA_PROP_TYPE_STRING, 127, + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL }, + { PROP_ID_BUFFER_TIME, offsetof (SpaALSAProps, buffer_time), + "buffer-time", "The total size of the buffer in time", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, + NULL }, + { PROP_ID_PERIOD_TIME, offsetof (SpaALSAProps, period_time), + "period-time", "The size of a period in time", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_UINT32, sizeof (uint32_t), + SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, + NULL }, + { PROP_ID_PERIOD_EVENT, offsetof (SpaALSAProps, period_event), + "period-event", "Generate an event each period", + SPA_PROP_FLAG_READWRITE, + SPA_PROP_TYPE_BOOL, sizeof (bool), + SPA_PROP_RANGE_TYPE_NONE, 0, NULL, + NULL }, +}; + +static SpaResult +spa_alsa_source_node_get_props (SpaNode *node, + SpaProps **props) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL || props == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + memcpy (&this->props[0], &this->props[1], sizeof (this->props[1])); + *props = &this->props[0].props; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_set_props (SpaNode *node, + const SpaProps *props) +{ + SpaALSASource *this; + SpaALSAProps *p; + SpaResult res; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + p = &this->props[1]; + + if (props == NULL) { + reset_alsa_props (p); + return SPA_RESULT_OK; + } + + res = spa_props_copy (props, &p->props); + + return res; +} + +static SpaResult +spa_alsa_source_node_send_command (SpaNode *node, + SpaNodeCommand *command) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL || command == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + switch (command->type) { + case SPA_NODE_COMMAND_INVALID: + return SPA_RESULT_INVALID_COMMAND; + + case SPA_NODE_COMMAND_START: + if (spa_alsa_start (this) < 0) + return SPA_RESULT_ERROR; + + update_state (this, SPA_NODE_STATE_STREAMING); + break; + case SPA_NODE_COMMAND_PAUSE: + if (spa_alsa_stop (this) < 0) + return SPA_RESULT_ERROR; + + update_state (this, SPA_NODE_STATE_PAUSED); + break; + case SPA_NODE_COMMAND_FLUSH: + case SPA_NODE_COMMAND_DRAIN: + case SPA_NODE_COMMAND_MARKER: + case SPA_NODE_COMMAND_CLOCK_UPDATE: + return SPA_RESULT_NOT_IMPLEMENTED; + } + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_set_event_callback (SpaNode *node, + SpaNodeEventCallback event, + void *user_data) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + this->event_cb = event; + this->user_data = user_data; + + update_state (this, SPA_NODE_STATE_CONFIGURE); + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_get_n_ports (SpaNode *node, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 0; + if (max_input_ports) + *max_input_ports = 0; + if (n_output_ports) + *n_output_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_get_port_ids (SpaNode *node, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (n_output_ports > 0 && output_ids != NULL) + output_ids[0] = 0; + + return SPA_RESULT_OK; +} + + +static SpaResult +spa_alsa_source_node_add_port (SpaNode *node, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_node_remove_port (SpaNode *node, + uint32_t port_id) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_node_port_enum_formats (SpaNode *node, + uint32_t port_id, + SpaFormat **format, + const SpaFormat *filter, + void **state) +{ + SpaALSASource *this; + int index; + + if (node == NULL || node->handle == NULL || format == NULL || state == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + index = (*state == NULL ? 0 : *(int*)state); + + switch (index) { + case 0: + spa_format_audio_init (SPA_MEDIA_TYPE_AUDIO, + SPA_MEDIA_SUBTYPE_RAW, + &this->query_format); + break; + case 1: + spa_format_audio_init (SPA_MEDIA_TYPE_AUDIO, + SPA_MEDIA_SUBTYPE_AAC, + &this->query_format); + break; + default: + return SPA_RESULT_ENUM_END; + } + *format = &this->query_format.format; + *(int*)state = ++index; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_set_format (SpaNode *node, + uint32_t port_id, + SpaPortFormatFlags flags, + const SpaFormat *format) +{ + SpaALSASource *this; + SpaResult res; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (format == NULL) { + this->have_format = false; + update_state (this, SPA_NODE_STATE_CONFIGURE); + return SPA_RESULT_OK; + } + + if ((res = spa_format_audio_parse (format, &this->current_format)) < 0) + return res; + + if (spa_alsa_set_format (this, &this->current_format, flags) < 0) + return SPA_RESULT_ERROR; + + this->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS | + SPA_PORT_INFO_FLAG_LIVE; + this->info.maxbuffering = this->buffer_frames * this->frame_size; + this->info.latency = (this->period_frames * SPA_NSEC_PER_SEC) / this->rate; + this->info.n_params = 1; + this->info.params = this->params; + this->params[0] = &this->param_buffers.param; + this->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS; + this->param_buffers.param.size = sizeof (this->param_buffers); + this->param_buffers.minsize = this->period_frames * this->frame_size; + this->param_buffers.stride = 0; + this->param_buffers.min_buffers = 1; + this->param_buffers.max_buffers = 16; + this->param_buffers.align = 16; + this->info.features = NULL; + + this->have_format = true; + + update_state (this, SPA_NODE_STATE_READY); + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_get_format (SpaNode *node, + uint32_t port_id, + const SpaFormat **format) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL || format == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; + + *format = &this->current_format.format; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_get_info (SpaNode *node, + uint32_t port_id, + const SpaPortInfo **info) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + *info = &this->info; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_get_props (SpaNode *node, + uint32_t port_id, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_node_port_set_props (SpaNode *node, + uint32_t port_id, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_node_port_use_buffers (SpaNode *node, + uint32_t port_id, + SpaBuffer **buffers, + uint32_t n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static void +recycle_buffer (SpaALSASource *this, uint32_t buffer_id) +{ + SpaALSABuffer *b; + + b = &this->alloc_buffers[buffer_id]; + if (!b->outstanding) + return; + + b->outstanding = false; + SPA_QUEUE_PUSH_TAIL (&this->free, SpaALSABuffer, next, b); +} + +static SpaResult +spa_alsa_source_node_port_alloc_buffers (SpaNode *node, + uint32_t port_id, + SpaAllocParam **params, + uint32_t n_params, + SpaBuffer **buffers, + uint32_t *n_buffers) +{ + SpaALSASource *this; + SpaALSABuffer *b; + unsigned int i, n_bufs; + uint8_t *bufmem; + size_t buffer_size; + + if (node == NULL || node->handle == NULL || buffers == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + this = (SpaALSASource *) node->handle; + + if (!this->have_format) + return SPA_RESULT_NO_FORMAT; + + n_bufs = *n_buffers; + + SPA_QUEUE_INIT (&this->free); + SPA_QUEUE_INIT (&this->ready); + + buffer_size = this->buffer_frames * this->frame_size; + + if (!this->alloc_bufmem) + this->alloc_bufmem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, + NULL, + buffer_size * n_bufs); + bufmem = spa_memory_ensure_ptr (this->alloc_bufmem); + + if (!this->alloc_mem) + this->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, NULL, sizeof (SpaALSABuffer) * n_bufs); + this->alloc_buffers = spa_memory_ensure_ptr (this->alloc_mem); + this->n_buffers = n_bufs; + + for (i = 0; i < n_bufs; i++) { + b = &this->alloc_buffers[i]; + b->buffer.id = i; + b->buffer.mem.mem = this->alloc_mem->mem; + b->buffer.mem.offset = sizeof (SpaALSABuffer) * i; + b->buffer.mem.size = sizeof (SpaALSABuffer); + + b->buffer.n_metas = 2; + b->buffer.metas = offsetof (SpaALSABuffer, metas); + b->buffer.n_datas = 1; + b->buffer.datas = offsetof (SpaALSABuffer, datas); + + b->header.flags = 0; + b->header.seq = 0; + b->header.pts = 0; + b->header.dts_offset = 0; + + b->metas[0].type = SPA_META_TYPE_HEADER; + b->metas[0].offset = offsetof (SpaALSABuffer, header); + b->metas[0].size = sizeof (b->header); + + b->ringbuffer.readindex = 0; + b->ringbuffer.writeindex = 0; + b->ringbuffer.size = 0; + b->ringbuffer.size_mask = 0; + + b->metas[1].type = SPA_META_TYPE_RINGBUFFER; + b->metas[1].offset = offsetof (SpaALSABuffer, ringbuffer); + b->metas[1].size = sizeof (b->ringbuffer); + + b->datas[0].mem.mem = this->alloc_bufmem->mem; + b->datas[0].mem.offset = buffer_size * i; + b->datas[0].mem.size = buffer_size; + b->datas[0].stride = 0; + b->ptr = bufmem + buffer_size * i; + + buffers[i] = b->outbuf = &b->buffer; + + b->outstanding = true; + recycle_buffer (this, b->outbuf->id); + } + *n_buffers = n_bufs; + + this->have_buffers = true; + + update_state (this, SPA_NODE_STATE_PAUSED); + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_reuse_buffer (SpaNode *node, + uint32_t port_id, + uint32_t buffer_id) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + if (!this->have_buffers) + return SPA_RESULT_NO_BUFFERS; + + if (buffer_id >= this->n_buffers) + return SPA_RESULT_INVALID_BUFFER_ID; + + recycle_buffer (this, buffer_id); + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_get_status (SpaNode *node, + uint32_t port_id, + const SpaPortStatus **status) +{ + SpaALSASource *this; + + if (node == NULL || node->handle == NULL || status == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + if (port_id != 0) + return SPA_RESULT_INVALID_PORT; + + *status = &this->status; + + return SPA_RESULT_OK; +} + +static SpaResult +spa_alsa_source_node_port_push_input (SpaNode *node, + unsigned int n_info, + SpaPortInputInfo *info) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_node_port_pull_output (SpaNode *node, + unsigned int n_info, + SpaPortOutputInfo *info) +{ + SpaALSASource *this; + unsigned int i; + bool have_error = false; + + if (node == NULL || node->handle == NULL || n_info == 0 || info == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) node->handle; + + for (i = 0; i < n_info; i++) { + SpaALSABuffer *b; + + if (info[i].port_id != 0) { + info[i].status = SPA_RESULT_INVALID_PORT; + have_error = true; + continue; + } + + if (!this->have_format) { + info[i].status = SPA_RESULT_NO_FORMAT; + have_error = true; + continue; + } + + SPA_QUEUE_POP_HEAD (&this->ready, SpaALSABuffer, next, b); + if (b == NULL) { + info[i].status = SPA_RESULT_UNEXPECTED; + have_error = true; + continue; + } + b->outstanding = true; + + info[i].buffer_id = b->outbuf->id; + info[i].status = SPA_RESULT_OK; + } + if (have_error) + return SPA_RESULT_ERROR; + + return SPA_RESULT_OK; + +} + +static const SpaNode alsasource_node = { + NULL, + sizeof (SpaNode), + NULL, + SPA_NODE_STATE_INIT, + spa_alsa_source_node_get_props, + spa_alsa_source_node_set_props, + spa_alsa_source_node_send_command, + spa_alsa_source_node_set_event_callback, + spa_alsa_source_node_get_n_ports, + spa_alsa_source_node_get_port_ids, + spa_alsa_source_node_add_port, + spa_alsa_source_node_remove_port, + spa_alsa_source_node_port_enum_formats, + spa_alsa_source_node_port_set_format, + spa_alsa_source_node_port_get_format, + spa_alsa_source_node_port_get_info, + spa_alsa_source_node_port_get_props, + spa_alsa_source_node_port_set_props, + spa_alsa_source_node_port_use_buffers, + spa_alsa_source_node_port_alloc_buffers, + spa_alsa_source_node_port_reuse_buffer, + spa_alsa_source_node_port_get_status, + spa_alsa_source_node_port_push_input, + spa_alsa_source_node_port_pull_output, +}; +static SpaResult +spa_alsa_source_clock_get_props (SpaClock *clock, + SpaProps **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_clock_set_props (SpaClock *clock, + const SpaProps *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static SpaResult +spa_alsa_source_clock_get_time (SpaClock *clock, + int32_t *rate, + int64_t *ticks, + int64_t *monotonic_time) +{ + SpaALSASource *this; + + if (clock == NULL || clock->handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) clock->handle; + + if (rate) + *rate = SPA_USEC_PER_SEC; + if (ticks) + *ticks = this->last_ticks; + if (monotonic_time) + *monotonic_time = this->last_monotonic; + + return SPA_RESULT_OK; +} + +static const SpaClock alsasource_clock = { + NULL, + sizeof (SpaClock), + NULL, + SPA_CLOCK_STATE_STOPPED, + spa_alsa_source_clock_get_props, + spa_alsa_source_clock_set_props, + spa_alsa_source_clock_get_time, +}; + +static SpaResult +spa_alsa_source_get_interface (SpaHandle *handle, + uint32_t interface_id, + void **interface) +{ + SpaALSASource *this; + + if (handle == NULL || interface == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + this = (SpaALSASource *) handle; + + switch (interface_id) { + case SPA_INTERFACE_ID_NODE: + *interface = &this->node; + break; + case SPA_INTERFACE_ID_CLOCK: + *interface = &this->clock; + break; + default: + return SPA_RESULT_UNKNOWN_INTERFACE; + } + return SPA_RESULT_OK; +} + +static SpaResult +alsa_source_clear (SpaHandle *handle) +{ + return SPA_RESULT_OK; +} + +static SpaResult +alsa_source_init (const SpaHandleFactory *factory, + SpaHandle *handle, + const SpaDict *info) +{ + SpaALSASource *this; + unsigned int i; + + if (factory == NULL || handle == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + handle->get_interface = spa_alsa_source_get_interface; + handle->clear = alsa_source_clear; + + this = (SpaALSASource *) handle; + this->node = alsasource_node; + this->node.handle = handle; + this->clock = alsasource_clock; + this->clock.handle = handle; + this->props[1].props.n_prop_info = PROP_ID_LAST; + this->props[1].props.prop_info = prop_info; + this->stream = SND_PCM_STREAM_CAPTURE; + reset_alsa_props (&this->props[1]); + + this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT; + + for (i = 0; info && i < info->n_items; i++) { + if (!strcmp (info->items[i].key, "alsa.card")) { + snprintf (this->props[1].device, 63, "hw:%s", info->items[i].value); + this->props[1].props.unset_mask &= ~1; + } + } + return SPA_RESULT_OK; +} + +static const SpaInterfaceInfo alsa_source_interfaces[] = +{ + { SPA_INTERFACE_ID_NODE, + SPA_INTERFACE_ID_NODE_NAME, + SPA_INTERFACE_ID_NODE_DESCRIPTION, + }, + { SPA_INTERFACE_ID_CLOCK, + SPA_INTERFACE_ID_CLOCK_NAME, + SPA_INTERFACE_ID_CLOCK_DESCRIPTION, + }, +}; + +static SpaResult +alsa_source_enum_interface_info (const SpaHandleFactory *factory, + const SpaInterfaceInfo **info, + void **state) +{ + int index; + + if (factory == NULL || info == NULL || state == NULL) + return SPA_RESULT_INVALID_ARGUMENTS; + + index = (*state == NULL ? 0 : *(int*)state); + + if (index < 0 || index >= SPA_N_ELEMENTS (alsa_source_interfaces)) + return SPA_RESULT_ENUM_END; + + *info = &alsa_source_interfaces[index]; + + *(int*)state = ++index; + + return SPA_RESULT_OK; +} + +const SpaHandleFactory spa_alsa_source_factory = +{ "alsa-source", + NULL, + sizeof (SpaALSASource), + alsa_source_init, + alsa_source_enum_interface_info, +}; diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 1405ffc1b..66a966e4f 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -106,9 +106,9 @@ spa_alsa_format_to_alsa (SpaAudioFormat format) } int -spa_alsa_set_format (SpaALSAState *state, SpaFormatAudio *fmt, bool try_only) +spa_alsa_set_format (SpaALSAState *state, SpaFormatAudio *fmt, SpaPortFormatFlags flags) { - unsigned int rrate; + unsigned int rrate, rchannels; snd_pcm_uframes_t size; int err, dir; snd_pcm_hw_params_t *params; @@ -136,16 +136,32 @@ spa_alsa_set_format (SpaALSAState *state, SpaFormatAudio *fmt, bool try_only) format = spa_alsa_format_to_alsa (info->format); printf ("Stream parameters are %iHz, %s, %i channels\n", info->rate, snd_pcm_format_name(format), info->channels); CHECK (snd_pcm_hw_params_set_format (hndl, params, format), "set_format"); + /* set the count of channels */ - CHECK (snd_pcm_hw_params_set_channels (hndl, params, info->channels), "set_channels"); + rchannels = info->channels; + CHECK (snd_pcm_hw_params_set_channels_near (hndl, params, &rchannels), "set_channels"); + if (rchannels != info->channels) { + fprintf (stderr, "Channels doesn't match (requested %u, get %u\n", info->channels, rchannels); + if (flags & SPA_PORT_FORMAT_FLAG_NEAREST) + info->channels = rchannels; + else + return -EINVAL; + } + /* set the stream rate */ rrate = info->rate; CHECK (snd_pcm_hw_params_set_rate_near (hndl, params, &rrate, 0), "set_rate_near"); if (rrate != info->rate) { - printf("Rate doesn't match (requested %iHz, get %iHz)\n", info->rate, rrate); - return -EINVAL; + fprintf (stderr, "Rate doesn't match (requested %iHz, get %iHz)\n", info->rate, rrate); + if (flags & SPA_PORT_FORMAT_FLAG_NEAREST) + info->rate = rrate; + else + return -EINVAL; } + state->format = format; + state->channels = info->channels; + state->rate = info->rate; state->frame_size = info->channels * 2; /* set the buffer time */ @@ -178,6 +194,9 @@ set_swparams (SpaALSAState *state) /* get the current params */ CHECK (snd_pcm_sw_params_current (hndl, params), "sw_params_current"); + + CHECK (snd_pcm_sw_params_set_tstamp_mode (hndl, params, SND_PCM_TSTAMP_ENABLE), "sw_params_set_tstamp_mode"); + /* start the transfer when the buffer is almost full: */ /* (buffer_frames / avail_min) * avail_min */ CHECK (snd_pcm_sw_params_set_start_threshold (hndl, params, @@ -305,7 +324,6 @@ mmap_read (SpaALSAState *state) if (b == NULL) { fprintf (stderr, "no more buffers\n"); } else { - b->next = NULL; dest = b->ptr; } @@ -326,6 +344,8 @@ mmap_read (SpaALSAState *state) (uint8_t *)my_areas[0].addr + (offset * state->frame_size), n_bytes); dest += n_bytes; + } else { + snd_pcm_areas_silence (my_areas, offset, state->channels, frames, state->format); } commitres = snd_pcm_mmap_commit (hndl, offset, frames); @@ -346,6 +366,7 @@ mmap_read (SpaALSAState *state) d = SPA_BUFFER_DATAS (b->outbuf); d[0].mem.size = avail * state->frame_size; + b->next = NULL; SPA_QUEUE_PUSH_TAIL (&state->ready, SpaALSABuffer, next, b); event.type = SPA_NODE_EVENT_TYPE_HAVE_OUTPUT; @@ -450,13 +471,13 @@ spa_alsa_stop (SpaALSAState *state) if (!state->opened) return 0; - snd_pcm_drop (state->hndl); - event.type = SPA_NODE_EVENT_TYPE_REMOVE_POLL; event.data = &state->poll; event.size = sizeof (state->poll); state->event_cb (&state->node, &event, state->user_data); + snd_pcm_drop (state->hndl); + spa_alsa_close (state); return 0; diff --git a/spa/plugins/alsa/alsa-utils.h b/spa/plugins/alsa/alsa-utils.h new file mode 100644 index 000000000..af2347ff1 --- /dev/null +++ b/spa/plugins/alsa/alsa-utils.h @@ -0,0 +1,120 @@ +/* Spa ALSA Sink + * Copyright (C) 2016 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __SPA_ALSA_UTILS_H__ +#define __SPA_ALSA_UTILS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include +#include + +typedef struct _SpaALSAState SpaALSAState; +typedef struct _SpaALSABuffer SpaALSABuffer; + +typedef struct { + SpaProps props; + char device[64]; + char device_name[128]; + char card_name[128]; + uint32_t buffer_time; + uint32_t period_time; + bool period_event; +} SpaALSAProps; + +struct _SpaALSABuffer { + SpaBuffer buffer; + SpaMeta metas[2]; + SpaMetaHeader header; + SpaMetaRingbuffer ringbuffer; + SpaData datas[1]; + SpaBuffer *outbuf; + void *ptr; + bool outstanding; + SpaALSABuffer *next; +}; + +struct _SpaALSAState { + SpaHandle handle; + SpaNode node; + SpaClock clock; + + snd_pcm_stream_t stream; + snd_output_t *output; + + SpaNodeEventCallback event_cb; + void *user_data; + + SpaALSAProps props[2]; + + bool opened; + snd_pcm_t *hndl; + + bool have_format; + SpaFormatAudio query_format; + SpaFormatAudio current_format; + snd_pcm_sframes_t buffer_frames; + snd_pcm_sframes_t period_frames; + snd_pcm_format_t format; + int rate; + int channels; + size_t frame_size; + + SpaPortInfo info; + SpaAllocParam *params[1]; + SpaAllocParamBuffers param_buffers; + SpaPortStatus status; + + bool have_buffers; + + SpaMemory *alloc_bufmem; + SpaMemory *alloc_mem; + SpaALSABuffer *alloc_buffers; + unsigned int n_buffers; + + SpaQueue free; + SpaQueue ready; + + bool running; + SpaPollFd fds[16]; + SpaPollItem poll; + + int64_t last_ticks; + int64_t last_monotonic; +}; + +int spa_alsa_set_format (SpaALSAState *state, + SpaFormatAudio *fmt, + SpaPortFormatFlags flags); + +int spa_alsa_start (SpaALSAState *state); +int spa_alsa_stop (SpaALSAState *state); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPA_ALSA_UTILS_H__ */ diff --git a/spa/plugins/audiotestsrc/audiotestsrc.c b/spa/plugins/audiotestsrc/audiotestsrc.c index 9a65f0dcc..1e1ec8e75 100644 --- a/spa/plugins/audiotestsrc/audiotestsrc.c +++ b/spa/plugins/audiotestsrc/audiotestsrc.c @@ -28,8 +28,7 @@ #include #include -#define TIMESPEC_TO_TIME(ts) ((ts)->tv_sec * 1000000000ll + (ts)->tv_nsec) -#define SAMPLES_TO_TIME(this,s) ((s) * 1000000000ll / (this)->current_format.info.raw.rate) +#define SAMPLES_TO_TIME(this,s) ((s) * SPA_NSEC_PER_SEC / (this)->current_format.info.raw.rate) #define BYTES_TO_SAMPLES(this,b) ((b)/(this)->bpf) #define BYTES_TO_TIME(this,b) SAMPLES_TO_TIME(this, BYTES_TO_SAMPLES (this, b)) @@ -100,7 +99,7 @@ struct _SpaAudioTestSrc { #define DEFAULT_WAVE 0 #define DEFAULT_VOLUME 1.0 #define DEFAULT_FREQ 440.0 -#define DEFAULT_LIVE true +#define DEFAULT_LIVE false static const double min_volume = 0.0; static const double max_volume = 10.0; @@ -282,8 +281,8 @@ audiotestsrc_on_output (SpaPollNotifyData *data) perror ("read timerfd"); next_time = this->start_time + this->elapsed_time; - this->timerspec.it_value.tv_sec = next_time / 1000000000; - this->timerspec.it_value.tv_nsec = next_time % 1000000000; + this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; + this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL); } @@ -325,8 +324,8 @@ update_poll_enabled (SpaAudioTestSrc *this, bool enabled) if (this->props[1].live) { if (enabled) { uint64_t next_time = this->start_time + this->elapsed_time; - this->timerspec.it_value.tv_sec = next_time / 1000000000; - this->timerspec.it_value.tv_nsec = next_time % 1000000000; + this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; + this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; } else { this->timerspec.it_value.tv_sec = 0; @@ -379,7 +378,10 @@ spa_audiotestsrc_node_send_command (SpaNode *node, return SPA_RESULT_OK; clock_gettime (CLOCK_MONOTONIC, &now); - this->start_time = TIMESPEC_TO_TIME (&now); + if (this->props[1].live) + this->start_time = SPA_TIMESPEC_TO_TIME (&now); + else + this->start_time = 0; this->sample_count = 0; this->elapsed_time = 0; @@ -945,10 +947,10 @@ spa_audiotestsrc_clock_get_time (SpaClock *clock, return SPA_RESULT_INVALID_ARGUMENTS; if (rate) - *rate = 1000000000; + *rate = SPA_NSEC_PER_SEC; clock_gettime (CLOCK_MONOTONIC, &now); - tnow = TIMESPEC_TO_TIME (&now); + tnow = SPA_TIMESPEC_TO_TIME (&now); if (ticks) *ticks = tnow; diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index 7c82d349f..061fc6352 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -783,7 +783,7 @@ spa_v4l2_source_clock_get_time (SpaClock *clock, state = &this->state[0]; if (rate) - *rate = 1000000; + *rate = SPA_USEC_PER_SEC; if (ticks) *ticks = state->last_ticks; if (monotonic_time) @@ -856,12 +856,12 @@ v4l2_source_init (const SpaHandleFactory *factory, this->props[1].props.prop_info = prop_info; reset_v4l2_source_props (&this->props[1]); - this->state[0].info.flags = SPA_PORT_INFO_FLAG_NONE; + this->state[0].info.flags = SPA_PORT_INFO_FLAG_LIVE; this->state[0].status.flags = SPA_PORT_STATUS_FLAG_NONE; this->state[0].export_buf = true; - for (i = 0; info && i < info->n_items; i ++) { + for (i = 0; info && i < info->n_items; i++) { if (!strcmp (info->items[i].key, "device.path")) { strncpy (this->props[1].device, info->items[i].value, 63); this->props[1].props.unset_mask &= ~1; diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index eb89835bf..457e2cdc0 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -785,9 +785,10 @@ spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only) state->fmt = fmt; state->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS | - SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | + SPA_PORT_INFO_FLAG_LIVE; state->info.maxbuffering = -1; - state->info.latency = (streamparm.parm.capture.timeperframe.numerator * 1000000000LL) / + state->info.latency = (streamparm.parm.capture.timeperframe.numerator * SPA_NSEC_PER_SEC) / streamparm.parm.capture.timeperframe.denominator; state->info.n_params = 1; @@ -833,7 +834,7 @@ mmap_read (SpaV4l2Source *this) if (buf.flags & V4L2_BUF_FLAG_ERROR) b->header.flags |= SPA_BUFFER_FLAG_CORRUPTED; - state->last_ticks = (int64_t)buf.timestamp.tv_sec * 1000000 + (uint64_t)buf.timestamp.tv_usec; + state->last_ticks = (int64_t)buf.timestamp.tv_sec * SPA_USEC_PER_SEC + (uint64_t)buf.timestamp.tv_usec; b->header.seq = buf.sequence; b->header.pts = state->last_ticks * 1000; diff --git a/spa/plugins/videotestsrc/videotestsrc.c b/spa/plugins/videotestsrc/videotestsrc.c index bcef43fb8..fbeb4ef9f 100644 --- a/spa/plugins/videotestsrc/videotestsrc.c +++ b/spa/plugins/videotestsrc/videotestsrc.c @@ -29,8 +29,7 @@ #include #include -#define TIMESPEC_TO_TIME(ts) ((ts)->tv_sec * 1000000000ll + (ts)->tv_nsec) -#define FRAMES_TO_TIME(this,f) ((this->current_format.info.raw.framerate.num * (f) * 1000000000ll) / \ +#define FRAMES_TO_TIME(this,f) ((this->current_format.info.raw.framerate.num * (f) * SPA_NSEC_PER_SEC) / \ (this->current_format.info.raw.framerate.denom)) #define STATE_GET_IMAGE_WIDTH(this) this->current_format.info.raw.size.width @@ -100,7 +99,7 @@ struct _SpaVideoTestSrc { SpaQueue ready; }; -#define DEFAULT_LIVE true +#define DEFAULT_LIVE false enum { PROP_ID_LIVE, @@ -230,8 +229,8 @@ videotestsrc_on_output (SpaPollNotifyData *data) perror ("read timerfd"); next_time = this->start_time + this->elapsed_time; - this->timerspec.it_value.tv_sec = next_time / 1000000000; - this->timerspec.it_value.tv_nsec = next_time % 1000000000; + this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; + this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL); } @@ -273,8 +272,8 @@ update_poll_enabled (SpaVideoTestSrc *this, bool enabled) if (this->props[1].live) { if (enabled) { uint64_t next_time = this->start_time + this->elapsed_time; - this->timerspec.it_value.tv_sec = next_time / 1000000000; - this->timerspec.it_value.tv_nsec = next_time % 1000000000; + this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; + this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; } else { this->timerspec.it_value.tv_sec = 0; @@ -327,7 +326,10 @@ spa_videotestsrc_node_send_command (SpaNode *node, return SPA_RESULT_OK; clock_gettime (CLOCK_MONOTONIC, &now); - this->start_time = TIMESPEC_TO_TIME (&now); + if (this->props[1].live) + this->start_time = SPA_TIMESPEC_TO_TIME (&now); + else + this->start_time = 0; this->frame_count = 0; this->elapsed_time = 0; @@ -894,10 +896,10 @@ spa_videotestsrc_clock_get_time (SpaClock *clock, return SPA_RESULT_INVALID_ARGUMENTS; if (rate) - *rate = 1000000000; + *rate = SPA_NSEC_PER_SEC; clock_gettime (CLOCK_MONOTONIC, &now); - tnow = TIMESPEC_TO_TIME (&now); + tnow = SPA_TIMESPEC_TO_TIME (&now); if (ticks) *ticks = tnow;