diff --git a/doc/design.txt b/doc/design.txt index bc0c0d8ba..b15a5f508 100644 --- a/doc/design.txt +++ b/doc/design.txt @@ -332,5 +332,25 @@ Types: message length is end +7: refresh request + Request a new refresh point + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last-id | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | request-type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PTS ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | .... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + : the last seen id + : the type of request + 0 = keyframe + 1 = keyframe+full headers + : the timestamp when the refresh should be, + 0 for as soon as possible diff --git a/pinos/Makefile.am b/pinos/Makefile.am index 9c15f652a..3a2136405 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -193,7 +193,7 @@ libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \ libpinos_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GST_CFLAGS) libpinos_@PINOS_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -libpinos_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LTLIBICONV) $(GST_LIBS) +libpinos_@PINOS_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LTLIBICONV) $(GST_LIBS) $(GST_BASE_LIBS) -lgstvideo-1.0 ################################### # Daemon core library # diff --git a/pinos/client/buffer.c b/pinos/client/buffer.c index 35794e168..56f6c2955 100644 --- a/pinos/client/buffer.c +++ b/pinos/client/buffer.c @@ -721,3 +721,55 @@ pinos_buffer_builder_add_format_change (PinosBufferBuilder *builder, return TRUE; } + +/** + * pinos_buffer_iter_parse_refresh_request: + * @iter: a #PinosBufferIter + * @payload: a #PinosPacketRefreshRequest + * + * Parse a #PINOS_PACKET_TYPE_REFRESH_REQUEST packet from @iter into @payload. + * + * Returns: %TRUE on success. + */ +gboolean +pinos_buffer_iter_parse_refresh_request (PinosBufferIter *iter, + PinosPacketRefreshRequest *payload) +{ + struct stack_iter *si = PPSI (iter); + + g_return_val_if_fail (is_valid_iter (iter), FALSE); + g_return_val_if_fail (si->type == PINOS_PACKET_TYPE_REFRESH_REQUEST, FALSE); + + if (si->size < sizeof (PinosPacketRefreshRequest)) + return FALSE; + + memcpy (payload, si->data, sizeof (*payload)); + + return TRUE; +} + +/** + * pinos_buffer_builder_add_refresh_request: + * @builder: a #PinosBufferBuilder + * @payload: a #PinosPacketRefreshRequest + * + * Add a #PINOS_PACKET_TYPE_REFRESH_REQUEST payload in @payload to @builder. + * + * Returns: %TRUE on success + */ +gboolean +pinos_buffer_builder_add_refresh_request (PinosBufferBuilder *builder, + PinosPacketRefreshRequest *payload) +{ + struct stack_builder *sb = PPSB (builder); + PinosPacketRefreshRequest *p; + + g_return_val_if_fail (is_valid_builder (builder), FALSE); + + p = builder_add_packet (sb, + PINOS_PACKET_TYPE_REFRESH_REQUEST, + sizeof (PinosPacketRefreshRequest)); + memcpy (p, payload, sizeof (*payload)); + + return TRUE; +} diff --git a/pinos/client/buffer.h b/pinos/client/buffer.h index 72d9bdfe8..7b8d91974 100644 --- a/pinos/client/buffer.h +++ b/pinos/client/buffer.h @@ -65,6 +65,7 @@ gpointer pinos_buffer_steal (PinosBuffer *buffer, * that a previously received fd-payload is no longer in use. * @PINOS_PACKET_TYPE_FORMAT_CHANGE: a format change. * @PINOS_PACKET_TYPE_PROPERTY_CHANGE: one or more property changes. + * @PINOS_PACKET_TYPE_REFRESH_REQUEST: ask for a new keyframe * * The possible packet types. */ @@ -77,6 +78,7 @@ typedef enum { PINOS_PACKET_TYPE_RELEASE_FD_PAYLOAD = 4, PINOS_PACKET_TYPE_FORMAT_CHANGE = 5, PINOS_PACKET_TYPE_PROPERTY_CHANGE = 6, + PINOS_PACKET_TYPE_REFRESH_REQUEST = 7, } PinosPacketType; @@ -215,5 +217,25 @@ gboolean pinos_buffer_iter_parse_property_change (PinosBufferIter gboolean pinos_buffer_builder_add_property_change (PinosBufferBuilder *builder, PinosPacketPropertyChange *payload); +/* refresh request packets */ +/** + * PinosPacketRefreshRequest: + * @last_id: last frame seen frame id + * @request_type: the type of the request + * @pts: the timestamp of the requested key frame, 0 = as soon as possible + * + * A refresh request packet. This packet is sent to trigger a new keyframe. + */ +typedef struct { + guint32 last_id; + guint32 request_type; + gint64 pts; +} PinosPacketRefreshRequest; + +gboolean pinos_buffer_iter_parse_refresh_request (PinosBufferIter *iter, + PinosPacketRefreshRequest *payload); +gboolean pinos_buffer_builder_add_refresh_request (PinosBufferBuilder *builder, + PinosPacketRefreshRequest *payload); + #endif /* __PINOS_BUFFER_H__ */ diff --git a/pinos/gst/gstpinospay.c b/pinos/gst/gstpinospay.c index ba0337102..33785a246 100644 --- a/pinos/gst/gstpinospay.c +++ b/pinos/gst/gstpinospay.c @@ -46,6 +46,8 @@ #include "gsttmpfileallocator.h" #include +#include + #include #include @@ -198,13 +200,19 @@ client_buffer_received (GstPinosPay *pay, GstBuffer *buffer, { PinosBuffer pbuf; PinosBufferIter it; + PinosBufferBuilder b; GstMapInfo info; const gchar *client_path; + gboolean have_out = FALSE; client_path = g_object_get_data (obj, "pinos-client-path"); if (client_path == NULL) return; + if (pay->pinos_input) { + pinos_buffer_builder_init (&b); + } + gst_buffer_map (buffer, &info, GST_MAP_READ); pinos_buffer_init_data (&pbuf, info.data, info.size, NULL); pinos_buffer_iter_init (&it, &pbuf); @@ -224,12 +232,54 @@ client_buffer_received (GstPinosPay *pay, GstBuffer *buffer, pinos_fd_manager_remove (pay->fdmanager, client_path, id); break; } + case PINOS_PACKET_TYPE_REFRESH_REQUEST: + { + PinosPacketRefreshRequest p; + + if (!pinos_buffer_iter_parse_refresh_request (&it, &p)) + continue; + + GST_LOG ("refresh request"); + if (!pay->pinos_input) { + gst_pad_push_event (pay->sinkpad, + gst_video_event_new_upstream_force_key_unit (p.pts, + p.request_type == 1, 0)); + } else { + pinos_buffer_builder_add_refresh_request (&b, &p); + have_out = TRUE; + } + break; + } default: break; } } gst_buffer_unmap (buffer, &info); pinos_buffer_clear (&pbuf); + + if (pay->pinos_input) { + GstBuffer *outbuf; + GstEvent *ev; + gsize size; + gpointer data; + + if (have_out) { + pinos_buffer_builder_end (&b, &pbuf); + + data = pinos_buffer_steal (&pbuf, &size, NULL); + + outbuf = gst_buffer_new_wrapped (data, size); + ev = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstNetworkMessage", + "object", G_TYPE_OBJECT, pay, + "buffer", GST_TYPE_BUFFER, outbuf, NULL)); + gst_buffer_unref (outbuf); + + gst_pad_push_event (pay->sinkpad, ev); + } else { + pinos_buffer_builder_clear (&b); + } + } } static gboolean @@ -264,9 +314,10 @@ gst_pinos_pay_src_event (GstPad * pad, GstObject * parent, GstEvent * event) client_buffer_received (pay, buf, obj); gst_buffer_unref (buf); g_object_unref (obj); + } - gst_event_unref (event); res = TRUE; + gst_event_unref (event); break; } default: diff --git a/pinos/gst/gstpinossink.c b/pinos/gst/gstpinossink.c index 18615b096..f6caa3a35 100644 --- a/pinos/gst/gstpinossink.c +++ b/pinos/gst/gstpinossink.c @@ -41,6 +41,7 @@ #include #include +#include #include "gsttmpfileallocator.h" @@ -290,7 +291,19 @@ on_new_buffer (GObject *gobject, g_hash_table_remove (pinossink->fdids, GINT_TO_POINTER (p.id)); break; } + case PINOS_PACKET_TYPE_REFRESH_REQUEST: + { + PinosPacketRefreshRequest p; + if (!pinos_buffer_iter_parse_refresh_request (&it, &p)) + continue; + + GST_LOG ("refresh request"); + gst_pad_push_event (GST_BASE_SINK_PAD (pinossink), + gst_video_event_new_upstream_force_key_unit (p.pts, + p.request_type == 1, 0)); + break; + } default: break; } diff --git a/pinos/gst/gstpinossrc.c b/pinos/gst/gstpinossrc.c index 88d16bda3..e0c59db4e 100644 --- a/pinos/gst/gstpinossrc.c +++ b/pinos/gst/gstpinossrc.c @@ -42,6 +42,7 @@ #include #include #include +#include static GQuark fdpayload_data_quark; @@ -84,6 +85,7 @@ static gboolean gst_pinos_src_unlock (GstBaseSrc * basesrc); static gboolean gst_pinos_src_unlock_stop (GstBaseSrc * basesrc); static gboolean gst_pinos_src_start (GstBaseSrc * basesrc); static gboolean gst_pinos_src_stop (GstBaseSrc * basesrc); +static gboolean gst_pinos_src_event (GstBaseSrc * src, GstEvent * event); static void gst_pinos_src_set_property (GObject * object, guint prop_id, @@ -245,7 +247,7 @@ gst_pinos_src_class_init (GstPinosSrcClass * klass) gstbasesrc_class->unlock_stop = gst_pinos_src_unlock_stop; gstbasesrc_class->start = gst_pinos_src_start; gstbasesrc_class->stop = gst_pinos_src_stop; - + gstbasesrc_class->event = gst_pinos_src_event; gstpushsrc_class->create = gst_pinos_src_create; GST_DEBUG_CATEGORY_INIT (pinos_src_debug, "pinossrc", 0, @@ -724,6 +726,55 @@ gst_pinos_src_unlock_stop (GstBaseSrc * basesrc) return TRUE; } +static gboolean +gst_pinos_src_event (GstBaseSrc * src, GstEvent * event) +{ + gboolean res = FALSE; + GstPinosSrc *pinossrc; + + pinossrc = GST_PINOS_SRC (src); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + if (gst_video_event_is_force_key_unit (event)) { + GstClockTime running_time; + gboolean all_headers; + guint count; + PinosPacketRefreshRequest refresh; + PinosBufferBuilder b; + PinosBuffer pbuf; + + gst_video_event_parse_upstream_force_key_unit (event, + &running_time, &all_headers, &count); + + refresh.last_id = 0; + refresh.request_type = all_headers ? 1 : 0; + refresh.pts = running_time; + + pinos_buffer_builder_init (&b); + pinos_buffer_builder_add_refresh_request (&b, &refresh); + pinos_buffer_builder_end (&b, &pbuf); + + GST_OBJECT_LOCK (pinossrc); + if (pinossrc->stream_state == PINOS_STREAM_STATE_STREAMING) { + GST_DEBUG_OBJECT (pinossrc, "send refresh request"); + pinos_stream_send_buffer (pinossrc->stream, &pbuf); + } + GST_OBJECT_UNLOCK (pinossrc); + + pinos_buffer_clear (&pbuf); + res = TRUE; + } else { + res = GST_BASE_SRC_CLASS (parent_class)->event (src, event); + } + break; + default: + res = GST_BASE_SRC_CLASS (parent_class)->event (src, event); + break; + } + return res; +} + static GstFlowReturn gst_pinos_src_create (GstPushSrc * psrc, GstBuffer ** buffer) {