mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	v4l2: continue work on the capture device
Remove activate and deactivate commands. Add STEP property range type for v4l2 frame sizes later v4l2: implement negotiation and data capture
This commit is contained in:
		
							parent
							
								
									beedb65f00
								
							
						
					
					
						commit
						a1a27328e2
					
				
					 17 changed files with 976 additions and 308 deletions
				
			
		| 
						 | 
				
			
			@ -25,7 +25,7 @@
 | 
			
		|||
#include <gio/gio.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/include/spa/node.h>
 | 
			
		||||
#include <spa/include/spa/audio/raw.h>
 | 
			
		||||
#include <spa/include/spa/audio/format.h>
 | 
			
		||||
 | 
			
		||||
#include "spa-alsa-sink.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +163,6 @@ create_pipeline (PinosSpaAlsaSink *this)
 | 
			
		|||
  SpaResult res;
 | 
			
		||||
  SpaProps *props;
 | 
			
		||||
  SpaPropValue value;
 | 
			
		||||
  SpaCommand cmd;
 | 
			
		||||
 | 
			
		||||
  if ((res = make_node (&priv->sink, &priv->sink_node, "spa/build/plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) {
 | 
			
		||||
    g_error ("can't create alsa-sink: %d", res);
 | 
			
		||||
| 
						 | 
				
			
			@ -181,10 +180,6 @@ create_pipeline (PinosSpaAlsaSink *this)
 | 
			
		|||
 | 
			
		||||
  if ((res = priv->sink_node->set_props (priv->sink, props)) < 0)
 | 
			
		||||
    g_debug ("got set_props error %d", res);
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_ACTIVATE;
 | 
			
		||||
  if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
 | 
			
		||||
    g_debug ("got activate error %d", res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
| 
						 | 
				
			
			@ -218,15 +213,7 @@ stop_pipeline (PinosSpaAlsaSink *sink)
 | 
			
		|||
static void
 | 
			
		||||
destroy_pipeline (PinosSpaAlsaSink *sink)
 | 
			
		||||
{
 | 
			
		||||
  PinosSpaAlsaSinkPrivate *priv = sink->priv;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaCommand cmd;
 | 
			
		||||
 | 
			
		||||
  g_debug ("spa-alsa-sink %p: destroy pipeline", sink);
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_DEACTIVATE;
 | 
			
		||||
  if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
 | 
			
		||||
    g_debug ("got error %d", res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,6 @@ typedef struct _SpaCommand SpaCommand;
 | 
			
		|||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  SPA_COMMAND_INVALID                 =  0,
 | 
			
		||||
  SPA_COMMAND_ACTIVATE,
 | 
			
		||||
  SPA_COMMAND_DEACTIVATE,
 | 
			
		||||
  SPA_COMMAND_START,
 | 
			
		||||
  SPA_COMMAND_STOP,
 | 
			
		||||
  SPA_COMMAND_FLUSH,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ typedef enum {
 | 
			
		|||
  SPA_RESULT_INVALID_DIRECTION         = -23,
 | 
			
		||||
  SPA_RESULT_TOO_MANY_PORTS            = -24,
 | 
			
		||||
  SPA_RESULT_INVALID_PROPERTY_ACCESS   = -25,
 | 
			
		||||
  SPA_RESULT_UNEXPECTED                = -26,
 | 
			
		||||
} SpaResult;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,8 +31,8 @@ typedef struct _SpaEvent SpaEvent;
 | 
			
		|||
/**
 | 
			
		||||
 * SpaEventType:
 | 
			
		||||
 * @SPA_EVENT_TYPE_INVALID: invalid event, should be ignored
 | 
			
		||||
 * @SPA_EVENT_TYPE_ACTIVATED: emited when the ACTIVATE command completes
 | 
			
		||||
 * @SPA_EVENT_TYPE_DEACTIVATED: emited when the DEACTIVATE command completes
 | 
			
		||||
 * @SPA_EVENT_TYPE_STARTED: emited when the START command completes
 | 
			
		||||
 * @SPA_EVENT_TYPE_STOPPED: emited when the STOP command completes
 | 
			
		||||
 * @SPA_EVENT_TYPE_CAN_PULL_OUTPUT: emited when an async node has output that can be pulled
 | 
			
		||||
 * @SPA_EVENT_TYPE_CAN_PUSH_INTPUT: emited when more data can be pushed to an async node
 | 
			
		||||
 * @SPA_EVENT_TYPE_PULL_INPUT: emited when data needs to be provided on an input
 | 
			
		||||
| 
						 | 
				
			
			@ -46,8 +46,8 @@ typedef struct _SpaEvent SpaEvent;
 | 
			
		|||
 */
 | 
			
		||||
typedef enum {
 | 
			
		||||
  SPA_EVENT_TYPE_INVALID                  = 0,
 | 
			
		||||
  SPA_EVENT_TYPE_ACTIVATED,
 | 
			
		||||
  SPA_EVENT_TYPE_DEACTIVATED,
 | 
			
		||||
  SPA_EVENT_TYPE_STARTED,
 | 
			
		||||
  SPA_EVENT_TYPE_STOPPED,
 | 
			
		||||
  SPA_EVENT_TYPE_CAN_PULL_OUTPUT,
 | 
			
		||||
  SPA_EVENT_TYPE_CAN_PUSH_INTPUT,
 | 
			
		||||
  SPA_EVENT_TYPE_PULL_INPUT,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -265,6 +265,7 @@ struct _SpaNode {
 | 
			
		|||
   * SpaNode::set_port_format:
 | 
			
		||||
   * @handle: a #SpaHandle
 | 
			
		||||
   * @port_id: the port to configure
 | 
			
		||||
   * @test_only: only check if the format is accepted
 | 
			
		||||
   * @format: a #SpaFormat with the format
 | 
			
		||||
   *
 | 
			
		||||
   * Set a format on @port_id of @node.
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +283,7 @@ struct _SpaNode {
 | 
			
		|||
   */
 | 
			
		||||
  SpaResult   (*set_port_format)      (SpaHandle        *handle,
 | 
			
		||||
                                       uint32_t          port_id,
 | 
			
		||||
                                       int               test_only,
 | 
			
		||||
                                       bool              test_only,
 | 
			
		||||
                                       const SpaFormat  *format);
 | 
			
		||||
  /**
 | 
			
		||||
   * SpaNode::get_port_format:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,12 +79,14 @@ typedef enum {
 | 
			
		|||
/* SpaPropRangeType:
 | 
			
		||||
 * @SPA_PROP_RANGE_TYPE_NONE: no range specified, full range of type applies
 | 
			
		||||
 * @SPA_PROP_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max
 | 
			
		||||
 * @SPA_PROP_RANGE_TYPE_STEP: range contains 3 values, min, max and step
 | 
			
		||||
 * @SPA_PROP_RANGE_TYPE_ENUM: range contains enum of possible values
 | 
			
		||||
 * @SPA_PROP_RANGE_TYPE_FLAGS: range contains flags of possible values
 | 
			
		||||
 */
 | 
			
		||||
typedef enum {
 | 
			
		||||
  SPA_PROP_RANGE_TYPE_NONE      = 0,
 | 
			
		||||
  SPA_PROP_RANGE_TYPE_MIN_MAX,
 | 
			
		||||
  SPA_PROP_RANGE_TYPE_STEP,
 | 
			
		||||
  SPA_PROP_RANGE_TYPE_ENUM,
 | 
			
		||||
  SPA_PROP_RANGE_TYPE_FLAGS,
 | 
			
		||||
} SpaPropRangeType;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,15 @@
 | 
			
		|||
#include <spa/audio/raw.h>
 | 
			
		||||
#include <spa/audio/format.h>
 | 
			
		||||
 | 
			
		||||
static const SpaAudioRawInfo default_info = {
 | 
			
		||||
  SPA_AUDIO_FORMAT_S16,
 | 
			
		||||
  SPA_AUDIO_FLAG_NONE,
 | 
			
		||||
  SPA_AUDIO_LAYOUT_INTERLEAVED,
 | 
			
		||||
  44100,
 | 
			
		||||
  2,
 | 
			
		||||
  0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint32_t format_values[] = {
 | 
			
		||||
  SPA_AUDIO_FORMAT_S8,
 | 
			
		||||
  SPA_AUDIO_FORMAT_U8,
 | 
			
		||||
| 
						 | 
				
			
			@ -98,13 +107,6 @@ static const SpaPropRangeInfo format_format_range[] = {
 | 
			
		|||
  { "F64BE", "F64BE", sizeof (uint32_t), &format_values[29] },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint32_t default_format = SPA_AUDIO_FORMAT_S16;
 | 
			
		||||
static const uint32_t default_flags = SPA_AUDIO_FLAG_NONE;
 | 
			
		||||
static const uint32_t default_layout = SPA_AUDIO_LAYOUT_INTERLEAVED;
 | 
			
		||||
static const uint32_t default_rate = 44100;
 | 
			
		||||
static const uint32_t default_channels = 2;
 | 
			
		||||
static const uint32_t default_channel_mask = 0;
 | 
			
		||||
 | 
			
		||||
static const uint32_t format_layouts[] = {
 | 
			
		||||
  SPA_AUDIO_LAYOUT_INTERLEAVED,
 | 
			
		||||
  SPA_AUDIO_LAYOUT_NON_INTERLEAVED,
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +140,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_FORMAT,       "format", "The media format",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_format,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.format,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (format_format_range), format_format_range,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.format),
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +149,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_FLAGS,        "flags", "Sample Flags",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_flags,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.flags,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_FLAGS, SPA_N_ELEMENTS (flags_range), flags_range,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.flags),
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +158,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_LAYOUT,       "layout", "Sample Layout",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_layout,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.layout,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (layouts_range), layouts_range,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.layout),
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +167,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_RATE,         "rate", "Audio sample rate",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_rate,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.rate,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.rate),
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +176,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_CHANNELS,     "channels", "Audio channels",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_channels,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.channels,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.channels),
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +185,7 @@ static const SpaPropInfo raw_format_prop_info[] =
 | 
			
		|||
  { SPA_PROP_ID_AUDIO_CHANNEL_MASK, "channel-mask", "Audio channel mask",
 | 
			
		||||
                                    SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                    SPA_PROP_TYPE_BITMASK, sizeof (uint32_t),
 | 
			
		||||
                                    sizeof (uint32_t), &default_channel_mask,
 | 
			
		||||
                                    sizeof (uint32_t), &default_info.channel_mask,
 | 
			
		||||
                                    SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                                    NULL,
 | 
			
		||||
                                    offsetof (SpaAudioRawFormat, info.channel_mask),
 | 
			
		||||
| 
						 | 
				
			
			@ -210,12 +212,7 @@ spa_audio_raw_format_init (SpaAudioRawFormat *format)
 | 
			
		|||
  format->format.props.set_prop = spa_props_generic_set_prop;
 | 
			
		||||
  format->format.props.get_prop = spa_props_generic_get_prop;
 | 
			
		||||
  format->unset_mask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 4);
 | 
			
		||||
  format->info.format = default_format;
 | 
			
		||||
  format->info.flags = default_flags;
 | 
			
		||||
  format->info.layout = default_layout;
 | 
			
		||||
  format->info.rate = default_rate;
 | 
			
		||||
  format->info.channels = default_channels;
 | 
			
		||||
  format->info.channel_mask = default_channel_mask;
 | 
			
		||||
  format->info = default_info;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@ reset_alsa_sink_props (SpaALSASinkProps *props)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  bool opened;
 | 
			
		||||
  snd_pcm_t *handle;
 | 
			
		||||
  snd_output_t *output;
 | 
			
		||||
  snd_pcm_sframes_t buffer_size;
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +78,6 @@ struct _SpaALSASink {
 | 
			
		|||
  SpaALSASinkProps tmp_props;
 | 
			
		||||
  SpaALSASinkProps props;
 | 
			
		||||
 | 
			
		||||
  bool activated;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -223,47 +222,37 @@ spa_alsa_sink_node_send_command (SpaHandle     *handle,
 | 
			
		|||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (!this->activated) {
 | 
			
		||||
        spa_alsa_open (this);
 | 
			
		||||
        this->activated = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->activated) {
 | 
			
		||||
        spa_alsa_close (this);
 | 
			
		||||
        this->activated = false;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      spa_alsa_start (this);
 | 
			
		||||
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STARTED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      spa_alsa_stop (this);
 | 
			
		||||
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STOPPED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
| 
						 | 
				
			
			@ -375,7 +364,7 @@ spa_alsa_sink_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		|||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                    uint32_t         port_id,
 | 
			
		||||
                                    int              test_only,
 | 
			
		||||
                                    bool             test_only,
 | 
			
		||||
                                    const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -310,6 +310,9 @@ spa_alsa_open (SpaALSASink *this)
 | 
			
		|||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  if (state->opened)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed");
 | 
			
		||||
 | 
			
		||||
  printf ("Playback device is '%s'\n", this->props.device);
 | 
			
		||||
| 
						 | 
				
			
			@ -321,15 +324,36 @@ spa_alsa_open (SpaALSASink *this)
 | 
			
		|||
                       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 int
 | 
			
		||||
spa_alsa_start (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  if (spa_alsa_open (this) < 0)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  CHECK (set_hwparams (this), "hwparams");
 | 
			
		||||
  CHECK (set_swparams (this), "swparams");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -352,16 +376,7 @@ spa_alsa_stop (SpaALSASink *this)
 | 
			
		|||
    state->running = false;
 | 
			
		||||
    pthread_join (state->thread, NULL);
 | 
			
		||||
  }
 | 
			
		||||
  spa_alsa_close (this);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_alsa_close (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err = 0;
 | 
			
		||||
 | 
			
		||||
  CHECK (snd_pcm_close (state->handle), "close failed");
 | 
			
		||||
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -137,38 +137,36 @@ spa_audiomixer_node_send_command (SpaHandle     *handle,
 | 
			
		|||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STARTED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STOPPED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +328,7 @@ spa_audiomixer_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		|||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                     uint32_t         port_id,
 | 
			
		||||
                                     int              test_only,
 | 
			
		||||
                                     bool             test_only,
 | 
			
		||||
                                     const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,38 +169,36 @@ spa_audiotestsrc_node_send_command (SpaHandle     *handle,
 | 
			
		|||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STARTED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STOPPED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
| 
						 | 
				
			
			@ -307,7 +305,7 @@ spa_audiotestsrc_node_enum_port_formats (SpaHandle        *handle,
 | 
			
		|||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                       uint32_t         port_id,
 | 
			
		||||
                                       int              test_only,
 | 
			
		||||
                                       bool             test_only,
 | 
			
		||||
                                       const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,8 @@
 | 
			
		|||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
#include <libv4l2.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <linux/videodev2.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/video/format.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -45,11 +45,7 @@ reset_v4l2_source_props (SpaV4l2SourceProps *props)
 | 
			
		|||
  strncpy (props->device, default_device, 64);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int fd;
 | 
			
		||||
  pthread_t thread;
 | 
			
		||||
  bool running;
 | 
			
		||||
} SpaV4l2State;
 | 
			
		||||
#define MAX_BUFFERS     256
 | 
			
		||||
 | 
			
		||||
typedef struct _V4l2Buffer V4l2Buffer;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,29 +55,42 @@ struct _V4l2Buffer {
 | 
			
		|||
  SpaMetaHeader header;
 | 
			
		||||
  SpaData data[1];
 | 
			
		||||
  V4l2Buffer *next;
 | 
			
		||||
  uint32_t index;
 | 
			
		||||
  SpaV4l2Source *source;
 | 
			
		||||
  bool outstanding;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  bool opened;
 | 
			
		||||
  int fd;
 | 
			
		||||
  struct v4l2_capability cap;
 | 
			
		||||
  struct v4l2_format fmt;
 | 
			
		||||
  enum v4l2_buf_type type;
 | 
			
		||||
  struct v4l2_requestbuffers reqbuf;
 | 
			
		||||
  pthread_t thread;
 | 
			
		||||
  bool running;
 | 
			
		||||
  V4l2Buffer buffers[MAX_BUFFERS];
 | 
			
		||||
  V4l2Buffer *ready;
 | 
			
		||||
  uint32_t ready_count;
 | 
			
		||||
} SpaV4l2State;
 | 
			
		||||
 | 
			
		||||
struct _SpaV4l2Source {
 | 
			
		||||
  SpaHandle handle;
 | 
			
		||||
 | 
			
		||||
  SpaV4l2SourceProps tmp_props;
 | 
			
		||||
  SpaV4l2SourceProps props;
 | 
			
		||||
 | 
			
		||||
  bool activated;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaVideoRawFormat query_format;
 | 
			
		||||
  SpaVideoRawFormat current_format;
 | 
			
		||||
  SpaVideoRawFormat raw_format[2];
 | 
			
		||||
  SpaFormat *current_format;
 | 
			
		||||
 | 
			
		||||
  SpaV4l2State state;
 | 
			
		||||
 | 
			
		||||
  SpaPortInfo info;
 | 
			
		||||
  SpaPortStatus status;
 | 
			
		||||
 | 
			
		||||
  V4l2Buffer buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "v4l2-utils.c"
 | 
			
		||||
| 
						 | 
				
			
			@ -181,48 +190,39 @@ spa_v4l2_source_node_send_command (SpaHandle     *handle,
 | 
			
		|||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (!this->activated) {
 | 
			
		||||
        spa_v4l2_open (this);
 | 
			
		||||
        this->activated = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->activated) {
 | 
			
		||||
        spa_v4l2_close (this);
 | 
			
		||||
        this->activated = false;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      spa_v4l2_start (this);
 | 
			
		||||
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STARTED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      spa_v4l2_stop (this);
 | 
			
		||||
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STOPPED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
| 
						 | 
				
			
			@ -317,12 +317,12 @@ spa_v4l2_source_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		|||
 | 
			
		||||
  switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      spa_video_raw_format_init (&this->query_format);
 | 
			
		||||
      spa_video_raw_format_init (&this->raw_format[0]);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_ENUM_END;
 | 
			
		||||
  }
 | 
			
		||||
  *format = &this->query_format.format;
 | 
			
		||||
  *format = &this->raw_format[0].format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -330,11 +330,13 @@ spa_v4l2_source_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		|||
static SpaResult
 | 
			
		||||
spa_v4l2_source_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                      uint32_t         port_id,
 | 
			
		||||
                                      int              test_only,
 | 
			
		||||
                                      bool             test_only,
 | 
			
		||||
                                      const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2Source *this = (SpaV4l2Source *) handle;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaFormat *f, *tf;
 | 
			
		||||
  size_t fs;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
| 
						 | 
				
			
			@ -343,14 +345,30 @@ spa_v4l2_source_node_set_port_format (SpaHandle       *handle,
 | 
			
		|||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (format == NULL) {
 | 
			
		||||
    this->have_format = false;
 | 
			
		||||
    this->current_format = NULL;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((res = spa_video_raw_format_parse (format, &this->current_format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if (format->media_type == SPA_MEDIA_TYPE_VIDEO) {
 | 
			
		||||
    if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) {
 | 
			
		||||
      if ((res = spa_video_raw_format_parse (format, &this->raw_format[0]) < 0))
 | 
			
		||||
        return res;
 | 
			
		||||
 | 
			
		||||
  this->have_format = true;
 | 
			
		||||
      f = &this->raw_format[0].format;
 | 
			
		||||
      tf = &this->raw_format[1].format;
 | 
			
		||||
      fs = sizeof (SpaVideoRawFormat);
 | 
			
		||||
    } else
 | 
			
		||||
      return SPA_RESULT_INVALID_MEDIA_TYPE;
 | 
			
		||||
  } else
 | 
			
		||||
    return SPA_RESULT_INVALID_MEDIA_TYPE;
 | 
			
		||||
 | 
			
		||||
  if (spa_v4l2_set_format (this, f, test_only) < 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_MEDIA_TYPE;
 | 
			
		||||
 | 
			
		||||
  if (!test_only) {
 | 
			
		||||
    memcpy (tf, f, fs);
 | 
			
		||||
    this->current_format = tf;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -368,10 +386,10 @@ spa_v4l2_source_node_get_port_format (SpaHandle        *handle,
 | 
			
		|||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
  if (this->current_format == NULL)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *format = &this->current_format.format;
 | 
			
		||||
  *format = this->current_format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -442,23 +460,41 @@ spa_v4l2_source_node_pull_port_output (SpaHandle      *handle,
 | 
			
		|||
                                       SpaOutputInfo  *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2Source *this = (SpaV4l2Source *) handle;
 | 
			
		||||
  SpaV4l2State *state;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
  bool have_error = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  state = &this->state;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    V4l2Buffer *b;
 | 
			
		||||
 | 
			
		||||
    if (info[i].port_id != 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_PORT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->have_format) {
 | 
			
		||||
    if (this->current_format == NULL) {
 | 
			
		||||
      info[i].status = SPA_RESULT_NO_FORMAT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (state->ready_count == 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_UNEXPECTED;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    b = state->ready;
 | 
			
		||||
    state->ready = b->next;
 | 
			
		||||
    state->ready_count--;
 | 
			
		||||
 | 
			
		||||
    b->outstanding = true;
 | 
			
		||||
 | 
			
		||||
    info[i].buffer = &b->buffer;
 | 
			
		||||
    info[i].status = SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,35 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
 | 
			
		||||
#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; }
 | 
			
		||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
xioctl (int fd, int request, void *arg)
 | 
			
		||||
{
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  do {
 | 
			
		||||
    err = ioctl (fd, request, arg);
 | 
			
		||||
  } while (err == -1 && errno == EINTR);
 | 
			
		||||
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_v4l2_open (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  struct stat st;
 | 
			
		||||
 | 
			
		||||
  if (state->opened)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  fprintf (stderr, "Playback device is '%s'\n", this->props.device);
 | 
			
		||||
 | 
			
		||||
  if (stat (this->props.device, &st) < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -28,42 +43,511 @@ spa_v4l2_open (SpaV4l2Source *this)
 | 
			
		|||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->state.fd = open (this->props.device, O_RDWR | O_NONBLOCK, 0);
 | 
			
		||||
  state->fd = open (this->props.device, O_RDWR | O_NONBLOCK, 0);
 | 
			
		||||
 | 
			
		||||
  if (this->state.fd == -1) {
 | 
			
		||||
    fprintf(stderr, "Cannot open '%s': %d, %s\n",
 | 
			
		||||
  if (state->fd == -1) {
 | 
			
		||||
    fprintf (stderr, "Cannot open '%s': %d, %s\n",
 | 
			
		||||
            this->props.device, errno, strerror (errno));
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_QUERYCAP, &state->cap) < 0) {
 | 
			
		||||
    perror ("QUERYCAP");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((state->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
 | 
			
		||||
    fprintf (stderr, "%s is no video capture device\n", this->props.device);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  state->opened = true;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static SpaVideoFormat
 | 
			
		||||
fourcc_to_video_format (uint32_t fourcc)
 | 
			
		||||
{
 | 
			
		||||
  SpaVideoFormat format;
 | 
			
		||||
 | 
			
		||||
  switch (fourcc) {
 | 
			
		||||
    case V4L2_PIX_FMT_GREY:    /*  8  Greyscale     */
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_GRAY8;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_Y16:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_GRAY16_LE;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_Y16_BE:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_GRAY16_BE;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_XRGB555:
 | 
			
		||||
    case V4L2_PIX_FMT_RGB555:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_RGB15;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_XRGB555X:
 | 
			
		||||
    case V4L2_PIX_FMT_RGB555X:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_BGR15;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_RGB565:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_RGB16;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_RGB24:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_RGB;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_BGR24:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_BGR;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_XRGB32:
 | 
			
		||||
    case V4L2_PIX_FMT_RGB32:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_xRGB;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_XBGR32:
 | 
			
		||||
    case V4L2_PIX_FMT_BGR32:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_BGRx;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_ABGR32:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_BGRA;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_ARGB32:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_ARGB;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV12:
 | 
			
		||||
    case V4L2_PIX_FMT_NV12M:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV12;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV12MT:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV12_64Z32;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV21:
 | 
			
		||||
    case V4L2_PIX_FMT_NV21M:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV21;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YVU410:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_YVU9;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YUV410:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_YUV9;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YUV420:
 | 
			
		||||
    case V4L2_PIX_FMT_YUV420M:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_I420;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YUYV:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_YUY2;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YVU420:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_YV12;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_UYVY:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_UYVY;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YUV411P:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_Y41B;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YUV422P:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_Y42B;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_YVYU:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_YVYU;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV16:
 | 
			
		||||
    case V4L2_PIX_FMT_NV16M:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV16;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV61:
 | 
			
		||||
    case V4L2_PIX_FMT_NV61M:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV61;
 | 
			
		||||
      break;
 | 
			
		||||
    case V4L2_PIX_FMT_NV24:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_NV24;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      format = SPA_VIDEO_FORMAT_UNKNOWN;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return format;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static uint32_t
 | 
			
		||||
video_format_to_fourcc (SpaVideoFormat format)
 | 
			
		||||
{
 | 
			
		||||
  uint32_t fourcc;
 | 
			
		||||
 | 
			
		||||
  switch (format) {
 | 
			
		||||
    case SPA_VIDEO_FORMAT_I420:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YUV420;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_YUY2:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YUYV;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_UYVY:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_UYVY;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_YV12:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YVU420;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_Y41B:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YUV411P;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_Y42B:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YUV422P;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_NV12:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV12;
 | 
			
		||||
      break;
 | 
			
		||||
   case SPA_VIDEO_FORMAT_NV12_64Z32:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV12MT;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_NV21:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV21;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_NV16:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV16;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_NV61:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV61;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_NV24:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_NV24;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_YVYU:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_YVYU;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_RGB15:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_RGB555;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_RGB16:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_RGB565;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_RGB:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_RGB24;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_BGR:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_BGR24;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_xRGB:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_RGB32;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_ARGB:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_RGB32;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_BGRx:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_BGR32;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_BGRA:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_BGR32;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_GRAY8:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_GREY;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_GRAY16_LE:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_Y16;
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_VIDEO_FORMAT_GRAY16_BE:
 | 
			
		||||
      fourcc = V4L2_PIX_FMT_Y16_BE;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      fourcc = 0;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return fourcc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_v4l2_set_format (SpaV4l2Source *this, SpaFormat *format, bool try_only)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
 | 
			
		||||
  struct v4l2_format reqfmt, fmt;
 | 
			
		||||
 | 
			
		||||
  CLEAR (fmt);
 | 
			
		||||
  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
 | 
			
		||||
  if (format->media_type == SPA_MEDIA_TYPE_VIDEO) {
 | 
			
		||||
    if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) {
 | 
			
		||||
      SpaVideoRawFormat *f = (SpaVideoRawFormat *) format;
 | 
			
		||||
 | 
			
		||||
      fmt.fmt.pix.pixelformat = video_format_to_fourcc (f->info.format);
 | 
			
		||||
      fmt.fmt.pix.width = f->info.width;
 | 
			
		||||
      fmt.fmt.pix.height = f->info.height;
 | 
			
		||||
      fmt.fmt.pix.field = V4L2_FIELD_ANY;
 | 
			
		||||
      fprintf (stderr, "set %08x %dx%d\n", fmt.fmt.pix.pixelformat,
 | 
			
		||||
          fmt.fmt.pix.width, fmt.fmt.pix.height);
 | 
			
		||||
    } else
 | 
			
		||||
      return -1;
 | 
			
		||||
  } else
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  reqfmt = fmt;
 | 
			
		||||
 | 
			
		||||
  if (spa_v4l2_open (this) < 0)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  if (xioctl (state->fd, cmd, &fmt) < 0) {
 | 
			
		||||
    perror ("VIDIOC_S_FMT");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (reqfmt.fmt.pix.pixelformat != fmt.fmt.pix.pixelformat ||
 | 
			
		||||
      reqfmt.fmt.pix.width != fmt.fmt.pix.width ||
 | 
			
		||||
      reqfmt.fmt.pix.height != fmt.fmt.pix.height)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  if (try_only)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  state->fmt = fmt;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_v4l2_close (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  if (close(this->state.fd))
 | 
			
		||||
    fprintf(stderr, "Cannot close %d, %s\n",
 | 
			
		||||
            errno, strerror (errno));
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
 | 
			
		||||
  if (!state->opened)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (close(state->fd))
 | 
			
		||||
    perror ("close");
 | 
			
		||||
 | 
			
		||||
  state->fd = -1;
 | 
			
		||||
  state->opened = false;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mmap_read (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  struct v4l2_buffer buf;
 | 
			
		||||
  V4l2Buffer *b;
 | 
			
		||||
 | 
			
		||||
  CLEAR(buf);
 | 
			
		||||
  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
  buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_DQBUF, &buf) < 0) {
 | 
			
		||||
    switch (errno) {
 | 
			
		||||
      case EAGAIN:
 | 
			
		||||
        return 0;
 | 
			
		||||
      case EIO:
 | 
			
		||||
      default:
 | 
			
		||||
        perror ("VIDIOC_DQBUF");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  fprintf (stderr, "captured buffer %d\n", buf.index);
 | 
			
		||||
 | 
			
		||||
  b = &state->buffers[buf.index];
 | 
			
		||||
  b->next = state->ready;
 | 
			
		||||
  state->ready = b;
 | 
			
		||||
  state->ready_count++;
 | 
			
		||||
 | 
			
		||||
  this->state.fd = -1;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *
 | 
			
		||||
v4l2_loop (void *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2Source *this = user_data;
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  struct pollfd fds[1];
 | 
			
		||||
 | 
			
		||||
  fds[0].fd = state->fd;
 | 
			
		||||
  fds[0].events = POLLIN | POLLPRI | POLLERR;
 | 
			
		||||
  fds[0].revents = 0;
 | 
			
		||||
 | 
			
		||||
  while (state->running) {
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    r = poll (fds, 1, 2000);
 | 
			
		||||
    if (r < 0) {
 | 
			
		||||
      if (errno == EINTR)
 | 
			
		||||
        continue;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (r == 0) {
 | 
			
		||||
      fprintf (stderr, "select timeout\n");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mmap_read (this) < 0)
 | 
			
		||||
     break;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      SpaEvent event;
 | 
			
		||||
 | 
			
		||||
      event.refcount = 1;
 | 
			
		||||
      event.notify = NULL;
 | 
			
		||||
      event.type = SPA_EVENT_TYPE_CAN_PULL_OUTPUT;
 | 
			
		||||
      event.port_id = 0;
 | 
			
		||||
      event.size = 0;
 | 
			
		||||
      event.data = NULL;
 | 
			
		||||
 | 
			
		||||
      this->event_cb (&this->handle, &event, this->user_data);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
static void
 | 
			
		||||
v4l2_buffer_free (void *data)
 | 
			
		||||
{
 | 
			
		||||
  V4l2Buffer *b = (V4l2Buffer *) data;
 | 
			
		||||
  SpaV4l2Source *this = b->source;
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  struct v4l2_buffer buf;
 | 
			
		||||
 | 
			
		||||
  CLEAR (buf);
 | 
			
		||||
  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
  buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
  buf.index = b->index;
 | 
			
		||||
 | 
			
		||||
  b->buffer.refcount = 1;
 | 
			
		||||
  b->outstanding = false;
 | 
			
		||||
 | 
			
		||||
  fprintf (stderr, "queue buffer %d\n", buf.index);
 | 
			
		||||
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_QBUF, &buf) < 0) {
 | 
			
		||||
    perror ("VIDIOC_QBUF");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mmap_init (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  struct v4l2_requestbuffers reqbuf;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  CLEAR(reqbuf);
 | 
			
		||||
  reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
  reqbuf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
  reqbuf.count = MAX_BUFFERS;
 | 
			
		||||
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
 | 
			
		||||
    perror ("VIDIOC_REQBUFS");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fprintf (stderr, "got %d buffers\n", reqbuf.count);
 | 
			
		||||
  if (reqbuf.count < 2) {
 | 
			
		||||
    fprintf (stderr, "can't allocate enough buffers\n");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state->reqbuf = reqbuf;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < reqbuf.count; i++) {
 | 
			
		||||
    struct v4l2_buffer buf;
 | 
			
		||||
    V4l2Buffer *b;
 | 
			
		||||
 | 
			
		||||
    CLEAR (buf);
 | 
			
		||||
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
    buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
    buf.index = i;
 | 
			
		||||
 | 
			
		||||
    if (xioctl (state->fd, VIDIOC_QUERYBUF, &buf) < 0) {
 | 
			
		||||
      perror ("VIDIOC_QUERYBUF");
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    b = &state->buffers[i];
 | 
			
		||||
    b->index = i;
 | 
			
		||||
    b->source = this;
 | 
			
		||||
    b->buffer.refcount = 1;
 | 
			
		||||
    b->buffer.notify = v4l2_buffer_free;
 | 
			
		||||
    b->buffer.size = buf.length;
 | 
			
		||||
    b->buffer.n_metas = 1;
 | 
			
		||||
    b->buffer.metas = b->meta;
 | 
			
		||||
    b->buffer.n_datas = 1;
 | 
			
		||||
    b->buffer.datas = b->data;
 | 
			
		||||
 | 
			
		||||
    b->header.flags = 0;
 | 
			
		||||
    b->header.seq = 0;
 | 
			
		||||
    b->header.pts = 0;
 | 
			
		||||
    b->header.dts_offset = 0;
 | 
			
		||||
 | 
			
		||||
    b->meta[0].type = SPA_META_TYPE_HEADER;
 | 
			
		||||
    b->meta[0].data = &b->header;
 | 
			
		||||
    b->meta[0].size = sizeof (b->header);
 | 
			
		||||
 | 
			
		||||
    b->data[0].type = SPA_DATA_TYPE_MEMPTR;
 | 
			
		||||
    b->data[0].size = buf.length;
 | 
			
		||||
    b->data[0].data = mmap (NULL,
 | 
			
		||||
                            buf.length,
 | 
			
		||||
                            PROT_READ | PROT_WRITE,
 | 
			
		||||
                            MAP_SHARED,
 | 
			
		||||
                            state->fd,
 | 
			
		||||
                            buf.m.offset);
 | 
			
		||||
 | 
			
		||||
    if (b->data[0].data == MAP_FAILED) {
 | 
			
		||||
      perror ("mmap");
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  for (i = 0; i < state->reqbuf.count; ++i) {
 | 
			
		||||
    struct v4l2_buffer buf;
 | 
			
		||||
 | 
			
		||||
    CLEAR (buf);
 | 
			
		||||
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
    buf.memory = V4L2_MEMORY_MMAP;
 | 
			
		||||
    buf.index = i;
 | 
			
		||||
 | 
			
		||||
    if (xioctl (state->fd, VIDIOC_QBUF, &buf) < 0) {
 | 
			
		||||
      perror ("VIDIOC_QBUF");
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
userptr_init (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
read_init (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_v4l2_start (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  int err;
 | 
			
		||||
  enum v4l2_buf_type type;
 | 
			
		||||
 | 
			
		||||
  if (spa_v4l2_open (this) < 0)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  if (state->cap.capabilities & V4L2_CAP_STREAMING) {
 | 
			
		||||
    if (mmap_init (this) < 0)
 | 
			
		||||
      if (userptr_init (this) < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
  } else if (state->cap.capabilities & V4L2_CAP_READWRITE) {
 | 
			
		||||
    if (read_init (this) < 0)
 | 
			
		||||
      return -1;
 | 
			
		||||
  } else
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
 | 
			
		||||
    perror ("VIDIOC_STREAMON");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state->running = true;
 | 
			
		||||
  if ((err = pthread_create (&state->thread, NULL, v4l2_loop, this)) != 0) {
 | 
			
		||||
    printf ("can't create thread: %d", err);
 | 
			
		||||
    printf ("can't create thread: %d %s", err, strerror (err));
 | 
			
		||||
    state->running = false;
 | 
			
		||||
  }
 | 
			
		||||
  return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -73,10 +557,19 @@ static int
 | 
			
		|||
spa_v4l2_stop (SpaV4l2Source *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaV4l2State *state = &this->state;
 | 
			
		||||
  enum v4l2_buf_type type;
 | 
			
		||||
 | 
			
		||||
  if (state->running) {
 | 
			
		||||
    state->running = false;
 | 
			
		||||
    pthread_join (state->thread, NULL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 | 
			
		||||
  if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
 | 
			
		||||
    perror ("VIDIOC_STREAMOFF");
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spa_v4l2_close (this);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -147,38 +147,36 @@ spa_volume_node_send_command (SpaHandle     *handle,
 | 
			
		|||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STARTED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_STOPPED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
| 
						 | 
				
			
			@ -288,7 +286,7 @@ spa_volume_node_enum_port_formats (SpaHandle        *handle,
 | 
			
		|||
static SpaResult
 | 
			
		||||
spa_volume_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                 uint32_t         port_id,
 | 
			
		||||
                                 int              test_only,
 | 
			
		||||
                                 bool             test_only,
 | 
			
		||||
                                 const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,3 +2,8 @@ executable('test-mixer', 'test-mixer.c',
 | 
			
		|||
           include_directories : inc,
 | 
			
		||||
           dependencies : [dl_lib],
 | 
			
		||||
           install : false)
 | 
			
		||||
 | 
			
		||||
executable('test-v4l2', 'test-v4l2.c',
 | 
			
		||||
           include_directories : inc,
 | 
			
		||||
           dependencies : [dl_lib],
 | 
			
		||||
           install : false)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,72 +239,34 @@ negotiate_formats (AppData *data)
 | 
			
		|||
  if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->sink_node->set_port_format (data->sink, 0, 0, format)) < 0)
 | 
			
		||||
  if ((res = data->sink_node->set_port_format (data->sink, 0, false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, 0, 0, format)) < 0)
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, 0, false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->mix_node->add_port (data->mix, SPA_DIRECTION_INPUT, &data->mix_ports[0])) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[0], 0, format)) < 0)
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[0], false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source1_node->set_port_format (data->source1, 0, 0, format)) < 0)
 | 
			
		||||
  if ((res = data->source1_node->set_port_format (data->source1, 0, false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->mix_node->add_port (data->mix, SPA_DIRECTION_INPUT, &data->mix_ports[1])) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[1], 0, format)) < 0)
 | 
			
		||||
  if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[1], false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source2_node->set_port_format (data->source2, 0, 0, format)) < 0)
 | 
			
		||||
  if ((res = data->source2_node->set_port_format (data->source2, 0, false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
start_nodes (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaCommand cmd;
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_ACTIVATE;
 | 
			
		||||
  if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->mix_node->send_command (data->mix, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->source1_node->send_command (data->source1, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->source2_node->send_command (data->source1, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
stop_nodes (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaCommand cmd;
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_DEACTIVATE;
 | 
			
		||||
  if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->mix_node->send_command (data->mix, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->source1_node->send_command (data->source1, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  if ((res = data->source2_node->send_command (data->source1, &cmd)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
run_async_sink (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -337,17 +299,7 @@ main (int argc, char *argv[])
 | 
			
		|||
    printf ("can't negotiate nodes: %d\n", res);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if ((res = start_nodes (&data)) < 0) {
 | 
			
		||||
    printf ("can't start nodes: %d\n", res);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  run_async_sink (&data);
 | 
			
		||||
 | 
			
		||||
  if ((res = stop_nodes (&data)) < 0) {
 | 
			
		||||
    printf ("can't stop nodes: %d\n", res);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										198
									
								
								spa/tests/test-v4l2.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								spa/tests/test-v4l2.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,198 @@
 | 
			
		|||
/* Spa
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/video/format.h>
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaHandle *source;
 | 
			
		||||
  const SpaNode *source_node;
 | 
			
		||||
} AppData;
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char *name)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  void *hnd;
 | 
			
		||||
  SpaEnumHandleFactoryFunc enum_func;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
 | 
			
		||||
  if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) {
 | 
			
		||||
    printf ("can't load %s: %s\n", lib, dlerror());
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
  if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) {
 | 
			
		||||
    printf ("can't find enum function\n");
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (i = 0; ;i++) {
 | 
			
		||||
    const SpaHandleFactory *factory;
 | 
			
		||||
    const void *iface;
 | 
			
		||||
 | 
			
		||||
    if ((res = enum_func (i, &factory)) < 0) {
 | 
			
		||||
      if (res != SPA_RESULT_ENUM_END)
 | 
			
		||||
        printf ("can't enumerate factories: %d\n", res);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    if (strcmp (factory->name, name))
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    if ((res = factory->instantiate (factory, handle)) < 0) {
 | 
			
		||||
      printf ("can't make factory instance: %d\n", res);
 | 
			
		||||
      return res;
 | 
			
		||||
    }
 | 
			
		||||
    if ((res = (*handle)->get_interface (*handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) {
 | 
			
		||||
      printf ("can't get interface %d\n", res);
 | 
			
		||||
      return res;
 | 
			
		||||
    }
 | 
			
		||||
    *node = iface;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_source_event (SpaHandle *handle, SpaEvent *event, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
  AppData *data = user_data;
 | 
			
		||||
 | 
			
		||||
  switch (event->type) {
 | 
			
		||||
    case SPA_EVENT_TYPE_CAN_PULL_OUTPUT:
 | 
			
		||||
    {
 | 
			
		||||
      SpaOutputInfo info[1] = { 0, };
 | 
			
		||||
      SpaResult res;
 | 
			
		||||
 | 
			
		||||
      if ((res = data->source_node->pull_port_output (data->source, 1, info)) < 0)
 | 
			
		||||
        printf ("got pull error %d\n", res);
 | 
			
		||||
 | 
			
		||||
      if (info[0].buffer) {
 | 
			
		||||
        spa_buffer_unref (info[0].buffer);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      printf ("got event %d\n", event->type);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
make_nodes (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaProps *props;
 | 
			
		||||
  SpaPropValue value;
 | 
			
		||||
 | 
			
		||||
  if ((res = make_node (&data->source, &data->source_node, "plugins/v4l2/libspa-v4l2.so", "v4l2-source")) < 0) {
 | 
			
		||||
    printf ("can't create v4l2-source: %d\n", res);
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
  data->source_node->set_event_callback (data->source, on_source_event, data);
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source_node->get_props (data->source, &props)) < 0)
 | 
			
		||||
    printf ("got get_props error %d\n", res);
 | 
			
		||||
 | 
			
		||||
  value.type = SPA_PROP_TYPE_STRING;
 | 
			
		||||
  value.value = "/dev/video1";
 | 
			
		||||
  value.size = strlen (value.value)+1;
 | 
			
		||||
  props->set_prop (props, spa_props_index_for_name (props, "device"), &value);
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source_node->set_props (data->source, props)) < 0)
 | 
			
		||||
    printf ("got set_props error %d\n", res);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
negotiate_formats (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaFormat *format;
 | 
			
		||||
  SpaProps *props;
 | 
			
		||||
  uint32_t val;
 | 
			
		||||
  SpaPropValue value;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source_node->enum_port_formats (data->source, 0, 0, &format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  props = &format->props;
 | 
			
		||||
 | 
			
		||||
  value.type = SPA_PROP_TYPE_UINT32;
 | 
			
		||||
  value.size = sizeof (uint32_t);
 | 
			
		||||
  value.value = &val;
 | 
			
		||||
 | 
			
		||||
  val = SPA_VIDEO_FORMAT_YUY2;
 | 
			
		||||
  if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  val = 320;
 | 
			
		||||
  if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_WIDTH), &value)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
  val = 240;
 | 
			
		||||
  if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_HEIGHT), &value)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  if ((res = data->source_node->set_port_format (data->source, 0, false, format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
run_async_source (AppData *data)
 | 
			
		||||
{
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
  SpaCommand cmd;
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_START;
 | 
			
		||||
  if ((res = data->source_node->send_command (data->source, &cmd)) < 0)
 | 
			
		||||
    printf ("got error %d\n", res);
 | 
			
		||||
 | 
			
		||||
  printf ("sleeping for 10 seconds\n");
 | 
			
		||||
  sleep (10);
 | 
			
		||||
 | 
			
		||||
  cmd.type = SPA_COMMAND_STOP;
 | 
			
		||||
  if ((res = data->source_node->send_command (data->source, &cmd)) < 0)
 | 
			
		||||
    printf ("got error %d\n", res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main (int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
  AppData data;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if ((res = make_nodes (&data)) < 0) {
 | 
			
		||||
    printf ("can't make nodes: %d\n", res);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  if ((res = negotiate_formats (&data)) < 0) {
 | 
			
		||||
    printf ("can't negotiate nodes: %d\n", res);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  run_async_source (&data);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue