add clock

Add a gstreamer pinos clock that reports the time at the server
This commit is contained in:
Wim Taymans 2016-09-12 12:28:51 +02:00
parent 0b380dd43e
commit f86b50d202
11 changed files with 259 additions and 72 deletions

View file

@ -268,6 +268,7 @@ plugin_LTLIBRARIES = libgstpinos.la
libgstpinos_la_SOURCES = \ libgstpinos_la_SOURCES = \
gst/gstburstcache.c \ gst/gstburstcache.c \
gst/gstpinos.c \ gst/gstpinos.c \
gst/gstpinosclock.c \
gst/gstpinosformat.c \ gst/gstpinosformat.c \
gst/gstpinosdeviceprovider.c \ gst/gstpinosdeviceprovider.c \
gst/gstpinossrc.c \ gst/gstpinossrc.c \
@ -281,7 +282,7 @@ libgstpinos_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GLIB_LIBS) $(LIBM) -lgst
libgstpinos_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) libgstpinos_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = gst/gstburstcache.h gst/gstpinossrc.h \ noinst_HEADERS = gst/gstburstcache.h gst/gstpinossrc.h \
gst/gstpinosformat.h \ gst/gstpinosclock.h gst/gstpinosformat.h \
gst/gstpinossink.h gst/gstpinosdeviceprovider.h gst/gstpinossink.h gst/gstpinosdeviceprovider.h
################################### ###################################

View file

