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