• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer Smart Video Encoder element
2  * Copyright (C) <2010> Edward Hervey <bilboed@gmail.com>
3  * Copyright (C) <2020> Thibault Saunier <tsaunier@igalia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <string.h>
26 #include <gst/video/video.h>
27 #include "gstsmartencoder.h"
28 
29 GST_DEBUG_CATEGORY_STATIC (smart_encoder_debug);
30 #define GST_CAT_DEFAULT smart_encoder_debug
31 
32 /* FIXME : Update this with new caps */
33 /* WARNING : We can only allow formats with closed-GOP */
34 #define ALLOWED_CAPS "video/x-h263;video/x-intel-h263;"\
35   "video/x-vp8;"\
36   "video/x-vp9;"\
37   "video/x-h264;"\
38   "video/x-h265;"\
39   "video/mpeg,mpegversion=(int)1,systemstream=(boolean)false;"\
40   "video/mpeg,mpegversion=(int)2,systemstream=(boolean)false;"
41 
42 static GstStaticPadTemplate src_template =
43 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS (ALLOWED_CAPS)
46     );
47 
48 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS (ALLOWED_CAPS)
52     );
53 
54 G_DEFINE_TYPE (GstSmartEncoder, gst_smart_encoder, GST_TYPE_BIN);
55 
56 static void
smart_encoder_reset(GstSmartEncoder * self)57 smart_encoder_reset (GstSmartEncoder * self)
58 {
59   gst_segment_init (&self->internal_segment, GST_FORMAT_UNDEFINED);
60   gst_segment_init (&self->input_segment, GST_FORMAT_UNDEFINED);
61   gst_segment_init (&self->output_segment, GST_FORMAT_UNDEFINED);
62 
63   if (self->decoder) {
64     /* Clean up/remove internal encoding elements */
65     gst_element_set_state (self->encoder, GST_STATE_NULL);
66     gst_element_set_state (self->decoder, GST_STATE_NULL);
67     gst_clear_object (&self->internal_srcpad);
68     gst_element_remove_pad (GST_ELEMENT (self), self->internal_sinkpad);
69     gst_bin_remove (GST_BIN (self), gst_object_ref (self->encoder));
70     gst_bin_remove (GST_BIN (self), self->decoder);
71 
72     self->decoder = NULL;
73     self->internal_sinkpad = NULL;
74   }
75   gst_clear_event (&self->segment_event);
76 }
77 
78 static void
translate_timestamp_from_internal_to_src(GstSmartEncoder * self,GstClockTime * ts)79 translate_timestamp_from_internal_to_src (GstSmartEncoder * self,
80     GstClockTime * ts)
81 {
82   GstClockTime running_time;
83 
84   if (gst_segment_to_running_time_full (&self->internal_segment,
85           GST_FORMAT_TIME, *ts, &running_time) > 0)
86     *ts = running_time + self->output_segment.start;
87   else                          /* Negative timestamp */
88     *ts = self->output_segment.start - running_time;
89 }
90 
91 static GstFlowReturn
gst_smart_encoder_finish_buffer(GstSmartEncoder * self,GstBuffer * buf)92 gst_smart_encoder_finish_buffer (GstSmartEncoder * self, GstBuffer * buf)
93 {
94   translate_timestamp_from_internal_to_src (self, &GST_BUFFER_PTS (buf));
95   translate_timestamp_from_internal_to_src (self, &GST_BUFFER_DTS (buf));
96   GST_BUFFER_DTS (buf) = GST_BUFFER_DTS (buf);
97   if (self->last_dts > GST_BUFFER_DTS (buf)) {
98     /* Hack to always produces dts increasing DTS-s that are close to what the
99      * encoder produced. */
100     GST_BUFFER_DTS (buf) = self->last_dts + 1;
101   }
102   self->last_dts = GST_BUFFER_DTS (buf);
103 
104   return gst_pad_push (self->srcpad, buf);
105 }
106 
107 /*****************************************
108  *    Internal encoder/decoder pipeline  *
109  ******************************************/
110 static gboolean
internal_event_func(GstPad * pad,GstObject * parent,GstEvent * event)111 internal_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
112 {
113   GstSmartEncoder *self = GST_SMART_ENCODER (parent);
114 
115   switch (GST_EVENT_TYPE (event)) {
116     case GST_EVENT_EOS:
117       g_mutex_lock (&self->internal_flow_lock);
118       if (self->internal_flow == GST_FLOW_CUSTOM_SUCCESS)
119         self->internal_flow = GST_FLOW_OK;
120       g_cond_signal (&self->internal_flow_cond);
121       g_mutex_unlock (&self->internal_flow_lock);
122       break;
123     case GST_EVENT_SEGMENT:
124       gst_event_copy_segment (event, &self->internal_segment);
125 
126       if (self->output_segment.format == GST_FORMAT_UNDEFINED) {
127         gst_segment_init (&self->output_segment, GST_FORMAT_TIME);
128 
129         /* Ensure that we can represent negative DTS in our 'single' segment */
130         self->output_segment.start = 60 * 60 * GST_SECOND * 1000;
131         if (!gst_pad_push_event (self->srcpad,
132                 gst_event_new_segment (&self->output_segment))) {
133           GST_ERROR_OBJECT (self, "Could not push segment!");
134 
135           GST_ELEMENT_FLOW_ERROR (self, GST_FLOW_ERROR);
136 
137           return FALSE;
138         }
139       }
140 
141       break;
142     case GST_EVENT_CAPS:
143     {
144       return gst_pad_push_event (self->srcpad, event);
145     }
146     default:
147       break;
148   }
149 
150   return gst_pad_event_default (pad, parent, event);
151 }
152 
153 static GstFlowReturn
internal_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)154 internal_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
155 {
156   return gst_smart_encoder_finish_buffer (GST_SMART_ENCODER (parent), buf);
157 }
158 
159 static void
decodebin_src_pad_added_cb(GstElement * decodebin,GstPad * srcpad,GstSmartEncoder * self)160 decodebin_src_pad_added_cb (GstElement * decodebin, GstPad * srcpad,
161     GstSmartEncoder * self)
162 {
163   GstPadLinkReturn ret = gst_pad_link (srcpad, self->encoder->sinkpads->data);
164 
165   if (ret != GST_PAD_LINK_OK) {
166     GST_ERROR_OBJECT (self, "Could not link decoder with encoder! %s",
167         gst_pad_link_get_name (ret));
168     g_mutex_lock (&self->internal_flow_lock);
169     self->internal_flow = GST_FLOW_NOT_LINKED;
170     g_mutex_unlock (&self->internal_flow_lock);
171   }
172 }
173 
174 static gboolean
setup_recoder_pipeline(GstSmartEncoder * self)175 setup_recoder_pipeline (GstSmartEncoder * self)
176 {
177   GstPad *tmppad;
178   GstElement *capsfilter;
179   GstPadLinkReturn lret;
180 
181   /* Fast path */
182   if (G_UNLIKELY (self->decoder))
183     return TRUE;
184 
185   g_assert (self->encoder);
186   GST_DEBUG ("Creating internal decoder and encoder");
187 
188   /* Create decoder/encoder */
189   self->decoder = gst_element_factory_make ("decodebin", NULL);
190   if (G_UNLIKELY (self->decoder == NULL))
191     goto no_decoder;
192   g_signal_connect (self->decoder, "pad-added",
193       G_CALLBACK (decodebin_src_pad_added_cb), self);
194   gst_element_set_locked_state (self->decoder, TRUE);
195   gst_bin_add (GST_BIN (self), self->decoder);
196   gst_bin_add (GST_BIN (self), gst_object_ref (self->encoder));
197 
198   GST_DEBUG_OBJECT (self, "Creating internal pads");
199 
200   /* Create internal pads */
201 
202   /* Source pad which we'll use to feed data to decoders */
203   self->internal_srcpad = gst_pad_new ("internal_src", GST_PAD_SRC);
204   self->internal_sinkpad = gst_pad_new ("internal_sink", GST_PAD_SINK);
205   gst_pad_set_iterate_internal_links_function (self->internal_sinkpad, NULL);
206   if (!gst_element_add_pad (GST_ELEMENT (self), self->internal_sinkpad)) {
207     GST_ERROR_OBJECT (self, "Could not add internal sinkpad %" GST_PTR_FORMAT,
208         self->internal_sinkpad);
209     return FALSE;
210   }
211 
212   gst_pad_set_chain_function (self->internal_sinkpad,
213       GST_DEBUG_FUNCPTR (internal_chain));
214   gst_pad_set_event_function (self->internal_sinkpad,
215       GST_DEBUG_FUNCPTR (internal_event_func));
216   gst_pad_set_active (self->internal_sinkpad, TRUE);
217   gst_pad_set_active (self->internal_srcpad, TRUE);
218 
219   GST_DEBUG_OBJECT (self, "Linking pads to elements");
220 
221   /* Link everything */
222   capsfilter = gst_element_factory_make ("capsfilter", NULL);
223   if (!gst_bin_add (GST_BIN (self), capsfilter)) {
224     GST_ERROR_OBJECT (self, "Could not add capsfilter!");
225     return FALSE;
226   }
227 
228   gst_element_sync_state_with_parent (capsfilter);
229   if (!gst_element_link (self->encoder, capsfilter))
230     goto encoder_capsfilter_link_fail;
231   tmppad = gst_element_get_static_pad (capsfilter, "src");
232   if ((lret =
233           gst_pad_link_full (tmppad, self->internal_sinkpad,
234               GST_PAD_LINK_CHECK_NOTHING)) < GST_PAD_LINK_OK)
235     goto sinkpad_link_fail;
236   gst_object_unref (tmppad);
237 
238   tmppad = gst_element_get_static_pad (self->decoder, "sink");
239   if (GST_PAD_LINK_FAILED (gst_pad_link_full (self->internal_srcpad,
240               tmppad, GST_PAD_LINK_CHECK_NOTHING)))
241     goto srcpad_link_fail;
242   gst_object_unref (tmppad);
243 
244   GST_DEBUG ("Done creating internal elements/pads");
245 
246   return TRUE;
247 
248 no_decoder:
249   {
250     GST_WARNING ("Couldn't find a decodebin?!");
251     return FALSE;
252   }
253 
254 srcpad_link_fail:
255   {
256     gst_object_unref (tmppad);
257     GST_WARNING ("Couldn't link internal srcpad to decoder");
258     return FALSE;
259   }
260 
261 sinkpad_link_fail:
262   {
263     gst_object_unref (tmppad);
264     GST_WARNING ("Couldn't link encoder to internal sinkpad: %s",
265         gst_pad_link_get_name (lret));
266     return FALSE;
267   }
268 
269 encoder_capsfilter_link_fail:
270   {
271     GST_WARNING ("Couldn't link encoder to capsfilter");
272     return FALSE;
273   }
274 }
275 
276 static GstFlowReturn
gst_smart_encoder_reencode_gop(GstSmartEncoder * self)277 gst_smart_encoder_reencode_gop (GstSmartEncoder * self)
278 {
279   GstFlowReturn res = GST_FLOW_OK;
280   GstCaps *caps = NULL;
281 
282   GST_DEBUG_OBJECT (self, "Reencoding GOP!");
283   if (self->decoder == NULL) {
284     if (!setup_recoder_pipeline (self)) {
285       GST_ERROR_OBJECT (self, "Could not setup reencoder pipeline");
286       return GST_FLOW_ERROR;
287     }
288   }
289 
290   /* Activate elements */
291   /* Set elements to PAUSED */
292   gst_element_set_state (self->encoder, GST_STATE_PLAYING);
293   gst_element_set_state (self->decoder, GST_STATE_PLAYING);
294 
295   GST_INFO ("Pushing Flush start/stop to clean decoder/encoder");
296   gst_pad_push_event (self->internal_srcpad, gst_event_new_flush_start ());
297   gst_pad_push_event (self->internal_srcpad, gst_event_new_flush_stop (TRUE));
298 
299   /* push segment_event */
300   GST_INFO ("Pushing segment_event %" GST_PTR_FORMAT, self->segment_event);
301   gst_pad_push_event (self->internal_srcpad,
302       gst_event_ref (self->stream_start_event));
303   caps = gst_pad_get_current_caps (self->sinkpad);
304   gst_pad_push_event (self->internal_srcpad, gst_event_new_caps (caps));
305   gst_caps_unref (caps);
306 
307   gst_pad_push_event (self->internal_srcpad,
308       gst_event_ref (self->segment_event));
309 
310   /* Push buffers through our pads */
311   GST_DEBUG ("Pushing %d pending buffers", g_list_length (self->pending_gop));
312 
313   g_mutex_lock (&self->internal_flow_lock);
314   self->internal_flow = GST_FLOW_CUSTOM_SUCCESS;
315   g_mutex_unlock (&self->internal_flow_lock);
316   while (self->pending_gop) {
317     GstBuffer *buf = (GstBuffer *) self->pending_gop->data;
318 
319     self->pending_gop =
320         g_list_remove_link (self->pending_gop, self->pending_gop);
321     res = gst_pad_push (self->internal_srcpad, buf);
322     if (res == GST_FLOW_EOS) {
323       GST_INFO_OBJECT (self, "Got eos... waiting for the event"
324           " waiting for encoding to be done");
325       break;
326     }
327 
328     if (res != GST_FLOW_OK) {
329       GST_WARNING ("Error pushing pending buffers : %s",
330           gst_flow_get_name (res));
331       goto done;
332     }
333   }
334 
335   GST_DEBUG_OBJECT (self, "-> Drain encoder.");
336   gst_pad_push_event (self->internal_srcpad, gst_event_new_eos ());
337 
338   g_mutex_lock (&self->internal_flow_lock);
339   while (self->internal_flow == GST_FLOW_CUSTOM_SUCCESS) {
340     g_cond_wait (&self->internal_flow_cond, &self->internal_flow_lock);
341   }
342   g_mutex_unlock (&self->internal_flow_lock);
343 
344   res = self->internal_flow;
345 
346   GST_DEBUG_OBJECT (self, "Done reencoding GOP.");
347   gst_element_set_state (self->encoder, GST_STATE_NULL);
348   gst_element_set_state (self->decoder, GST_STATE_NULL);
349   GST_OBJECT_FLAG_UNSET (self->internal_sinkpad, GST_PAD_FLAG_EOS);
350   GST_OBJECT_FLAG_UNSET (self->internal_srcpad, GST_PAD_FLAG_EOS);
351 
352 done:
353   g_list_free_full (self->pending_gop, (GDestroyNotify) gst_buffer_unref);
354   self->pending_gop = NULL;
355 
356   return res;
357 }
358 
359 static gboolean
gst_smart_encoder_force_reencoding_for_caps(GstSmartEncoder * self)360 gst_smart_encoder_force_reencoding_for_caps (GstSmartEncoder * self)
361 {
362   const gchar *profile;
363   GstStructure *structure = gst_caps_get_structure (self->original_caps, 0);
364 
365   if (!gst_structure_has_name (structure, "video/x-vp9"))
366     return FALSE;
367 
368   if (!(profile = gst_structure_get_string (structure, "profile"))) {
369     GST_WARNING_OBJECT (self,
370         "No profile set on `vp9` stream, force reencoding");
371 
372     return TRUE;
373   }
374 
375   if (g_strcmp0 (profile, "0") && g_strcmp0 (profile, "2")) {
376     GST_INFO_OBJECT (self, "vp9 profile %s not supported for smart reencoding"
377         " as it might be using RGB stream which we can't handle properly"
378         " force reencoding", profile);
379     return TRUE;
380   }
381 
382   return FALSE;
383 }
384 
385 static GstFlowReturn
gst_smart_encoder_push_pending_gop(GstSmartEncoder * self)386 gst_smart_encoder_push_pending_gop (GstSmartEncoder * self)
387 {
388   guint64 cstart, cstop;
389   GList *tmp;
390   gboolean force_reencoding = FALSE;
391   GstFlowReturn res = GST_FLOW_OK;
392 
393   GST_DEBUG ("Pushing pending GOP (%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
394       ")", GST_TIME_ARGS (self->gop_start), GST_TIME_ARGS (self->gop_stop));
395 
396   if (!self->pending_gop) {
397     /* This might happen on EOS */
398     GST_INFO_OBJECT (self, "Empty gop!");
399     goto done;
400   }
401 
402   if (!gst_segment_clip (&self->input_segment, GST_FORMAT_TIME, self->gop_start,
403           self->gop_stop, &cstart, &cstop)) {
404     /* The whole GOP is outside the segment, there's most likely
405      * a bug somewhere. */
406     GST_DEBUG_OBJECT (self,
407         "GOP is entirely outside of the segment, upstream gave us too much data: (%"
408         GST_TIME_FORMAT " -- %" GST_TIME_FORMAT ")",
409         GST_TIME_ARGS (self->gop_start), GST_TIME_ARGS (self->gop_stop));
410     for (tmp = self->pending_gop; tmp; tmp = tmp->next)
411       gst_buffer_unref ((GstBuffer *) tmp->data);
412 
413     goto done;
414   }
415 
416   force_reencoding = gst_smart_encoder_force_reencoding_for_caps (self);
417   if ((cstart != self->gop_start)
418       || (cstop != self->gop_stop)
419       || force_reencoding) {
420     GST_INFO_OBJECT (self,
421         "GOP needs to be re-encoded from %" GST_TIME_FORMAT " to %"
422         GST_TIME_FORMAT " - %" GST_SEGMENT_FORMAT, GST_TIME_ARGS (cstart),
423         GST_TIME_ARGS (cstop), &self->input_segment);
424     res = gst_smart_encoder_reencode_gop (self);
425 
426     /* Make sure we push the original caps when resuming the original stream */
427     if (!force_reencoding)
428       self->push_original_caps = TRUE;
429   } else {
430     if (self->push_original_caps) {
431       gst_pad_push_event (self->srcpad,
432           gst_event_new_caps (self->original_caps));
433       self->push_original_caps = FALSE;
434     }
435 
436     if (self->output_segment.format == GST_FORMAT_UNDEFINED) {
437       gst_segment_init (&self->output_segment, GST_FORMAT_TIME);
438 
439       /* Ensure that we can represent negative DTS in our 'single' segment */
440       self->output_segment.start = 60 * 60 * GST_SECOND * 1000;
441       if (!gst_pad_push_event (self->srcpad,
442               gst_event_new_segment (&self->output_segment))) {
443         GST_ERROR_OBJECT (self, "Could not push segment!");
444 
445         GST_ELEMENT_FLOW_ERROR (self, GST_FLOW_ERROR);
446 
447         return GST_FLOW_ERROR;
448       }
449     }
450 
451     /* The whole GOP is within the segment, push all pending buffers downstream */
452     GST_INFO_OBJECT (self,
453         "GOP doesn't need to be modified, pushing downstream: %" GST_TIME_FORMAT
454         " to %" GST_TIME_FORMAT, GST_TIME_ARGS (cstart), GST_TIME_ARGS (cstop));
455 
456     self->internal_segment = self->input_segment;
457     for (tmp = self->pending_gop; tmp; tmp = tmp->next) {
458       GstBuffer *buf = (GstBuffer *) tmp->data;
459 
460       res = gst_smart_encoder_finish_buffer (self, buf);
461       if (G_UNLIKELY (res != GST_FLOW_OK))
462         break;
463     }
464   }
465 
466 done:
467   g_list_free (self->pending_gop);
468   self->pending_gop = NULL;
469   self->gop_start = GST_CLOCK_TIME_NONE;
470   self->gop_stop = 0;
471 
472   return res;
473 }
474 
475 static GstFlowReturn
gst_smart_encoder_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)476 gst_smart_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
477 {
478   GstSmartEncoder *self;
479   GstFlowReturn res = GST_FLOW_OK;
480   gboolean discont, keyframe;
481   GstClockTime end_time;
482 
483   self = GST_SMART_ENCODER (parent->parent);
484 
485   discont = GST_BUFFER_IS_DISCONT (buf);
486   keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
487   end_time = GST_BUFFER_PTS (buf);
488   if (GST_CLOCK_TIME_IS_VALID (end_time))
489     end_time += (GST_BUFFER_DURATION_IS_VALID (buf) ? buf->duration : 0);
490 
491   GST_DEBUG_OBJECT (pad,
492       "New buffer %s %s %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
493       discont ? "discont" : "", keyframe ? "keyframe" : "",
494       GST_TIME_ARGS (GST_BUFFER_PTS (buf)), GST_TIME_ARGS (end_time));
495 
496   if (keyframe) {
497     /* If there's a pending GOP, flush it out */
498     if (self->pending_gop) {
499       /* Mark stop of previous gop */
500       if (GST_BUFFER_PTS_IS_VALID (buf)) {
501         if (self->gop_stop > buf->pts)
502           GST_WARNING_OBJECT (self, "Next gop start < current gop" " end");
503         self->gop_stop = buf->pts;
504       }
505 
506       /* flush pending */
507       res = gst_smart_encoder_push_pending_gop (self);
508       if (G_UNLIKELY (res != GST_FLOW_OK))
509         goto beach;
510     }
511 
512     /* Mark gop_start for new gop */
513     self->gop_start = GST_BUFFER_TIMESTAMP (buf);
514   }
515 
516   /* Store buffer */
517   self->pending_gop = g_list_append (self->pending_gop, buf);
518 
519   /* Update GOP stop position */
520   if (GST_CLOCK_TIME_IS_VALID (end_time))
521     self->gop_stop = MAX (self->gop_stop, end_time);
522 
523   GST_DEBUG_OBJECT (self, "Buffer stored , Current GOP : %"
524       GST_TIME_FORMAT " -- %" GST_TIME_FORMAT,
525       GST_TIME_ARGS (self->gop_start), GST_TIME_ARGS (self->gop_stop));
526 
527 beach:
528   return res;
529 }
530 
531 static GstCaps *
smart_encoder_get_caps(GstSmartEncoder * self,GstCaps * original_caps)532 smart_encoder_get_caps (GstSmartEncoder * self, GstCaps * original_caps)
533 {
534   gint i;
535   GstCaps *caps, *outcaps;
536   GstStructure *original_struct = gst_caps_get_structure (original_caps, 0);
537   GstStructure *out_struct, *_struct;
538   GstVideoInfo info;
539   static const gchar *default_fields[] = {
540     "pixel-aspect-ratio",
541     "framerate",
542     "interlace-mode",
543     "colorimetry",
544     "chroma-site",
545     "multiview-mode",
546     "multiview-flags",
547   };
548 
549   if (!gst_structure_has_name (original_struct, "video/x-vp8")) {
550 
551     return gst_caps_ref (original_caps);
552   }
553 
554   /* VP8 is always decoded into YUV colorspaces and we support VP9 profiles
555    * where only YUV is supported (0 and 2) so we ensure that all the
556    * default fields for video/x-raw are set on the caps if none provided by
557    * upstream. This allows us to allow renegotiating new caps downstream when
558    * switching from no reencoding to reencoding making sure all the fields are
559    * defined all the time
560    */
561   caps = gst_caps_copy (original_caps);
562   _struct = gst_caps_get_structure (caps, 0);
563   gst_structure_set_name (_struct, "video/x-raw");
564   gst_structure_set (_struct,
565       "format", G_TYPE_STRING, "I420",
566       "multiview-mode", G_TYPE_STRING, "mono",
567       "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
568       GST_VIDEO_MULTIVIEW_FLAGS_NONE, GST_FLAG_SET_MASK_EXACT, NULL);
569 
570   gst_video_info_from_caps (&info, caps);
571   gst_caps_unref (caps);
572   caps = gst_video_info_to_caps (&info);
573   _struct = gst_caps_get_structure (caps, 0);
574 
575   outcaps = gst_caps_copy (original_caps);
576   out_struct = gst_caps_get_structure (outcaps, 0);
577   for (i = 0; i < G_N_ELEMENTS (default_fields); i++) {
578     const gchar *field = default_fields[i];
579 
580     if (!gst_structure_has_field (original_struct, field)) {
581       const GValue *v = gst_structure_get_value (_struct, field);
582       g_assert (v);
583       gst_structure_set_value (out_struct, field, v);
584     }
585   }
586   gst_caps_unref (caps);
587 
588   return outcaps;
589 }
590 
591 static gboolean
smart_encoder_sink_event(GstPad * pad,GstObject * ghostpad,GstEvent * event)592 smart_encoder_sink_event (GstPad * pad, GstObject * ghostpad, GstEvent * event)
593 {
594   gboolean res = TRUE;
595   GstSmartEncoder *self = GST_SMART_ENCODER (ghostpad->parent);
596 
597   switch (GST_EVENT_TYPE (event)) {
598     case GST_EVENT_FLUSH_STOP:
599       smart_encoder_reset (self);
600       break;
601     case GST_EVENT_CAPS:
602     {
603       GstCaps *caps;
604 
605       gst_event_parse_caps (event, &caps);
606       if (self->original_caps)
607         gst_caps_unref (self->original_caps);
608 
609 
610       self->original_caps = smart_encoder_get_caps (self, caps);
611       self->push_original_caps = TRUE;
612       gst_clear_event (&event);
613       break;
614     }
615     case GST_EVENT_STREAM_START:
616       gst_event_replace (&self->stream_start_event, gst_event_ref (event));
617       break;
618     case GST_EVENT_SEGMENT:
619     {
620       GST_INFO_OBJECT (self, "Pushing pending GOP on new segment");
621       gst_smart_encoder_push_pending_gop (self);
622 
623       gst_event_copy_segment (event, &self->input_segment);
624 
625       GST_DEBUG_OBJECT (self, "input_segment: %" GST_SEGMENT_FORMAT,
626           &self->input_segment);
627       if (self->input_segment.format != GST_FORMAT_TIME) {
628         GST_ERROR_OBJECT (self, "Can't handle streams %s format",
629             gst_format_get_name (self->input_segment.format));
630         gst_event_unref (event);
631 
632         return FALSE;
633       }
634       self->segment_event = event;
635       event = NULL;
636       GST_INFO_OBJECT (self, "Eating segment");
637       break;
638     }
639     case GST_EVENT_EOS:
640       if (self->input_segment.format == GST_FORMAT_TIME)
641         gst_smart_encoder_push_pending_gop (self);
642       break;
643     default:
644       break;
645   }
646 
647   if (event)
648     res = gst_pad_push_event (self->srcpad, event);
649 
650   return res;
651 }
652 
653 static GstCaps *
smart_encoder_sink_getcaps(GstSmartEncoder * self,GstPad * pad,GstCaps * filter)654 smart_encoder_sink_getcaps (GstSmartEncoder * self, GstPad * pad,
655     GstCaps * filter)
656 {
657   GstCaps *peer, *tmpl, *res;
658 
659   tmpl = gst_static_pad_template_get_caps (&src_template);
660 
661   /* Try getting it from downstream */
662   peer = gst_pad_peer_query_caps (self->srcpad, tmpl);
663   if (peer == NULL) {
664     res = tmpl;
665   } else {
666     res = peer;
667     gst_caps_unref (tmpl);
668   }
669 
670   if (filter) {
671     GstCaps *filtered_res = gst_caps_intersect (res, filter);
672 
673     gst_caps_unref (res);
674     if (!filtered_res || gst_caps_is_empty (filtered_res)) {
675       res = NULL;
676     } else {
677       res = filtered_res;
678     }
679   }
680 
681   return res;
682 }
683 
684 static gboolean
_pad_sink_acceptcaps(GstPad * pad,GstSmartEncoder * self,GstCaps * caps)685 _pad_sink_acceptcaps (GstPad * pad, GstSmartEncoder * self, GstCaps * caps)
686 {
687   gboolean ret;
688   GstCaps *modified_caps;
689   GstCaps *accepted_caps;
690   gint i, n;
691   GstStructure *s;
692 
693   GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps);
694 
695   accepted_caps = gst_pad_get_current_caps (GST_PAD (self->srcpad));
696   if (accepted_caps == NULL)
697     accepted_caps = gst_pad_get_pad_template_caps (GST_PAD (self->srcpad));
698   accepted_caps = gst_caps_make_writable (accepted_caps);
699 
700   GST_LOG_OBJECT (pad, "src caps %" GST_PTR_FORMAT, accepted_caps);
701 
702   n = gst_caps_get_size (accepted_caps);
703   for (i = 0; i < n; i++) {
704     s = gst_caps_get_structure (accepted_caps, i);
705 
706     if (gst_structure_has_name (s, "video/x-h264") ||
707         gst_structure_has_name (s, "video/x-h265")) {
708       gst_structure_remove_fields (s, "codec_data", "tier", "profile", "level",
709           NULL);
710     } else if (gst_structure_has_name (s, "video/x-vp8")
711         || gst_structure_has_name (s, "video/x-vp9")) {
712       gst_structure_remove_field (s, "streamheader");
713     }
714   }
715 
716   modified_caps = gst_caps_copy (caps);
717   n = gst_caps_get_size (modified_caps);
718   for (i = 0; i < n; i++) {
719     s = gst_caps_get_structure (modified_caps, i);
720 
721     if (gst_structure_has_name (s, "video/x-h264") ||
722         gst_structure_has_name (s, "video/x-h265")) {
723       gst_structure_remove_fields (s, "codec_data", "tier", "profile", "level",
724           NULL);
725     } else if (gst_structure_has_name (s, "video/x-vp8")
726         || gst_structure_has_name (s, "video/x-vp9")) {
727       gst_structure_remove_field (s, "streamheader");
728     }
729   }
730 
731   ret = gst_caps_can_intersect (modified_caps, accepted_caps);
732   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
733       (ret ? "" : "Doesn't "), caps);
734   return ret;
735 }
736 
737 static gboolean
smart_encoder_sink_query(GstPad * pad,GstObject * ghostpad,GstQuery * query)738 smart_encoder_sink_query (GstPad * pad, GstObject * ghostpad, GstQuery * query)
739 {
740   gboolean res;
741   GstSmartEncoder *self = GST_SMART_ENCODER (ghostpad->parent);
742 
743   switch (GST_QUERY_TYPE (query)) {
744     case GST_QUERY_CAPS:
745     {
746       GstCaps *filter, *caps;
747 
748       gst_query_parse_caps (query, &filter);
749       caps = smart_encoder_sink_getcaps (self, pad, filter);
750       GST_DEBUG_OBJECT (self, "Got caps: %" GST_PTR_FORMAT, caps);
751       gst_query_set_caps_result (query, caps);
752       gst_caps_unref (caps);
753       res = TRUE;
754       break;
755     }
756     case GST_QUERY_ACCEPT_CAPS:
757     {
758       GstCaps *caps;
759 
760       gst_query_parse_accept_caps (query, &caps);
761       res = _pad_sink_acceptcaps (GST_PAD (pad), self, caps);
762       gst_query_set_accept_caps_result (query, res);
763       res = TRUE;
764       break;
765     }
766     default:
767       res = gst_pad_query_default (pad, ghostpad, query);
768       break;
769   }
770   return res;
771 }
772 
773 static gboolean
gst_smart_encoder_add_parser(GstSmartEncoder * self,GstCaps * format)774 gst_smart_encoder_add_parser (GstSmartEncoder * self, GstCaps * format)
775 {
776   const gchar *stream_format;
777   GstPad *chainpad, *internal_chainpad, *sinkpad = NULL;
778   GstStructure *structure = gst_caps_get_structure (format, 0);
779   GstElement *capsfilter = gst_element_factory_make ("capsfilter", NULL);
780   GstElement *parser = NULL;
781 
782   gst_bin_add (GST_BIN (self), capsfilter);
783   g_object_set (capsfilter, "caps", format, NULL);
784   if (gst_structure_has_name (structure, "video/x-h264")) {
785     parser = gst_element_factory_make ("h264parse", NULL);
786     if (!parser) {
787       GST_ERROR_OBJECT (self, "`h264parse` is missing, can't encode smartly");
788 
789       goto failed;
790     }
791 
792     stream_format = gst_structure_get_string (structure, "stream-format");
793     if (g_strcmp0 (stream_format, "avc1"))
794       g_object_set (parser, "config-interval", -1, NULL);
795 
796   } else if (gst_structure_has_name (gst_caps_get_structure (format, 0),
797           "video/x-h265")) {
798     parser = gst_element_factory_make ("h265parse", NULL);
799     if (!parser) {
800       GST_ERROR_OBJECT (self, "`h265parse` is missing, can't encode smartly");
801 
802       goto failed;
803     }
804 
805     stream_format = gst_structure_get_string (structure, "stream-format");
806     if (g_strcmp0 (stream_format, "hvc1"))
807       g_object_set (parser, "config-interval", -1, NULL);
808   } else if (gst_structure_has_name (structure, "video/x-vp9")) {
809     parser = gst_element_factory_make ("vp9parse", NULL);
810     if (!parser) {
811       GST_ERROR_OBJECT (self, "`vp9parse` is missing, can't encode smartly");
812 
813       goto failed;
814     }
815   } else {
816     sinkpad = gst_element_get_static_pad (capsfilter, "sink");
817   }
818 
819   if (parser) {
820     if (!gst_bin_add (GST_BIN (self), parser)) {
821       GST_ERROR_OBJECT (self, "Could not add parser.");
822 
823       goto failed;
824     }
825 
826     if (!gst_element_link (parser, capsfilter)) {
827       GST_ERROR_OBJECT (self, "Could not link capfilter and parser.");
828 
829       goto failed;
830     }
831 
832     sinkpad = gst_element_get_static_pad (parser, "sink");
833   }
834 
835   g_assert (sinkpad);
836 
837   /* The chainpad is the pad that is linked to the srcpad of the chain
838    * of element that is linked to our public sinkpad, this is the pad where
839    * we chain the buffers either directly to our srcpad or through the
840    * reencoding sub chain. */
841   chainpad =
842       GST_PAD (gst_ghost_pad_new ("chainpad", capsfilter->srcpads->data));
843   gst_element_add_pad (GST_ELEMENT (self), chainpad);
844   internal_chainpad =
845       GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (chainpad)));
846   gst_pad_set_chain_function (internal_chainpad, gst_smart_encoder_chain);
847   gst_pad_set_event_function (internal_chainpad, smart_encoder_sink_event);
848   gst_pad_set_query_function (internal_chainpad, smart_encoder_sink_query);
849 
850   gst_ghost_pad_set_target (GST_GHOST_PAD (self->sinkpad), sinkpad);
851   gst_object_unref (sinkpad);
852 
853   return TRUE;
854 
855 failed:
856   gst_clear_object (&parser);
857 
858   return FALSE;
859 }
860 
861 gboolean
gst_smart_encoder_set_encoder(GstSmartEncoder * self,GstCaps * format,GstElement * encoder)862 gst_smart_encoder_set_encoder (GstSmartEncoder * self, GstCaps * format,
863     GstElement * encoder)
864 {
865   self->encoder = g_object_ref_sink (encoder);
866   gst_element_set_locked_state (self->encoder, TRUE);
867 
868   return gst_smart_encoder_add_parser (self, format);
869 }
870 
871 /******************************************
872  *    GstElement vmethod implementations  *
873  ******************************************/
874 
875 static GstStateChangeReturn
gst_smart_encoder_change_state(GstElement * element,GstStateChange transition)876 gst_smart_encoder_change_state (GstElement * element, GstStateChange transition)
877 {
878   GstSmartEncoder *self;
879   GstStateChangeReturn ret;
880 
881   g_return_val_if_fail (GST_IS_SMART_ENCODER (element),
882       GST_STATE_CHANGE_FAILURE);
883 
884   self = GST_SMART_ENCODER (element);
885 
886   ret =
887       GST_ELEMENT_CLASS (gst_smart_encoder_parent_class)->change_state (element,
888       transition);
889 
890   switch (transition) {
891     case GST_STATE_CHANGE_PAUSED_TO_READY:
892       smart_encoder_reset (self);
893       break;
894     default:
895       break;
896   }
897 
898   return ret;
899 }
900 
901 /******************************************
902  *          GObject vmethods              *
903  ******************************************/
904 static void
gst_smart_encoder_finalize(GObject * object)905 gst_smart_encoder_finalize (GObject * object)
906 {
907   GstSmartEncoder *self = (GstSmartEncoder *) object;
908   g_mutex_clear (&self->internal_flow_lock);
909   g_cond_clear (&self->internal_flow_cond);
910 
911   G_OBJECT_CLASS (gst_smart_encoder_parent_class)->finalize (object);
912 }
913 
914 static void
gst_smart_encoder_dispose(GObject * object)915 gst_smart_encoder_dispose (GObject * object)
916 {
917   GstSmartEncoder *self = (GstSmartEncoder *) object;
918 
919   gst_clear_object (&self->encoder);
920 
921   if (self->original_caps) {
922     gst_caps_unref (self->original_caps);
923     self->original_caps = NULL;
924   }
925 
926   G_OBJECT_CLASS (gst_smart_encoder_parent_class)->dispose (object);
927 }
928 
929 
930 static void
gst_smart_encoder_class_init(GstSmartEncoderClass * klass)931 gst_smart_encoder_class_init (GstSmartEncoderClass * klass)
932 {
933   GObjectClass *gobject_class;
934   GstElementClass *element_class;
935 
936   element_class = (GstElementClass *) klass;
937   gobject_class = G_OBJECT_CLASS (klass);
938 
939   gst_smart_encoder_parent_class = g_type_class_peek_parent (klass);
940 
941   gst_element_class_add_static_pad_template (element_class, &src_template);
942   gst_element_class_add_static_pad_template (element_class, &sink_template);
943 
944   gst_element_class_set_static_metadata (element_class, "Smart Video Encoder",
945       "Codec/Recoder/Video",
946       "Re-encodes portions of Video that lay on segment boundaries",
947       "Edward Hervey <bilboed@gmail.com>");
948 
949   gobject_class->dispose = (GObjectFinalizeFunc) gst_smart_encoder_dispose;
950   gobject_class->finalize = (GObjectFinalizeFunc) gst_smart_encoder_finalize;
951   element_class->change_state = gst_smart_encoder_change_state;
952 
953   GST_DEBUG_CATEGORY_INIT (smart_encoder_debug, "smartencoder", 0,
954       "Smart Encoder");
955 }
956 
957 static void
gst_smart_encoder_init(GstSmartEncoder * self)958 gst_smart_encoder_init (GstSmartEncoder * self)
959 {
960   GstPadTemplate *template = gst_static_pad_template_get (&sink_template);
961 
962   self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", template);
963   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
964   gst_object_unref (template);
965 
966   self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
967   gst_pad_use_fixed_caps (self->srcpad);
968   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
969 
970   g_mutex_init (&self->internal_flow_lock);
971   g_cond_init (&self->internal_flow_cond);
972   smart_encoder_reset (self);
973 }
974