@ -95,7 +95,8 @@ struct _PinosStreamPrivate
GArray *buffer_ids; GArray *buffer_ids;
gboolean in_order; gboolean in_order;
gint64 last_timestamp; gint64 last_ticks;
gint32 last_rate;
gint64 last_monotonic; gint64 last_monotonic;
}; };
@ -637,6 +638,23 @@ send_need_input (PinosStream *stream, uint32_t port_id)
spa_control_clear (&control); spa_control_clear (&control);
} }
static void
add_request_clock_update (PinosStream *stream, SpaControlBuilder *builder)
{
SpaControlCmdNodeEvent cne;
SpaNodeEvent ne;
SpaNodeEventRequestClockUpdate rcu;
cne.event = ≠
ne.type = SPA_NODE_EVENT_TYPE_REQUEST_CLOCK_UPDATE;
ne.data = &rcu;
ne.size = sizeof (rcu);
rcu.update_mask = SPA_NODE_EVENT_REQUEST_CLOCK_UPDATE_TIME;
rcu.timestamp = 0;
rcu.offset = 0;
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_NODE_EVENT, &cne);
}
static void static void
send_reuse_buffer (PinosStream *stream, uint32_t port_id, uint32_t buffer_id) send_reuse_buffer (PinosStream *stream, uint32_t port_id, uint32_t buffer_id)
{ {
@ -794,6 +812,7 @@ handle_node_command (PinosStream *stream,
g_debug ("stream %p: start", stream); g_debug ("stream %p: start", stream);
control_builder_init (stream, &builder); control_builder_init (stream, &builder);
add_request_clock_update (stream, &builder);
if (priv->direction == PINOS_DIRECTION_INPUT) if (priv->direction == PINOS_DIRECTION_INPUT)
add_need_input (stream, &builder, 0); add_need_input (stream, &builder, 0);
add_state_change (stream, &builder, SPA_NODE_STATE_STREAMING); add_state_change (stream, &builder, SPA_NODE_STATE_STREAMING);
@ -816,8 +835,8 @@ handle_node_command (PinosStream *stream,
case SPA_NODE_COMMAND_CLOCK_UPDATE: case SPA_NODE_COMMAND_CLOCK_UPDATE:
{ {
SpaNodeCommandClockUpdate *cu = command->data; SpaNodeCommandClockUpdate *cu = command->data;
g_debug ("got clock update %"PRId64", %"PRId64, cu->timestamp, cu->monotonic_time); priv->last_ticks = cu->ticks;
priv->last_timestamp = cu->timestamp; priv->last_rate = cu->rate;
priv->last_monotonic = cu->monotonic_time; priv->last_monotonic = cu->monotonic_time;
break; break;
} }
@ -1050,20 +1069,9 @@ on_timeout (gpointer user_data)
PinosStreamPrivate *priv = stream->priv; PinosStreamPrivate *priv = stream->priv;
SpaControlBuilder builder; SpaControlBuilder builder;
SpaControl control; SpaControl control;
SpaControlCmdNodeEvent cne;
SpaNodeEvent ne;
SpaNodeEventRequestClockUpdate rcu;
cne.event = ≠
ne.type = SPA_NODE_EVENT_TYPE_REQUEST_CLOCK_UPDATE;
ne.data = &rcu;
ne.size = sizeof (rcu);
rcu.update_mask = SPA_NODE_EVENT_REQUEST_CLOCK_UPDATE_TIME;
rcu.timestamp = 0;
rcu.offset = 0;
control_builder_init (stream, &builder); control_builder_init (stream, &builder);
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_NODE_EVENT, &cne); add_request_clock_update (stream, &builder);
spa_control_builder_end (&builder, &control); spa_control_builder_end (&builder, &control);
if (spa_control_write (&control, priv->fd) < 0) if (spa_control_write (&control, priv->fd) < 0)
@ -1540,6 +1548,26 @@ pinos_stream_disconnect (PinosStream *stream)
return TRUE; return TRUE;
} }
gboolean
pinos_stream_get_time (PinosStream *stream,
PinosTime *time)
{
PinosStreamPrivate *priv;
gint64 now, elapsed;
g_return_val_if_fail (PINOS_IS_STREAM (stream), FALSE);
priv = stream->priv;
g_return_val_if_fail (time, FALSE);
now = g_get_monotonic_time ();
elapsed = now - (priv->last_monotonic / 1000);
time->ticks = priv->last_ticks + (elapsed * priv->last_rate) / G_USEC_PER_SEC;
time->rate = priv->last_rate;
return TRUE;
}
/** /**
* pinos_stream_get_empty_buffer: * pinos_stream_get_empty_buffer:
* @stream: a #PinosStream * @stream: a #PinosStream

View file

@ -64,6 +64,11 @@ typedef enum {
PINOS_STREAM_MODE_RINGBUFFER = 1, PINOS_STREAM_MODE_RINGBUFFER = 1,
} PinosStreamMode; } PinosStreamMode;
typedef struct {
gint64 ticks;
gint32 rate;
} PinosTime;
/** /**
* PinosStream: * PinosStream:
* *
@ -110,6 +115,9 @@ gboolean pinos_stream_start_allocation (PinosStream *stream,
gboolean pinos_stream_start (PinosStream *stream); gboolean pinos_stream_start (PinosStream *stream);
gboolean pinos_stream_stop (PinosStream *stream); gboolean pinos_stream_stop (PinosStream *stream);
gboolean pinos_stream_get_time (PinosStream *stream,
PinosTime *time);
guint pinos_stream_get_empty_buffer (PinosStream *stream); guint pinos_stream_get_empty_buffer (PinosStream *stream);
gboolean pinos_stream_recycle_buffer (PinosStream *stream, gboolean pinos_stream_recycle_buffer (PinosStream *stream,
guint id); guint id);

88
pinos/gst/gstpinosclock.c Normal file
View file

@ -0,0 +1,88 @@
/* GStreamer
* 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 Street, Suite 500,
* Boston, MA 02110-1335, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include "gstpinosclock.h"
GST_DEBUG_CATEGORY_STATIC (gst_pinos_clock_debug_category);
#define GST_CAT_DEFAULT gst_pinos_clock_debug_category
G_DEFINE_TYPE (GstPinosClock, gst_pinos_clock, GST_TYPE_SYSTEM_CLOCK);
GstClock *
gst_pinos_clock_new (PinosStream *stream)
{
GstPinosClock *clock;
clock = g_object_new (GST_TYPE_PINOS_CLOCK, NULL);
clock->stream = stream;
return GST_CLOCK_CAST (clock);
}
static GstClockTime
gst_pinos_clock_get_internal_time (GstClock * clock)
{
GstPinosClock *pclock = (GstPinosClock *) clock;
GstClockTime result;
PinosTime t;
pinos_stream_get_time (pclock->stream, &t);
result = gst_util_uint64_scale_int (t.ticks, GST_SECOND, t.rate);
GST_DEBUG ("%"PRId64", %d %"PRId64, t.ticks, t.rate, result);
return result;
}
static void
gst_pinos_clock_finalize (GObject * object)
{
GstPinosClock *clock = GST_PINOS_CLOCK (object);
GST_DEBUG_OBJECT (clock, "finalize");
G_OBJECT_CLASS (gst_pinos_clock_parent_class)->finalize (object);
}
static void
gst_pinos_clock_class_init (GstPinosClockClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstClockClass *gstclock_class = GST_CLOCK_CLASS (klass);
gobject_class->finalize = gst_pinos_clock_finalize;
gstclock_class->get_internal_time = gst_pinos_clock_get_internal_time;
GST_DEBUG_CATEGORY_INIT (gst_pinos_clock_debug_category, "pinosclock", 0,
"debug category for pinosclock object");
}
static void
gst_pinos_clock_init (GstPinosClock * clock)
{
GST_OBJECT_FLAG_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER);
}

62
pinos/gst/gstpinosclock.h Normal file
View file

@ -0,0 +1,62 @@
/* GStreamer
* 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.
*/
#ifndef __GST_PINOS_CLOCK_H__
#define __GST_PINOS_CLOCK_H__
#include <gst/gst.h>
#include <client/pinos.h>
G_BEGIN_DECLS
#define GST_TYPE_PINOS_CLOCK \
(gst_pinos_clock_get_type())
#define GST_PINOS_CLOCK(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PINOS_CLOCK,GstPinosClock))
#define GST_PINOS_CLOCK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PINOS_CLOCK,GstPinosClockClass))
#define GST_IS_PINOS_CLOCK(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PINOS_CLOCK))
#define GST_IS_PINOS_CLOCK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PINOS_CLOCK))
#define GST_PINOS_CLOCK_GET_CLASS(klass) \
(G_TYPE_INSTANCE_GET_CLASS ((klass), GST_TYPE_PINOS_CLOCK, GstPinosClockClass))
typedef struct _GstPinosClock GstPinosClock;
typedef struct _GstPinosClockClass GstPinosClockClass;
struct _GstPinosClock {
GstSystemClock parent;
PinosStream *stream;
};
struct _GstPinosClockClass {
GstSystemClockClass parent_class;
};
GType gst_pinos_clock_get_type (void);
GstClock * gst_pinos_clock_new (PinosStream *stream);
G_END_DECLS
#endif /* __GST_PINOS_CLOCK_H__ */

View file

@ -48,6 +48,7 @@
#include <spa/include/spa/memory.h> #include <spa/include/spa/memory.h>
#include <spa/include/spa/buffer.h> #include <spa/include/spa/buffer.h>
#include "gstpinosclock.h"
static GQuark process_mem_data_quark; static GQuark process_mem_data_quark;
@ -269,6 +270,7 @@ gst_pinos_src_init (GstPinosSrc * src)
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
GST_OBJECT_FLAG_SET (src, GST_ELEMENT_FLAG_PROVIDE_CLOCK); GST_OBJECT_FLAG_SET (src, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
g_queue_init (&src->queue); g_queue_init (&src->queue);
@ -523,31 +525,11 @@ parse_stream_properties (GstPinosSrc *pinossrc, PinosProperties *props)
var = pinos_properties_get (props, "pinos.latency.max"); var = pinos_properties_get (props, "pinos.latency.max");
pinossrc->max_latency = var ? (GstClockTime) atoi (var) : GST_CLOCK_TIME_NONE; pinossrc->max_latency = var ? (GstClockTime) atoi (var) : GST_CLOCK_TIME_NONE;
var = pinos_properties_get (props, "pinos.clock.type"); pinossrc->is_live = TRUE;
if (var != NULL) { pinossrc->min_latency = 100000000;
GST_DEBUG_OBJECT (pinossrc, "got clock type %s", var); pinossrc->max_latency = GST_CLOCK_TIME_NONE;
if (strcmp (var, "gst.net.time.provider") == 0) {
const gchar *address;
gint port;
GstClockTime base_time;
address = pinos_properties_get (props, "pinos.clock.address");
port = atoi (pinos_properties_get (props, "pinos.clock.port"));
base_time = atoll (pinos_properties_get (props, "pinos.clock.base-time"));
GST_DEBUG_OBJECT (pinossrc, "making net clock for %s:%d %" G_GUINT64_FORMAT, address, port, base_time);
if (pinossrc->clock)
gst_object_unref (pinossrc->clock);
pinossrc->clock = gst_net_client_clock_new ("pinosclock", address, port, base_time);
gst_element_post_message (GST_ELEMENT_CAST (pinossrc),
gst_message_new_clock_provide (GST_OBJECT_CAST (pinossrc),
pinossrc->clock, TRUE));
}
}
} }
static gboolean static gboolean
gst_pinos_src_stream_start (GstPinosSrc *pinossrc) gst_pinos_src_stream_start (GstPinosSrc *pinossrc)
{ {
@ -817,6 +799,8 @@ gst_pinos_src_query (GstBaseSrc * src, GstQuery * query)
switch (GST_QUERY_TYPE (query)) { switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY: case GST_QUERY_LATENCY:
pinossrc->min_latency = 10000000;
pinossrc->max_latency = GST_CLOCK_TIME_NONE;
gst_query_set_latency (query, pinossrc->is_live, pinossrc->min_latency, pinossrc->max_latency); gst_query_set_latency (query, pinossrc->is_live, pinossrc->min_latency, pinossrc->max_latency);
res = TRUE; res = TRUE;
break; break;
@ -1004,6 +988,7 @@ gst_pinos_src_open (GstPinosSrc * pinossrc)
g_signal_connect (pinossrc->stream, "add-buffer", (GCallback) on_add_buffer, pinossrc); g_signal_connect (pinossrc->stream, "add-buffer", (GCallback) on_add_buffer, pinossrc);
g_signal_connect (pinossrc->stream, "remove-buffer", (GCallback) on_remove_buffer, pinossrc); g_signal_connect (pinossrc->stream, "remove-buffer", (GCallback) on_remove_buffer, pinossrc);
g_signal_connect (pinossrc->stream, "new-buffer", (GCallback) on_new_buffer, pinossrc); g_signal_connect (pinossrc->stream, "new-buffer", (GCallback) on_new_buffer, pinossrc);
pinossrc->clock = gst_pinos_clock_new (pinossrc->stream);
pinos_main_loop_unlock (pinossrc->loop); pinos_main_loop_unlock (pinossrc->loop);
return TRUE; return TRUE;
@ -1031,6 +1016,7 @@ gst_pinos_src_close (GstPinosSrc * pinossrc)
g_main_context_unref (pinossrc->context); g_main_context_unref (pinossrc->context);
GST_OBJECT_LOCK (pinossrc); GST_OBJECT_LOCK (pinossrc);
pinossrc->stream_state = PINOS_STREAM_STATE_UNCONNECTED; pinossrc->stream_state = PINOS_STREAM_STATE_UNCONNECTED;
g_clear_object (&pinossrc->clock);
g_clear_object (&pinossrc->stream); g_clear_object (&pinossrc->stream);
GST_OBJECT_UNLOCK (pinossrc); GST_OBJECT_UNLOCK (pinossrc);
clear_queue (pinossrc); clear_queue (pinossrc);

View file

@ -292,6 +292,29 @@ suspend_node (PinosNode *this)
} }
static void
send_clock_update (PinosNode *this)
{
PinosNodePrivate *priv = this->priv;
if (priv->clock) {
SpaNodeCommand cmd;
SpaNodeCommandClockUpdate cu;
SpaResult res;
cmd.type = SPA_NODE_COMMAND_CLOCK_UPDATE;
cmd.data = &cu;
cmd.size = sizeof (cu);
cu.change_mask = SPA_NODE_COMMAND_CLOCK_UPDATE_TIME;
res = spa_clock_get_time (priv->clock, &cu.rate, &cu.ticks, &cu.monotonic_time);
cu.scale = (1 << 16) | 1;
cu.state = SPA_CLOCK_STATE_RUNNING;
if ((res = spa_node_send_command (this->node, &cmd)) < 0)
g_debug ("got error %d", res);
}
}
static gboolean static gboolean
node_set_state (PinosNode *this, node_set_state (PinosNode *this,
PinosNodeState state) PinosNodeState state)
@ -482,26 +505,9 @@ on_node_event (SpaNode *node, SpaNodeEvent *event, void *user_data)
break; break;
} }
case SPA_NODE_EVENT_TYPE_REQUEST_CLOCK_UPDATE: case SPA_NODE_EVENT_TYPE_REQUEST_CLOCK_UPDATE:
{ send_clock_update (this);
SpaResult res;
if (priv->clock) {
SpaNodeCommand cmd;
SpaNodeCommandClockUpdate cu;
cmd.type = SPA_NODE_COMMAND_CLOCK_UPDATE;
cmd.data = &cu;
cmd.size = sizeof (cu);
cu.change_mask = SPA_NODE_COMMAND_CLOCK_UPDATE_TIME;
res = spa_clock_get_time (priv->clock, &cu.timestamp, &cu.monotonic_time);
cu.scale = (1 << 16) | 1;
cu.state = SPA_CLOCK_STATE_RUNNING;
if ((res = spa_node_send_command (this->node, &cmd)) < 0)
g_debug ("got error %d", res);
}
break; break;
}
default: default:
g_debug ("node %p: got event %d", this, event->type); g_debug ("node %p: got event %d", this, event->type);
break; break;

