1 /*
2  * Copyright (C) 2010, 2013 Ole André Vadla Ravnås <oleavr@soundrop.com>
3  * Copyright (C) 2013 Intel Corporation
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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "vtenc.h"
25 
26 #include "coremediabuffer.h"
27 #include "corevideobuffer.h"
28 #include "vtutil.h"
29 #include <gst/pbutils/codec-utils.h>
30 
31 #define VTENC_DEFAULT_USAGE       6     /* Profile: Baseline  Level: 2.1 */
32 #define VTENC_DEFAULT_BITRATE     0
33 #define VTENC_DEFAULT_FRAME_REORDERING TRUE
34 #define VTENC_DEFAULT_REALTIME FALSE
35 #define VTENC_DEFAULT_QUALITY 0.5
36 #define VTENC_DEFAULT_MAX_KEYFRAME_INTERVAL 0
37 #define VTENC_DEFAULT_MAX_KEYFRAME_INTERVAL_DURATION 0
38 
39 GST_DEBUG_CATEGORY (gst_vtenc_debug);
40 #define GST_CAT_DEFAULT (gst_vtenc_debug)
41 
42 #define GST_VTENC_CODEC_DETAILS_QDATA \
43     g_quark_from_static_string ("vtenc-codec-details")
44 
45 /* define EnableHardwareAcceleratedVideoEncoder in < 10.9 */
46 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 1090
47 const CFStringRef
48     kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder =
49 CFSTR ("EnableHardwareAcceleratedVideoEncoder");
50 const CFStringRef
51     kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder =
52 CFSTR ("RequireHardwareAcceleratedVideoEncoder");
53 const CFStringRef kVTCompressionPropertyKey_ProfileLevel =
54 CFSTR ("ProfileLevel");
55 const CFStringRef kVTProfileLevel_H264_Baseline_AutoLevel =
56 CFSTR ("H264_Baseline_AutoLevel");
57 #endif
58 
59 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 1080
60 const CFStringRef kVTCompressionPropertyKey_Quality = CFSTR ("Quality");
61 #endif
62 
63 #ifdef HAVE_VIDEOTOOLBOX_10_9_6
64 extern OSStatus
65 VTCompressionSessionPrepareToEncodeFrames (VTCompressionSessionRef session)
66     __attribute__ ((weak_import));
67 #endif
68 
69 enum
70 {
71   PROP_0,
72   PROP_USAGE,
73   PROP_BITRATE,
74   PROP_ALLOW_FRAME_REORDERING,
75   PROP_REALTIME,
76   PROP_QUALITY,
77   PROP_MAX_KEYFRAME_INTERVAL,
78   PROP_MAX_KEYFRAME_INTERVAL_DURATION
79 };
80 
81 typedef struct _GstVTEncFrame GstVTEncFrame;
82 
83 struct _GstVTEncFrame
84 {
85   GstBuffer *buf;
86   GstVideoFrame videoframe;
87 };
88 
89 static GstElementClass *parent_class = NULL;
90 
91 static void gst_vtenc_get_property (GObject * obj, guint prop_id,
92     GValue * value, GParamSpec * pspec);
93 static void gst_vtenc_set_property (GObject * obj, guint prop_id,
94     const GValue * value, GParamSpec * pspec);
95 static void gst_vtenc_finalize (GObject * obj);
96 
97 static gboolean gst_vtenc_start (GstVideoEncoder * enc);
98 static gboolean gst_vtenc_stop (GstVideoEncoder * enc);
99 static gboolean gst_vtenc_set_format (GstVideoEncoder * enc,
100     GstVideoCodecState * input_state);
101 static GstFlowReturn gst_vtenc_handle_frame (GstVideoEncoder * enc,
102     GstVideoCodecFrame * frame);
103 static GstFlowReturn gst_vtenc_finish (GstVideoEncoder * enc);
104 static gboolean gst_vtenc_flush (GstVideoEncoder * enc);
105 
106 static void gst_vtenc_clear_cached_caps_downstream (GstVTEnc * self);
107 
108 static VTCompressionSessionRef gst_vtenc_create_session (GstVTEnc * self);
109 static void gst_vtenc_destroy_session (GstVTEnc * self,
110     VTCompressionSessionRef * session);
111 static void gst_vtenc_session_dump_properties (GstVTEnc * self,
112     VTCompressionSessionRef session);
113 static void gst_vtenc_session_configure_expected_framerate (GstVTEnc * self,
114     VTCompressionSessionRef session, gdouble framerate);
115 static void gst_vtenc_session_configure_max_keyframe_interval (GstVTEnc * self,
116     VTCompressionSessionRef session, gint interval);
117 static void gst_vtenc_session_configure_max_keyframe_interval_duration
118     (GstVTEnc * self, VTCompressionSessionRef session, gdouble duration);
119 static void gst_vtenc_session_configure_bitrate (GstVTEnc * self,
120     VTCompressionSessionRef session, guint bitrate);
121 static OSStatus gst_vtenc_session_configure_property_int (GstVTEnc * self,
122     VTCompressionSessionRef session, CFStringRef name, gint value);
123 static OSStatus gst_vtenc_session_configure_property_double (GstVTEnc * self,
124     VTCompressionSessionRef session, CFStringRef name, gdouble value);
125 static void gst_vtenc_session_configure_allow_frame_reordering (GstVTEnc * self,
126     VTCompressionSessionRef session, gboolean allow_frame_reordering);
127 static void gst_vtenc_session_configure_realtime (GstVTEnc * self,
128     VTCompressionSessionRef session, gboolean realtime);
129 
130 static GstFlowReturn gst_vtenc_encode_frame (GstVTEnc * self,
131     GstVideoCodecFrame * frame);
132 static void gst_vtenc_enqueue_buffer (void *outputCallbackRefCon,
133     void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags,
134     CMSampleBufferRef sampleBuffer);
135 static gboolean gst_vtenc_buffer_is_keyframe (GstVTEnc * self,
136     CMSampleBufferRef sbuf);
137 
138 
139 #ifndef HAVE_IOS
140 static GstVTEncFrame *gst_vtenc_frame_new (GstBuffer * buf,
141     GstVideoInfo * videoinfo);
142 static void gst_vtenc_frame_free (GstVTEncFrame * frame);
143 
144 static void gst_pixel_buffer_release_cb (void *releaseRefCon,
145     const void *dataPtr, size_t dataSize, size_t numberOfPlanes,
146     const void *planeAddresses[]);
147 #endif
148 
149 #ifdef HAVE_IOS
150 static GstStaticCaps sink_caps =
151 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, I420 }"));
152 #else
153 static GstStaticCaps sink_caps =
154 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ UYVY, NV12, I420 }"));
155 #endif
156 
157 static void
gst_vtenc_base_init(GstVTEncClass * klass)158 gst_vtenc_base_init (GstVTEncClass * klass)
159 {
160   const GstVTEncoderDetails *codec_details =
161       GST_VTENC_CLASS_GET_CODEC_DETAILS (klass);
162   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
163   const int min_width = 1, max_width = G_MAXINT;
164   const int min_height = 1, max_height = G_MAXINT;
165   const int min_fps_n = 0, max_fps_n = G_MAXINT;
166   const int min_fps_d = 1, max_fps_d = 1;
167   GstPadTemplate *sink_template, *src_template;
168   GstCaps *src_caps;
169   gchar *longname, *description;
170 
171   longname = g_strdup_printf ("%s encoder", codec_details->name);
172   description = g_strdup_printf ("%s encoder", codec_details->name);
173 
174   gst_element_class_set_metadata (element_class, longname,
175       "Codec/Encoder/Video/Hardware", description,
176       "Ole André Vadla Ravnås <oleavr@soundrop.com>, Dominik Röttsches <dominik.rottsches@intel.com>");
177 
178   g_free (longname);
179   g_free (description);
180 
181   sink_template = gst_pad_template_new ("sink",
182       GST_PAD_SINK, GST_PAD_ALWAYS, gst_static_caps_get (&sink_caps));
183   gst_element_class_add_pad_template (element_class, sink_template);
184 
185   src_caps = gst_caps_new_simple (codec_details->mimetype,
186       "width", GST_TYPE_INT_RANGE, min_width, max_width,
187       "height", GST_TYPE_INT_RANGE, min_height, max_height,
188       "framerate", GST_TYPE_FRACTION_RANGE,
189       min_fps_n, min_fps_d, max_fps_n, max_fps_d, NULL);
190   if (codec_details->format_id == kCMVideoCodecType_H264) {
191     gst_structure_set (gst_caps_get_structure (src_caps, 0),
192         "stream-format", G_TYPE_STRING, "avc",
193         "alignment", G_TYPE_STRING, "au", NULL);
194   }
195   src_template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
196       src_caps);
197   gst_element_class_add_pad_template (element_class, src_template);
198   gst_caps_unref (src_caps);
199 }
200 
201 static void
gst_vtenc_class_init(GstVTEncClass * klass)202 gst_vtenc_class_init (GstVTEncClass * klass)
203 {
204   GObjectClass *gobject_class;
205   GstVideoEncoderClass *gstvideoencoder_class;
206 
207   gobject_class = (GObjectClass *) klass;
208   gstvideoencoder_class = (GstVideoEncoderClass *) klass;
209 
210   parent_class = g_type_class_peek_parent (klass);
211 
212   gobject_class->get_property = gst_vtenc_get_property;
213   gobject_class->set_property = gst_vtenc_set_property;
214   gobject_class->finalize = gst_vtenc_finalize;
215 
216   gstvideoencoder_class->start = gst_vtenc_start;
217   gstvideoencoder_class->stop = gst_vtenc_stop;
218   gstvideoencoder_class->set_format = gst_vtenc_set_format;
219   gstvideoencoder_class->handle_frame = gst_vtenc_handle_frame;
220   gstvideoencoder_class->finish = gst_vtenc_finish;
221   gstvideoencoder_class->flush = gst_vtenc_flush;
222 
223   g_object_class_install_property (gobject_class, PROP_BITRATE,
224       g_param_spec_uint ("bitrate", "Bitrate",
225           "Target video bitrate in kbps (0 = auto)",
226           0, G_MAXUINT, VTENC_DEFAULT_BITRATE,
227           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
228 
229   g_object_class_install_property (gobject_class, PROP_ALLOW_FRAME_REORDERING,
230       g_param_spec_boolean ("allow-frame-reordering", "Allow frame reordering",
231           "Whether to allow frame reordering or not",
232           VTENC_DEFAULT_FRAME_REORDERING,
233           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
234 
235   g_object_class_install_property (gobject_class, PROP_REALTIME,
236       g_param_spec_boolean ("realtime", "Realtime",
237           "Configure the encoder for realtime output",
238           VTENC_DEFAULT_REALTIME,
239           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
240 
241   g_object_class_install_property (gobject_class, PROP_QUALITY,
242       g_param_spec_double ("quality", "Quality",
243           "The desired compression quality",
244           0.0, 1.0, VTENC_DEFAULT_QUALITY,
245           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
246 
247   g_object_class_install_property (gobject_class, PROP_MAX_KEYFRAME_INTERVAL,
248       g_param_spec_int ("max-keyframe-interval", "Max Keyframe Interval",
249           "Maximum number of frames between keyframes (0 = auto)",
250           0, G_MAXINT, VTENC_DEFAULT_MAX_KEYFRAME_INTERVAL,
251           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
252 
253   g_object_class_install_property (gobject_class,
254       PROP_MAX_KEYFRAME_INTERVAL_DURATION,
255       g_param_spec_uint64 ("max-keyframe-interval-duration",
256           "Max Keyframe Interval Duration",
257           "Maximum number of nanoseconds between keyframes (0 = no limit)", 0,
258           G_MAXUINT64, VTENC_DEFAULT_MAX_KEYFRAME_INTERVAL_DURATION,
259           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
260 }
261 
262 static void
gst_vtenc_init(GstVTEnc * self)263 gst_vtenc_init (GstVTEnc * self)
264 {
265   GstVTEncClass *klass = (GstVTEncClass *) G_OBJECT_GET_CLASS (self);
266   CFStringRef keyframe_props_keys[] = { kVTEncodeFrameOptionKey_ForceKeyFrame };
267   CFBooleanRef keyframe_props_values[] = { kCFBooleanTrue };
268 
269   self->details = GST_VTENC_CLASS_GET_CODEC_DETAILS (klass);
270 
271   /* These could be controlled by properties later */
272   self->dump_properties = FALSE;
273   self->dump_attributes = FALSE;
274   self->latency_frames = -1;
275   self->session = NULL;
276   self->profile_level = NULL;
277 
278   self->keyframe_props =
279       CFDictionaryCreate (NULL, (const void **) keyframe_props_keys,
280       (const void **) keyframe_props_values, G_N_ELEMENTS (keyframe_props_keys),
281       &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
282 }
283 
284 static void
gst_vtenc_finalize(GObject * obj)285 gst_vtenc_finalize (GObject * obj)
286 {
287   GstVTEnc *self = GST_VTENC_CAST (obj);
288 
289   CFRelease (self->keyframe_props);
290 
291   G_OBJECT_CLASS (parent_class)->finalize (obj);
292 }
293 
294 static guint
gst_vtenc_get_bitrate(GstVTEnc * self)295 gst_vtenc_get_bitrate (GstVTEnc * self)
296 {
297   guint result;
298 
299   GST_OBJECT_LOCK (self);
300   result = self->bitrate;
301   GST_OBJECT_UNLOCK (self);
302 
303   return result;
304 }
305 
306 static void
gst_vtenc_set_bitrate(GstVTEnc * self,guint bitrate)307 gst_vtenc_set_bitrate (GstVTEnc * self, guint bitrate)
308 {
309   GST_OBJECT_LOCK (self);
310 
311   self->bitrate = bitrate;
312 
313   if (self->session != NULL)
314     gst_vtenc_session_configure_bitrate (self, self->session, bitrate);
315 
316   GST_OBJECT_UNLOCK (self);
317 }
318 
319 static gboolean
gst_vtenc_get_allow_frame_reordering(GstVTEnc * self)320 gst_vtenc_get_allow_frame_reordering (GstVTEnc * self)
321 {
322   gboolean result;
323 
324   GST_OBJECT_LOCK (self);
325   result = self->allow_frame_reordering;
326   GST_OBJECT_UNLOCK (self);
327 
328   return result;
329 }
330 
331 static void
gst_vtenc_set_allow_frame_reordering(GstVTEnc * self,gboolean allow_frame_reordering)332 gst_vtenc_set_allow_frame_reordering (GstVTEnc * self,
333     gboolean allow_frame_reordering)
334 {
335   GST_OBJECT_LOCK (self);
336   self->allow_frame_reordering = allow_frame_reordering;
337   if (self->session != NULL) {
338     gst_vtenc_session_configure_allow_frame_reordering (self,
339         self->session, allow_frame_reordering);
340   }
341   GST_OBJECT_UNLOCK (self);
342 }
343 
344 static gboolean
gst_vtenc_get_realtime(GstVTEnc * self)345 gst_vtenc_get_realtime (GstVTEnc * self)
346 {
347   gboolean result;
348 
349   GST_OBJECT_LOCK (self);
350   result = self->realtime;
351   GST_OBJECT_UNLOCK (self);
352 
353   return result;
354 }
355 
356 static void
gst_vtenc_set_realtime(GstVTEnc * self,gboolean realtime)357 gst_vtenc_set_realtime (GstVTEnc * self, gboolean realtime)
358 {
359   GST_OBJECT_LOCK (self);
360   self->realtime = realtime;
361   if (self->session != NULL)
362     gst_vtenc_session_configure_realtime (self, self->session, realtime);
363   GST_OBJECT_UNLOCK (self);
364 }
365 
366 static gdouble
gst_vtenc_get_quality(GstVTEnc * self)367 gst_vtenc_get_quality (GstVTEnc * self)
368 {
369   gdouble result;
370 
371   GST_OBJECT_LOCK (self);
372   result = self->quality;
373   GST_OBJECT_UNLOCK (self);
374 
375   return result;
376 }
377 
378 static void
gst_vtenc_set_quality(GstVTEnc * self,gdouble quality)379 gst_vtenc_set_quality (GstVTEnc * self, gdouble quality)
380 {
381   GST_OBJECT_LOCK (self);
382   self->quality = quality;
383   GST_INFO_OBJECT (self, "setting quality %f", quality);
384   if (self->session != NULL) {
385     gst_vtenc_session_configure_property_double (self, self->session,
386         kVTCompressionPropertyKey_Quality, quality);
387   }
388   GST_OBJECT_UNLOCK (self);
389 }
390 
391 static gint
gst_vtenc_get_max_keyframe_interval(GstVTEnc * self)392 gst_vtenc_get_max_keyframe_interval (GstVTEnc * self)
393 {
394   gint result;
395 
396   GST_OBJECT_LOCK (self);
397   result = self->max_keyframe_interval;
398   GST_OBJECT_UNLOCK (self);
399 
400   return result;
401 }
402 
403 static void
gst_vtenc_set_max_keyframe_interval(GstVTEnc * self,gint interval)404 gst_vtenc_set_max_keyframe_interval (GstVTEnc * self, gint interval)
405 {
406   GST_OBJECT_LOCK (self);
407   self->max_keyframe_interval = interval;
408   if (self->session != NULL) {
409     gst_vtenc_session_configure_max_keyframe_interval (self, self->session,
410         interval);
411   }
412   GST_OBJECT_UNLOCK (self);
413 }
414 
415 static GstClockTime
gst_vtenc_get_max_keyframe_interval_duration(GstVTEnc * self)416 gst_vtenc_get_max_keyframe_interval_duration (GstVTEnc * self)
417 {
418   GstClockTime result;
419 
420   GST_OBJECT_LOCK (self);
421   result = self->max_keyframe_interval_duration;
422   GST_OBJECT_UNLOCK (self);
423 
424   return result;
425 }
426 
427 static void
gst_vtenc_set_max_keyframe_interval_duration(GstVTEnc * self,GstClockTime interval)428 gst_vtenc_set_max_keyframe_interval_duration (GstVTEnc * self,
429     GstClockTime interval)
430 {
431   GST_OBJECT_LOCK (self);
432   self->max_keyframe_interval_duration = interval;
433   if (self->session != NULL) {
434     gst_vtenc_session_configure_max_keyframe_interval_duration (self,
435         self->session, interval / ((gdouble) GST_SECOND));
436   }
437   GST_OBJECT_UNLOCK (self);
438 }
439 
440 static void
gst_vtenc_get_property(GObject * obj,guint prop_id,GValue * value,GParamSpec * pspec)441 gst_vtenc_get_property (GObject * obj, guint prop_id, GValue * value,
442     GParamSpec * pspec)
443 {
444   GstVTEnc *self = GST_VTENC_CAST (obj);
445 
446   switch (prop_id) {
447     case PROP_BITRATE:
448       g_value_set_uint (value, gst_vtenc_get_bitrate (self) / 1000);
449       break;
450     case PROP_ALLOW_FRAME_REORDERING:
451       g_value_set_boolean (value, gst_vtenc_get_allow_frame_reordering (self));
452       break;
453     case PROP_REALTIME:
454       g_value_set_boolean (value, gst_vtenc_get_realtime (self));
455       break;
456     case PROP_QUALITY:
457       g_value_set_double (value, gst_vtenc_get_quality (self));
458       break;
459     case PROP_MAX_KEYFRAME_INTERVAL:
460       g_value_set_int (value, gst_vtenc_get_max_keyframe_interval (self));
461       break;
462     case PROP_MAX_KEYFRAME_INTERVAL_DURATION:
463       g_value_set_uint64 (value,
464           gst_vtenc_get_max_keyframe_interval_duration (self));
465       break;
466     default:
467       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
468       break;
469   }
470 }
471 
472 static void
gst_vtenc_set_property(GObject * obj,guint prop_id,const GValue * value,GParamSpec * pspec)473 gst_vtenc_set_property (GObject * obj, guint prop_id, const GValue * value,
474     GParamSpec * pspec)
475 {
476   GstVTEnc *self = GST_VTENC_CAST (obj);
477 
478   switch (prop_id) {
479     case PROP_BITRATE:
480       gst_vtenc_set_bitrate (self, g_value_get_uint (value) * 1000);
481       break;
482     case PROP_ALLOW_FRAME_REORDERING:
483       gst_vtenc_set_allow_frame_reordering (self, g_value_get_boolean (value));
484       break;
485     case PROP_REALTIME:
486       gst_vtenc_set_realtime (self, g_value_get_boolean (value));
487       break;
488     case PROP_QUALITY:
489       gst_vtenc_set_quality (self, g_value_get_double (value));
490       break;
491     case PROP_MAX_KEYFRAME_INTERVAL:
492       gst_vtenc_set_max_keyframe_interval (self, g_value_get_int (value));
493       break;
494     case PROP_MAX_KEYFRAME_INTERVAL_DURATION:
495       gst_vtenc_set_max_keyframe_interval_duration (self,
496           g_value_get_uint64 (value));
497       break;
498     default:
499       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
500       break;
501   }
502 }
503 
504 static GstFlowReturn
gst_vtenc_finish_encoding(GstVTEnc * self,gboolean is_flushing)505 gst_vtenc_finish_encoding (GstVTEnc * self, gboolean is_flushing)
506 {
507   GST_DEBUG_OBJECT (self,
508       "complete enconding and clean buffer queue, is flushing %d", is_flushing);
509   GstVideoCodecFrame *outframe;
510   GstFlowReturn ret = GST_FLOW_OK;
511   OSStatus vt_status;
512 
513   /* We need to unlock the stream lock here because
514    * it can wait for gst_vtenc_enqueue_buffer() to
515    * handle a buffer... which will take the stream
516    * lock from another thread and then deadlock */
517   GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
518   GST_DEBUG_OBJECT (self, "starting VTCompressionSessionCompleteFrames");
519   vt_status =
520       VTCompressionSessionCompleteFrames (self->session,
521       kCMTimePositiveInfinity);
522   GST_DEBUG_OBJECT (self, "VTCompressionSessionCompleteFrames ended");
523   GST_VIDEO_ENCODER_STREAM_LOCK (self);
524   if (vt_status != noErr) {
525     GST_WARNING_OBJECT (self, "VTCompressionSessionCompleteFrames returned %d",
526         (int) vt_status);
527   }
528 
529   while ((outframe = g_async_queue_try_pop (self->cur_outframes))) {
530     if (is_flushing) {
531       GST_DEBUG_OBJECT (self, "flushing frame number %d",
532           outframe->system_frame_number);
533       gst_video_codec_frame_unref (outframe);
534     } else {
535       GST_DEBUG_OBJECT (self, "finish frame number %d",
536           outframe->system_frame_number);
537       ret =
538           gst_video_encoder_finish_frame (GST_VIDEO_ENCODER_CAST (self),
539           outframe);
540     }
541   }
542 
543   GST_DEBUG_OBJECT (self, "buffer queue cleaned");
544 
545   return ret;
546 }
547 
548 static gboolean
gst_vtenc_start(GstVideoEncoder * enc)549 gst_vtenc_start (GstVideoEncoder * enc)
550 {
551   GstVTEnc *self = GST_VTENC_CAST (enc);
552 
553   self->cur_outframes = g_async_queue_new ();
554 
555   return TRUE;
556 }
557 
558 static gboolean
gst_vtenc_stop(GstVideoEncoder * enc)559 gst_vtenc_stop (GstVideoEncoder * enc)
560 {
561   GstVTEnc *self = GST_VTENC_CAST (enc);
562 
563   GST_VIDEO_ENCODER_STREAM_LOCK (self);
564   gst_vtenc_flush (enc);
565   GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
566 
567   GST_OBJECT_LOCK (self);
568   gst_vtenc_destroy_session (self, &self->session);
569   GST_OBJECT_UNLOCK (self);
570 
571   if (self->profile_level)
572     CFRelease (self->profile_level);
573   self->profile_level = NULL;
574 
575   if (self->input_state)
576     gst_video_codec_state_unref (self->input_state);
577   self->input_state = NULL;
578 
579   self->negotiated_width = self->negotiated_height = 0;
580   self->negotiated_fps_n = self->negotiated_fps_d = 0;
581 
582   gst_vtenc_clear_cached_caps_downstream (self);
583 
584   g_async_queue_unref (self->cur_outframes);
585   self->cur_outframes = NULL;
586 
587   return TRUE;
588 }
589 
590 static CFStringRef
gst_vtenc_profile_level_key(GstVTEnc * self,const gchar * profile,const gchar * level_arg)591 gst_vtenc_profile_level_key (GstVTEnc * self, const gchar * profile,
592     const gchar * level_arg)
593 {
594   char level[64];
595   gchar *key = NULL;
596   CFStringRef ret = NULL;
597 
598   if (profile == NULL)
599     profile = "main";
600   if (level_arg == NULL)
601     level_arg = "AutoLevel";
602   strncpy (level, level_arg, sizeof (level));
603 
604   if (!strcmp (profile, "constrained-baseline") ||
605       !strcmp (profile, "baseline")) {
606     profile = "Baseline";
607   } else if (g_str_has_prefix (profile, "high")) {
608     profile = "High";
609   } else if (!strcmp (profile, "main")) {
610     profile = "Main";
611   } else {
612     g_assert_not_reached ();
613   }
614 
615   if (strlen (level) == 1) {
616     level[1] = '_';
617     level[2] = '0';
618   } else if (strlen (level) == 3) {
619     level[1] = '_';
620   }
621 
622   key = g_strdup_printf ("H264_%s_%s", profile, level);
623   ret = CFStringCreateWithBytes (NULL, (const guint8 *) key, strlen (key),
624       kCFStringEncodingASCII, 0);
625 
626   GST_INFO_OBJECT (self, "negotiated profile and level %s", key);
627 
628   g_free (key);
629 
630   return ret;
631 }
632 
633 static gboolean
gst_vtenc_negotiate_profile_and_level(GstVideoEncoder * enc)634 gst_vtenc_negotiate_profile_and_level (GstVideoEncoder * enc)
635 {
636   GstVTEnc *self = GST_VTENC_CAST (enc);
637   GstCaps *allowed_caps = NULL;
638   gboolean ret = TRUE;
639   const gchar *profile = NULL;
640   const gchar *level = NULL;
641 
642   allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (enc));
643   if (allowed_caps) {
644     GstStructure *s;
645 
646     if (gst_caps_is_empty (allowed_caps)) {
647       GST_ERROR_OBJECT (self, "no allowed downstream caps");
648       goto fail;
649     }
650 
651     allowed_caps = gst_caps_make_writable (allowed_caps);
652     allowed_caps = gst_caps_fixate (allowed_caps);
653     s = gst_caps_get_structure (allowed_caps, 0);
654 
655     profile = gst_structure_get_string (s, "profile");
656     level = gst_structure_get_string (s, "level");
657   }
658 
659   if (self->profile_level)
660     CFRelease (self->profile_level);
661   self->profile_level = gst_vtenc_profile_level_key (self, profile, level);
662   if (self->profile_level == NULL) {
663     GST_ERROR_OBJECT (enc, "invalid profile and level");
664     goto fail;
665   }
666 
667 out:
668   if (allowed_caps)
669     gst_caps_unref (allowed_caps);
670 
671   return ret;
672 
673 fail:
674   ret = FALSE;
675   goto out;
676 }
677 
678 static gboolean
gst_vtenc_set_format(GstVideoEncoder * enc,GstVideoCodecState * state)679 gst_vtenc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
680 {
681   GstVTEnc *self = GST_VTENC_CAST (enc);
682   VTCompressionSessionRef session;
683 
684   if (self->input_state)
685     gst_video_codec_state_unref (self->input_state);
686   self->input_state = gst_video_codec_state_ref (state);
687 
688   self->negotiated_width = state->info.width;
689   self->negotiated_height = state->info.height;
690   self->negotiated_fps_n = state->info.fps_n;
691   self->negotiated_fps_d = state->info.fps_d;
692   self->video_info = state->info;
693 
694   GST_OBJECT_LOCK (self);
695   gst_vtenc_destroy_session (self, &self->session);
696   GST_OBJECT_UNLOCK (self);
697 
698   gst_vtenc_negotiate_profile_and_level (enc);
699 
700   session = gst_vtenc_create_session (self);
701   GST_OBJECT_LOCK (self);
702   self->session = session;
703   GST_OBJECT_UNLOCK (self);
704 
705   return session != NULL;
706 }
707 
708 static gboolean
gst_vtenc_is_negotiated(GstVTEnc * self)709 gst_vtenc_is_negotiated (GstVTEnc * self)
710 {
711   return self->negotiated_width != 0;
712 }
713 
714 static gboolean
gst_vtenc_negotiate_downstream(GstVTEnc * self,CMSampleBufferRef sbuf)715 gst_vtenc_negotiate_downstream (GstVTEnc * self, CMSampleBufferRef sbuf)
716 {
717   gboolean result;
718   GstCaps *caps;
719   GstStructure *s;
720   GstVideoCodecState *state;
721 
722   if (self->caps_width == self->negotiated_width &&
723       self->caps_height == self->negotiated_height &&
724       self->caps_fps_n == self->negotiated_fps_n &&
725       self->caps_fps_d == self->negotiated_fps_d) {
726     return TRUE;
727   }
728 
729   caps = gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (self));
730   caps = gst_caps_make_writable (caps);
731   s = gst_caps_get_structure (caps, 0);
732   gst_structure_set (s,
733       "width", G_TYPE_INT, self->negotiated_width,
734       "height", G_TYPE_INT, self->negotiated_height,
735       "framerate", GST_TYPE_FRACTION,
736       self->negotiated_fps_n, self->negotiated_fps_d, NULL);
737 
738   if (self->details->format_id == kCMVideoCodecType_H264) {
739     CMFormatDescriptionRef fmt;
740     CFDictionaryRef atoms;
741     CFStringRef avccKey;
742     CFDataRef avcc;
743     guint8 *codec_data;
744     gsize codec_data_size;
745     GstBuffer *codec_data_buf;
746     guint8 sps[3];
747 
748     fmt = CMSampleBufferGetFormatDescription (sbuf);
749     atoms = CMFormatDescriptionGetExtension (fmt,
750         kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms);
751     avccKey = CFStringCreateWithCString (NULL, "avcC", kCFStringEncodingUTF8);
752     avcc = CFDictionaryGetValue (atoms, avccKey);
753     CFRelease (avccKey);
754     codec_data_size = CFDataGetLength (avcc);
755     codec_data = g_malloc (codec_data_size);
756     CFDataGetBytes (avcc, CFRangeMake (0, codec_data_size), codec_data);
757     codec_data_buf = gst_buffer_new_wrapped (codec_data, codec_data_size);
758 
759     gst_structure_set (s, "codec_data", GST_TYPE_BUFFER, codec_data_buf, NULL);
760 
761     sps[0] = codec_data[1];
762     sps[1] = codec_data[2] & ~0xDF;
763     sps[2] = codec_data[3];
764 
765     gst_codec_utils_h264_caps_set_level_and_profile (caps, sps, 3);
766 
767     gst_buffer_unref (codec_data_buf);
768   }
769 
770   state =
771       gst_video_encoder_set_output_state (GST_VIDEO_ENCODER_CAST (self), caps,
772       self->input_state);
773   gst_video_codec_state_unref (state);
774   result = gst_video_encoder_negotiate (GST_VIDEO_ENCODER_CAST (self));
775 
776   self->caps_width = self->negotiated_width;
777   self->caps_height = self->negotiated_height;
778   self->caps_fps_n = self->negotiated_fps_n;
779   self->caps_fps_d = self->negotiated_fps_d;
780 
781   return result;
782 }
783 
784 static void
gst_vtenc_clear_cached_caps_downstream(GstVTEnc * self)785 gst_vtenc_clear_cached_caps_downstream (GstVTEnc * self)
786 {
787   self->caps_width = self->caps_height = 0;
788   self->caps_fps_n = self->caps_fps_d = 0;
789 }
790 
791 static GstFlowReturn
gst_vtenc_handle_frame(GstVideoEncoder * enc,GstVideoCodecFrame * frame)792 gst_vtenc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame)
793 {
794   GstVTEnc *self = GST_VTENC_CAST (enc);
795 
796   if (!gst_vtenc_is_negotiated (self))
797     goto not_negotiated;
798 
799   return gst_vtenc_encode_frame (self, frame);
800 
801 not_negotiated:
802   gst_video_codec_frame_unref (frame);
803   return GST_FLOW_NOT_NEGOTIATED;
804 }
805 
806 static GstFlowReturn
gst_vtenc_finish(GstVideoEncoder * enc)807 gst_vtenc_finish (GstVideoEncoder * enc)
808 {
809   GstVTEnc *self = GST_VTENC_CAST (enc);
810   return gst_vtenc_finish_encoding (self, FALSE);
811 }
812 
813 static gboolean
gst_vtenc_flush(GstVideoEncoder * enc)814 gst_vtenc_flush (GstVideoEncoder * enc)
815 {
816   GstVTEnc *self = GST_VTENC_CAST (enc);
817   GstFlowReturn ret;
818 
819   ret = gst_vtenc_finish_encoding (self, TRUE);
820 
821   return (ret == GST_FLOW_OK);
822 }
823 
824 static VTCompressionSessionRef
gst_vtenc_create_session(GstVTEnc * self)825 gst_vtenc_create_session (GstVTEnc * self)
826 {
827   VTCompressionSessionRef session = NULL;
828   CFMutableDictionaryRef encoder_spec = NULL, pb_attrs;
829   OSStatus status;
830 
831 #if !HAVE_IOS
832   const GstVTEncoderDetails *codec_details =
833       GST_VTENC_CLASS_GET_CODEC_DETAILS (G_OBJECT_GET_CLASS (self));
834 
835   encoder_spec =
836       CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks,
837       &kCFTypeDictionaryValueCallBacks);
838   gst_vtutil_dict_set_boolean (encoder_spec,
839       kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, true);
840   if (codec_details->require_hardware)
841     gst_vtutil_dict_set_boolean (encoder_spec,
842         kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder,
843         TRUE);
844 #endif
845 
846   pb_attrs = CFDictionaryCreateMutable (NULL, 0, &kCFTypeDictionaryKeyCallBacks,
847       &kCFTypeDictionaryValueCallBacks);
848   gst_vtutil_dict_set_i32 (pb_attrs, kCVPixelBufferWidthKey,
849       self->negotiated_width);
850   gst_vtutil_dict_set_i32 (pb_attrs, kCVPixelBufferHeightKey,
851       self->negotiated_height);
852 
853   status = VTCompressionSessionCreate (NULL,
854       self->negotiated_width, self->negotiated_height,
855       self->details->format_id, encoder_spec, pb_attrs, NULL,
856       gst_vtenc_enqueue_buffer, self, &session);
857   GST_INFO_OBJECT (self, "VTCompressionSessionCreate for %d x %d => %d",
858       self->negotiated_width, self->negotiated_height, (int) status);
859   if (status != noErr) {
860     GST_ERROR_OBJECT (self, "VTCompressionSessionCreate() returned: %d",
861         (int) status);
862     goto beach;
863   }
864 
865   gst_vtenc_session_configure_expected_framerate (self, session,
866       (gdouble) self->negotiated_fps_n / (gdouble) self->negotiated_fps_d);
867 
868   status = VTSessionSetProperty (session,
869       kVTCompressionPropertyKey_ProfileLevel, self->profile_level);
870   GST_DEBUG_OBJECT (self, "kVTCompressionPropertyKey_ProfileLevel => %d",
871       (int) status);
872 
873   status = VTSessionSetProperty (session,
874       kVTCompressionPropertyKey_AllowTemporalCompression, kCFBooleanTrue);
875   GST_DEBUG_OBJECT (self,
876       "kVTCompressionPropertyKey_AllowTemporalCompression => %d", (int) status);
877 
878   gst_vtenc_session_configure_max_keyframe_interval (self, session,
879       self->max_keyframe_interval);
880   gst_vtenc_session_configure_max_keyframe_interval_duration (self, session,
881       self->max_keyframe_interval_duration / ((gdouble) GST_SECOND));
882 
883   gst_vtenc_session_configure_bitrate (self, session,
884       gst_vtenc_get_bitrate (self));
885   gst_vtenc_session_configure_realtime (self, session,
886       gst_vtenc_get_realtime (self));
887   gst_vtenc_session_configure_allow_frame_reordering (self, session,
888       gst_vtenc_get_allow_frame_reordering (self));
889   gst_vtenc_set_quality (self, self->quality);
890 
891   if (self->dump_properties) {
892     gst_vtenc_session_dump_properties (self, session);
893     self->dump_properties = FALSE;
894   }
895 #ifdef HAVE_VIDEOTOOLBOX_10_9_6
896   if (VTCompressionSessionPrepareToEncodeFrames) {
897     status = VTCompressionSessionPrepareToEncodeFrames (session);
898     if (status != noErr) {
899       GST_ERROR_OBJECT (self,
900           "VTCompressionSessionPrepareToEncodeFrames() returned: %d",
901           (int) status);
902     }
903   }
904 #endif
905 
906 beach:
907   if (encoder_spec)
908     CFRelease (encoder_spec);
909   CFRelease (pb_attrs);
910 
911   return session;
912 }
913 
914 static void
gst_vtenc_destroy_session(GstVTEnc * self,VTCompressionSessionRef * session)915 gst_vtenc_destroy_session (GstVTEnc * self, VTCompressionSessionRef * session)
916 {
917   VTCompressionSessionInvalidate (*session);
918   if (*session != NULL) {
919     CFRelease (*session);
920     *session = NULL;
921   }
922 }
923 
924 typedef struct
925 {
926   GstVTEnc *self;
927   VTCompressionSessionRef session;
928 } GstVTDumpPropCtx;
929 
930 static void
gst_vtenc_session_dump_property(CFStringRef prop_name,CFDictionaryRef prop_attrs,GstVTDumpPropCtx * dpc)931 gst_vtenc_session_dump_property (CFStringRef prop_name,
932     CFDictionaryRef prop_attrs, GstVTDumpPropCtx * dpc)
933 {
934   gchar *name_str;
935   CFTypeRef prop_value;
936   OSStatus status;
937 
938   name_str = gst_vtutil_string_to_utf8 (prop_name);
939   if (dpc->self->dump_attributes) {
940     gchar *attrs_str;
941 
942     attrs_str = gst_vtutil_object_to_string (prop_attrs);
943     GST_DEBUG_OBJECT (dpc->self, "%s = %s", name_str, attrs_str);
944     g_free (attrs_str);
945   }
946 
947   status = VTSessionCopyProperty (dpc->session, prop_name, NULL, &prop_value);
948   if (status == noErr) {
949     gchar *value_str;
950 
951     value_str = gst_vtutil_object_to_string (prop_value);
952     GST_DEBUG_OBJECT (dpc->self, "%s = %s", name_str, value_str);
953     g_free (value_str);
954 
955     if (prop_value != NULL)
956       CFRelease (prop_value);
957   } else {
958     GST_DEBUG_OBJECT (dpc->self, "%s = <failed to query: %d>",
959         name_str, (int) status);
960   }
961 
962   g_free (name_str);
963 }
964 
965 static void
gst_vtenc_session_dump_properties(GstVTEnc * self,VTCompressionSessionRef session)966 gst_vtenc_session_dump_properties (GstVTEnc * self,
967     VTCompressionSessionRef session)
968 {
969   GstVTDumpPropCtx dpc = { self, session };
970   CFDictionaryRef dict;
971   OSStatus status;
972 
973   status = VTSessionCopySupportedPropertyDictionary (session, &dict);
974   if (status != noErr)
975     goto error;
976   CFDictionaryApplyFunction (dict,
977       (CFDictionaryApplierFunction) gst_vtenc_session_dump_property, &dpc);
978   CFRelease (dict);
979 
980   return;
981 
982 error:
983   GST_WARNING_OBJECT (self, "failed to dump properties");
984 }
985 
986 static void
gst_vtenc_session_configure_expected_framerate(GstVTEnc * self,VTCompressionSessionRef session,gdouble framerate)987 gst_vtenc_session_configure_expected_framerate (GstVTEnc * self,
988     VTCompressionSessionRef session, gdouble framerate)
989 {
990   gst_vtenc_session_configure_property_double (self, session,
991       kVTCompressionPropertyKey_ExpectedFrameRate, framerate);
992 }
993 
994 static void
gst_vtenc_session_configure_max_keyframe_interval(GstVTEnc * self,VTCompressionSessionRef session,gint interval)995 gst_vtenc_session_configure_max_keyframe_interval (GstVTEnc * self,
996     VTCompressionSessionRef session, gint interval)
997 {
998   gst_vtenc_session_configure_property_int (self, session,
999       kVTCompressionPropertyKey_MaxKeyFrameInterval, interval);
1000 }
1001 
1002 static void
gst_vtenc_session_configure_max_keyframe_interval_duration(GstVTEnc * self,VTCompressionSessionRef session,gdouble duration)1003 gst_vtenc_session_configure_max_keyframe_interval_duration (GstVTEnc * self,
1004     VTCompressionSessionRef session, gdouble duration)
1005 {
1006   gst_vtenc_session_configure_property_double (self, session,
1007       kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, duration);
1008 }
1009 
1010 static void
gst_vtenc_session_configure_bitrate(GstVTEnc * self,VTCompressionSessionRef session,guint bitrate)1011 gst_vtenc_session_configure_bitrate (GstVTEnc * self,
1012     VTCompressionSessionRef session, guint bitrate)
1013 {
1014   gst_vtenc_session_configure_property_int (self, session,
1015       kVTCompressionPropertyKey_AverageBitRate, bitrate);
1016 }
1017 
1018 static void
gst_vtenc_session_configure_allow_frame_reordering(GstVTEnc * self,VTCompressionSessionRef session,gboolean allow_frame_reordering)1019 gst_vtenc_session_configure_allow_frame_reordering (GstVTEnc * self,
1020     VTCompressionSessionRef session, gboolean allow_frame_reordering)
1021 {
1022   VTSessionSetProperty (session, kVTCompressionPropertyKey_AllowFrameReordering,
1023       allow_frame_reordering ? kCFBooleanTrue : kCFBooleanFalse);
1024 }
1025 
1026 static void
gst_vtenc_session_configure_realtime(GstVTEnc * self,VTCompressionSessionRef session,gboolean realtime)1027 gst_vtenc_session_configure_realtime (GstVTEnc * self,
1028     VTCompressionSessionRef session, gboolean realtime)
1029 {
1030   VTSessionSetProperty (session, kVTCompressionPropertyKey_RealTime,
1031       realtime ? kCFBooleanTrue : kCFBooleanFalse);
1032 }
1033 
1034 static OSStatus
gst_vtenc_session_configure_property_int(GstVTEnc * self,VTCompressionSessionRef session,CFStringRef name,gint value)1035 gst_vtenc_session_configure_property_int (GstVTEnc * self,
1036     VTCompressionSessionRef session, CFStringRef name, gint value)
1037 {
1038   CFNumberRef num;
1039   OSStatus status;
1040   gchar name_str[128];
1041 
1042   num = CFNumberCreate (NULL, kCFNumberIntType, &value);
1043   status = VTSessionSetProperty (session, name, num);
1044   CFRelease (num);
1045 
1046   CFStringGetCString (name, name_str, sizeof (name_str), kCFStringEncodingUTF8);
1047   GST_DEBUG_OBJECT (self, "%s(%d) => %d", name_str, value, (int) status);
1048 
1049   return status;
1050 }
1051 
1052 static OSStatus
gst_vtenc_session_configure_property_double(GstVTEnc * self,VTCompressionSessionRef session,CFStringRef name,gdouble value)1053 gst_vtenc_session_configure_property_double (GstVTEnc * self,
1054     VTCompressionSessionRef session, CFStringRef name, gdouble value)
1055 {
1056   CFNumberRef num;
1057   OSStatus status;
1058   gchar name_str[128];
1059 
1060   num = CFNumberCreate (NULL, kCFNumberDoubleType, &value);
1061   status = VTSessionSetProperty (session, name, num);
1062   CFRelease (num);
1063 
1064   CFStringGetCString (name, name_str, sizeof (name_str), kCFStringEncodingUTF8);
1065   GST_DEBUG_OBJECT (self, "%s(%f) => %d", name_str, value, (int) status);
1066 
1067   return status;
1068 }
1069 
1070 static void
gst_vtenc_update_latency(GstVTEnc * self)1071 gst_vtenc_update_latency (GstVTEnc * self)
1072 {
1073   OSStatus status;
1074   CFNumberRef value;
1075   int frames = 0;
1076   GstClockTime frame_duration;
1077   GstClockTime latency;
1078 
1079   if (self->video_info.fps_d == 0) {
1080     GST_INFO_OBJECT (self, "framerate not known, can't set latency");
1081     return;
1082   }
1083 
1084   status = VTSessionCopyProperty (self->session,
1085       kVTCompressionPropertyKey_NumberOfPendingFrames, NULL, &value);
1086   if (status != noErr || !value) {
1087     GST_INFO_OBJECT (self, "failed to get NumberOfPendingFrames: %d", status);
1088     return;
1089   }
1090 
1091   CFNumberGetValue (value, kCFNumberSInt32Type, &frames);
1092   if (self->latency_frames == -1 || self->latency_frames != frames) {
1093     self->latency_frames = frames;
1094     if (self->video_info.fps_d == 0 || self->video_info.fps_n == 0) {
1095       /* FIXME: Assume 25fps. This is better than reporting no latency at
1096        * all and then later failing in live pipelines
1097        */
1098       frame_duration = gst_util_uint64_scale (GST_SECOND, 1, 25);
1099     } else {
1100       frame_duration = gst_util_uint64_scale (GST_SECOND,
1101           self->video_info.fps_d, self->video_info.fps_n);
1102     }
1103     latency = frame_duration * frames;
1104     GST_INFO_OBJECT (self,
1105         "latency status %d frames %d fps %d/%d time %" GST_TIME_FORMAT, status,
1106         frames, self->video_info.fps_n, self->video_info.fps_d,
1107         GST_TIME_ARGS (latency));
1108     gst_video_encoder_set_latency (GST_VIDEO_ENCODER (self), latency, latency);
1109   }
1110   CFRelease (value);
1111 }
1112 
1113 static GstFlowReturn
gst_vtenc_encode_frame(GstVTEnc * self,GstVideoCodecFrame * frame)1114 gst_vtenc_encode_frame (GstVTEnc * self, GstVideoCodecFrame * frame)
1115 {
1116   CMTime ts, duration;
1117   GstCoreMediaMeta *meta;
1118   CVPixelBufferRef pbuf = NULL;
1119   GstVideoCodecFrame *outframe;
1120   OSStatus vt_status;
1121   GstFlowReturn ret = GST_FLOW_OK;
1122   gboolean renegotiated;
1123   CFDictionaryRef frame_props = NULL;
1124 
1125   if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
1126     GST_INFO_OBJECT (self, "received force-keyframe-event, will force intra");
1127     frame_props = self->keyframe_props;
1128   }
1129 
1130   ts = CMTimeMake (frame->pts, GST_SECOND);
1131   if (frame->duration != GST_CLOCK_TIME_NONE)
1132     duration = CMTimeMake (frame->duration, GST_SECOND);
1133   else
1134     duration = kCMTimeInvalid;
1135 
1136   meta = gst_buffer_get_core_media_meta (frame->input_buffer);
1137   if (meta != NULL) {
1138     pbuf = gst_core_media_buffer_get_pixel_buffer (frame->input_buffer);
1139   }
1140 #ifdef HAVE_IOS
1141   if (pbuf == NULL) {
1142     GstVideoFrame inframe, outframe;
1143     GstBuffer *outbuf;
1144     OSType pixel_format_type;
1145     CVReturn cv_ret;
1146 
1147     /* FIXME: iOS has special stride requirements that we don't know yet.
1148      * Copy into a newly allocated pixelbuffer for now. Probably makes
1149      * sense to create a buffer pool around these at some point.
1150      */
1151 
1152     switch (GST_VIDEO_INFO_FORMAT (&self->video_info)) {
1153       case GST_VIDEO_FORMAT_I420:
1154         pixel_format_type = kCVPixelFormatType_420YpCbCr8Planar;
1155         break;
1156       case GST_VIDEO_FORMAT_NV12:
1157         pixel_format_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
1158         break;
1159       default:
1160         goto cv_error;
1161     }
1162 
1163     if (!gst_video_frame_map (&inframe, &self->video_info, frame->input_buffer,
1164             GST_MAP_READ))
1165       goto cv_error;
1166 
1167     cv_ret =
1168         CVPixelBufferCreate (NULL, self->negotiated_width,
1169         self->negotiated_height, pixel_format_type, NULL, &pbuf);
1170 
1171     if (cv_ret != kCVReturnSuccess) {
1172       gst_video_frame_unmap (&inframe);
1173       goto cv_error;
1174     }
1175 
1176     outbuf =
1177         gst_core_video_buffer_new ((CVBufferRef) pbuf, &self->video_info, NULL);
1178     if (!gst_video_frame_map (&outframe, &self->video_info, outbuf,
1179             GST_MAP_WRITE)) {
1180       gst_video_frame_unmap (&inframe);
1181       gst_buffer_unref (outbuf);
1182       CVPixelBufferRelease (pbuf);
1183       goto cv_error;
1184     }
1185 
1186     if (!gst_video_frame_copy (&outframe, &inframe)) {
1187       gst_video_frame_unmap (&inframe);
1188       gst_buffer_unref (outbuf);
1189       CVPixelBufferRelease (pbuf);
1190       goto cv_error;
1191     }
1192 
1193     gst_buffer_unref (outbuf);
1194     gst_video_frame_unmap (&inframe);
1195     gst_video_frame_unmap (&outframe);
1196   }
1197 #else
1198   if (pbuf == NULL) {
1199     GstVTEncFrame *vframe;
1200     CVReturn cv_ret;
1201 
1202     vframe = gst_vtenc_frame_new (frame->input_buffer, &self->video_info);
1203     if (!vframe)
1204       goto cv_error;
1205 
1206     {
1207       const size_t num_planes = GST_VIDEO_FRAME_N_PLANES (&vframe->videoframe);
1208       void *plane_base_addresses[GST_VIDEO_MAX_PLANES];
1209       size_t plane_widths[GST_VIDEO_MAX_PLANES];
1210       size_t plane_heights[GST_VIDEO_MAX_PLANES];
1211       size_t plane_bytes_per_row[GST_VIDEO_MAX_PLANES];
1212       OSType pixel_format_type;
1213       size_t i;
1214 
1215       for (i = 0; i < num_planes; i++) {
1216         plane_base_addresses[i] =
1217             GST_VIDEO_FRAME_PLANE_DATA (&vframe->videoframe, i);
1218         plane_widths[i] = GST_VIDEO_FRAME_COMP_WIDTH (&vframe->videoframe, i);
1219         plane_heights[i] = GST_VIDEO_FRAME_COMP_HEIGHT (&vframe->videoframe, i);
1220         plane_bytes_per_row[i] =
1221             GST_VIDEO_FRAME_COMP_STRIDE (&vframe->videoframe, i);
1222         plane_bytes_per_row[i] =
1223             GST_VIDEO_FRAME_COMP_STRIDE (&vframe->videoframe, i);
1224       }
1225 
1226       switch (GST_VIDEO_INFO_FORMAT (&self->video_info)) {
1227         case GST_VIDEO_FORMAT_I420:
1228           pixel_format_type = kCVPixelFormatType_420YpCbCr8Planar;
1229           break;
1230         case GST_VIDEO_FORMAT_NV12:
1231           pixel_format_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
1232           break;
1233         case GST_VIDEO_FORMAT_UYVY:
1234           pixel_format_type = kCVPixelFormatType_422YpCbCr8;
1235           break;
1236         default:
1237           gst_vtenc_frame_free (vframe);
1238           goto cv_error;
1239       }
1240 
1241       cv_ret = CVPixelBufferCreateWithPlanarBytes (NULL,
1242           self->negotiated_width, self->negotiated_height,
1243           pixel_format_type,
1244           frame,
1245           GST_VIDEO_FRAME_SIZE (&vframe->videoframe),
1246           num_planes,
1247           plane_base_addresses,
1248           plane_widths,
1249           plane_heights,
1250           plane_bytes_per_row, gst_pixel_buffer_release_cb, vframe, NULL,
1251           &pbuf);
1252       if (cv_ret != kCVReturnSuccess) {
1253         gst_vtenc_frame_free (vframe);
1254         goto cv_error;
1255       }
1256     }
1257   }
1258 #endif
1259 
1260   /* We need to unlock the stream lock here because
1261    * it can wait for gst_vtenc_enqueue_buffer() to
1262    * handle a buffer... which will take the stream
1263    * lock from another thread and then deadlock */
1264   GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
1265   vt_status = VTCompressionSessionEncodeFrame (self->session,
1266       pbuf, ts, duration, frame_props,
1267       GINT_TO_POINTER (frame->system_frame_number), NULL);
1268   GST_VIDEO_ENCODER_STREAM_LOCK (self);
1269 
1270   if (vt_status != noErr) {
1271     GST_WARNING_OBJECT (self, "VTCompressionSessionEncodeFrame returned %d",
1272         (int) vt_status);
1273   }
1274 
1275   gst_video_codec_frame_unref (frame);
1276 
1277   CVPixelBufferRelease (pbuf);
1278 
1279   renegotiated = FALSE;
1280   while ((outframe = g_async_queue_try_pop (self->cur_outframes))) {
1281     if (outframe->output_buffer) {
1282       if (!renegotiated) {
1283         meta = gst_buffer_get_core_media_meta (outframe->output_buffer);
1284         /* Try to renegotiate once */
1285         if (meta) {
1286           if (gst_vtenc_negotiate_downstream (self, meta->sample_buf)) {
1287             renegotiated = TRUE;
1288           } else {
1289             ret = GST_FLOW_NOT_NEGOTIATED;
1290             gst_video_codec_frame_unref (outframe);
1291             /* the rest of the frames will be pop'd and unref'd later */
1292             break;
1293           }
1294         }
1295       }
1296 
1297       gst_vtenc_update_latency (self);
1298     }
1299 
1300     /* releases frame, even if it has no output buffer (i.e. failed to encode) */
1301     ret =
1302         gst_video_encoder_finish_frame (GST_VIDEO_ENCODER_CAST (self),
1303         outframe);
1304   }
1305 
1306   return ret;
1307 
1308 cv_error:
1309   {
1310     gst_video_codec_frame_unref (frame);
1311     return GST_FLOW_ERROR;
1312   }
1313 }
1314 
1315 static void
gst_vtenc_enqueue_buffer(void * outputCallbackRefCon,void * sourceFrameRefCon,OSStatus status,VTEncodeInfoFlags infoFlags,CMSampleBufferRef sampleBuffer)1316 gst_vtenc_enqueue_buffer (void *outputCallbackRefCon,
1317     void *sourceFrameRefCon,
1318     OSStatus status,
1319     VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
1320 {
1321   GstVTEnc *self = outputCallbackRefCon;
1322   gboolean is_keyframe;
1323   GstVideoCodecFrame *frame;
1324 
1325   frame =
1326       gst_video_encoder_get_frame (GST_VIDEO_ENCODER_CAST (self),
1327       GPOINTER_TO_INT (sourceFrameRefCon));
1328 
1329   if (status != noErr) {
1330     if (frame) {
1331       GST_ELEMENT_ERROR (self, LIBRARY, ENCODE, (NULL),
1332           ("Failed to encode frame %d: %d", frame->system_frame_number,
1333               (int) status));
1334     } else {
1335       GST_ELEMENT_ERROR (self, LIBRARY, ENCODE, (NULL),
1336           ("Failed to encode (frame unknown): %d", (int) status));
1337     }
1338     goto beach;
1339   }
1340 
1341   if (!frame) {
1342     GST_WARNING_OBJECT (self, "No corresponding frame found!");
1343     goto beach;
1344   }
1345 
1346   /* This may happen if we don't have enough bitrate */
1347   if (sampleBuffer == NULL)
1348     goto beach;
1349 
1350   is_keyframe = gst_vtenc_buffer_is_keyframe (self, sampleBuffer);
1351 
1352   if (is_keyframe) {
1353     GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
1354     gst_vtenc_clear_cached_caps_downstream (self);
1355   }
1356 
1357   /* We are dealing with block buffers here, so we don't need
1358    * to enable the use of the video meta API on the core media buffer */
1359   frame->output_buffer = gst_core_media_buffer_new (sampleBuffer, FALSE, NULL);
1360 
1361 beach:
1362   /* needed anyway so the frame will be released */
1363   if (frame)
1364     g_async_queue_push (self->cur_outframes, frame);
1365 }
1366 
1367 static gboolean
gst_vtenc_buffer_is_keyframe(GstVTEnc * self,CMSampleBufferRef sbuf)1368 gst_vtenc_buffer_is_keyframe (GstVTEnc * self, CMSampleBufferRef sbuf)
1369 {
1370   gboolean result = FALSE;
1371   CFArrayRef attachments_for_sample;
1372 
1373   attachments_for_sample = CMSampleBufferGetSampleAttachmentsArray (sbuf, 0);
1374   if (attachments_for_sample != NULL) {
1375     CFDictionaryRef attachments;
1376     CFBooleanRef depends_on_others;
1377 
1378     attachments = CFArrayGetValueAtIndex (attachments_for_sample, 0);
1379     depends_on_others = CFDictionaryGetValue (attachments,
1380         kCMSampleAttachmentKey_DependsOnOthers);
1381     result = (depends_on_others == kCFBooleanFalse);
1382   }
1383 
1384   return result;
1385 }
1386 
1387 #ifndef HAVE_IOS
1388 static GstVTEncFrame *
gst_vtenc_frame_new(GstBuffer * buf,GstVideoInfo * video_info)1389 gst_vtenc_frame_new (GstBuffer * buf, GstVideoInfo * video_info)
1390 {
1391   GstVTEncFrame *frame;
1392 
1393   frame = g_slice_new (GstVTEncFrame);
1394   frame->buf = gst_buffer_ref (buf);
1395   if (!gst_video_frame_map (&frame->videoframe, video_info, buf, GST_MAP_READ)) {
1396     gst_buffer_unref (frame->buf);
1397     g_slice_free (GstVTEncFrame, frame);
1398     return NULL;
1399   }
1400 
1401   return frame;
1402 }
1403 
1404 static void
gst_vtenc_frame_free(GstVTEncFrame * frame)1405 gst_vtenc_frame_free (GstVTEncFrame * frame)
1406 {
1407   gst_video_frame_unmap (&frame->videoframe);
1408   gst_buffer_unref (frame->buf);
1409   g_slice_free (GstVTEncFrame, frame);
1410 }
1411 
1412 static void
gst_pixel_buffer_release_cb(void * releaseRefCon,const void * dataPtr,size_t dataSize,size_t numberOfPlanes,const void * planeAddresses[])1413 gst_pixel_buffer_release_cb (void *releaseRefCon, const void *dataPtr,
1414     size_t dataSize, size_t numberOfPlanes, const void *planeAddresses[])
1415 {
1416   GstVTEncFrame *frame = (GstVTEncFrame *) releaseRefCon;
1417   gst_vtenc_frame_free (frame);
1418 }
1419 #endif
1420 
1421 static void
gst_vtenc_register(GstPlugin * plugin,const GstVTEncoderDetails * codec_details)1422 gst_vtenc_register (GstPlugin * plugin,
1423     const GstVTEncoderDetails * codec_details)
1424 {
1425   GTypeInfo type_info = {
1426     sizeof (GstVTEncClass),
1427     (GBaseInitFunc) gst_vtenc_base_init,
1428     NULL,
1429     (GClassInitFunc) gst_vtenc_class_init,
1430     NULL,
1431     NULL,
1432     sizeof (GstVTEnc),
1433     0,
1434     (GInstanceInitFunc) gst_vtenc_init,
1435   };
1436   gchar *type_name;
1437   GType type;
1438   gboolean result;
1439 
1440   type_name = g_strdup_printf ("vtenc_%s", codec_details->element_name);
1441 
1442   type =
1443       g_type_register_static (GST_TYPE_VIDEO_ENCODER, type_name, &type_info, 0);
1444 
1445   g_type_set_qdata (type, GST_VTENC_CODEC_DETAILS_QDATA,
1446       (gpointer) codec_details);
1447 
1448   result = gst_element_register (plugin, type_name, GST_RANK_PRIMARY, type);
1449   if (!result) {
1450     GST_ERROR_OBJECT (plugin, "failed to register element %s", type_name);
1451   }
1452 
1453   g_free (type_name);
1454 }
1455 
1456 static const GstVTEncoderDetails gst_vtenc_codecs[] = {
1457   {"H.264", "h264", "video/x-h264", kCMVideoCodecType_H264, FALSE},
1458 #ifndef HAVE_IOS
1459   {"H.264 (HW only)", "h264_hw", "video/x-h264", kCMVideoCodecType_H264, TRUE},
1460 #endif
1461 };
1462 
1463 void
gst_vtenc_register_elements(GstPlugin * plugin)1464 gst_vtenc_register_elements (GstPlugin * plugin)
1465 {
1466   guint i;
1467 
1468   GST_DEBUG_CATEGORY_INIT (gst_vtenc_debug, "vtenc",
1469       0, "Apple VideoToolbox Encoder Wrapper");
1470 
1471   for (i = 0; i != G_N_ELEMENTS (gst_vtenc_codecs); i++)
1472     gst_vtenc_register (plugin, &gst_vtenc_codecs[i]);
1473 }
1474