View file

@ -107,7 +107,8 @@ struct _SpaClock {
const SpaProps *props); const SpaProps *props);
SpaResult (*get_time) (SpaClock *clock, SpaResult (*get_time) (SpaClock *clock,
int64_t *clock_time, int32_t *rate,
int64_t *ticks,
int64_t *monotonic_time); int64_t *monotonic_time);
}; };

View file

@ -48,11 +48,12 @@ struct _SpaNodeCommand {
/** /**
* SpaNodeCommandClockUpdate: * SpaNodeCommandClockUpdate:
* @change_mask: marks which fields are updated * @change_mask: marks which fields are updated
* @timestamp: the new timestamp, when @change_mask = 1<<0 * @rate: the number of @ticks per second
* @monotonic_time: the new monotonic time associated with @timestamp, when * @ticks: the new ticks, when @change_mask = 1<<0
* @change_mask = 1<<0 * @monotonic_time: the new monotonic time in nanoseconds associated with
* @ticks, when @change_mask = 1<<0
* @offset: the difference between the time when this update was generated * @offset: the difference between the time when this update was generated
* and @monotonic_time * and @monotonic_time in nanoseconds
* @scale: update to the speed stored as Q16.16, @change_mask = 1<<1 * @scale: update to the speed stored as Q16.16, @change_mask = 1<<1
* @state: the new clock state, when @change_mask = 1<<2 * @state: the new clock state, when @change_mask = 1<<2
*/ */
@ -61,7 +62,8 @@ typedef struct {
#define SPA_NODE_COMMAND_CLOCK_UPDATE_SCALE (1 << 1) #define SPA_NODE_COMMAND_CLOCK_UPDATE_SCALE (1 << 1)
#define SPA_NODE_COMMAND_CLOCK_UPDATE_STATE (1 << 2) #define SPA_NODE_COMMAND_CLOCK_UPDATE_STATE (1 << 2)
uint32_t change_mask; uint32_t change_mask;
int64_t timestamp; int32_t rate;
int64_t ticks;
int64_t monotonic_time; int64_t monotonic_time;
int64_t offset; int64_t offset;
int32_t scale; int32_t scale;

View file

@ -115,7 +115,7 @@ typedef struct {
SpaAllocParamBuffers param_buffers; SpaAllocParamBuffers param_buffers;
SpaPortStatus status; SpaPortStatus status;
int64_t last_timestamp; int64_t last_ticks;
int64_t last_monotonic; int64_t last_monotonic;
} SpaV4l2State; } SpaV4l2State;
@ -759,7 +759,8 @@ spa_v4l2_source_clock_set_props (SpaClock *clock,
static SpaResult static SpaResult
spa_v4l2_source_clock_get_time (SpaClock *clock, spa_v4l2_source_clock_get_time (SpaClock *clock,
int64_t *clock_time, int32_t *rate,
int64_t *ticks,
int64_t *monotonic_time) int64_t *monotonic_time)
{ {
SpaV4l2Source *this; SpaV4l2Source *this;
@ -771,8 +772,10 @@ spa_v4l2_source_clock_get_time (SpaClock *clock,
this = (SpaV4l2Source *) clock->handle; this = (SpaV4l2Source *) clock->handle;
state = &this->state[0]; state = &this->state[0];
if (clock_time) if (rate)
*clock_time = state->last_timestamp; *rate = 1000000;
if (ticks)
*ticks = state->last_ticks;
if (monotonic_time) if (monotonic_time)
*monotonic_time = state->last_monotonic; *monotonic_time = state->last_monotonic;

View file

@ -788,7 +788,8 @@ spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only)
state->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS | state->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS |
SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
state->info.maxbuffering = -1; state->info.maxbuffering = -1;
state->info.latency = -1; state->info.latency = (streamparm.parm.capture.timeperframe.numerator * 1000000000LL) /
streamparm.parm.capture.timeperframe.denominator;
state->info.n_params = 1; state->info.n_params = 1;
state->info.params = state->params; state->info.params = state->params;
@ -834,12 +835,13 @@ mmap_read (SpaV4l2Source *this)
if (buf.flags & V4L2_BUF_FLAG_ERROR) if (buf.flags & V4L2_BUF_FLAG_ERROR)
b->header.flags |= SPA_BUFFER_FLAG_CORRUPTED; b->header.flags |= SPA_BUFFER_FLAG_CORRUPTED;
state->last_ticks = (int64_t)buf.timestamp.tv_sec * 1000000 + (uint64_t)buf.timestamp.tv_usec;
b->header.seq = buf.sequence; b->header.seq = buf.sequence;
b->header.pts = (uint64_t)buf.timestamp.tv_sec * 1000000000lu + (uint64_t)buf.timestamp.tv_usec * 1000lu; b->header.pts = state->last_ticks * 1000;
state->last_timestamp = b->header.pts;
if (buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) if (buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC)
state->last_monotonic = state->last_timestamp; state->last_monotonic = b->header.pts;
else else
state->last_monotonic = SPA_TIME_INVALID; state->last_monotonic = SPA_TIME_INVALID;