2 * Copyright (C) 2010, 2013 Ole André Vadla Ravnås <oleavr@soundrop.com>
3 * Copyright (C) 2013 Intel Corporation
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.
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.
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.
26 #include "coremediabuffer.h"
27 #include "corevideobuffer.h"
29 #include <gst/pbutils/codec-utils.h>
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
39 GST_DEBUG_CATEGORY (gst_vtenc_debug);
40 #define GST_CAT_DEFAULT (gst_vtenc_debug)
42 #define GST_VTENC_CODEC_DETAILS_QDATA \
43 g_quark_from_static_string ("vtenc-codec-details")
45 /* define EnableHardwareAcceleratedVideoEncoder in < 10.9 */
46 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 1090
48 kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder =
49 CFSTR ("EnableHardwareAcceleratedVideoEncoder");
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");
59 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 1080
60 const CFStringRef kVTCompressionPropertyKey_Quality = CFSTR ("Quality");
63 #ifdef HAVE_VIDEOTOOLBOX_10_9_6
65 VTCompressionSessionPrepareToEncodeFrames (VTCompressionSessionRef session)
66 __attribute__ ((weak_import));
74 PROP_ALLOW_FRAME_REORDERING,
77 PROP_MAX_KEYFRAME_INTERVAL,
78 PROP_MAX_KEYFRAME_INTERVAL_DURATION
81 typedef struct _GstVTEncFrame GstVTEncFrame;
86 GstVideoFrame videoframe;
89 static GstElementClass *parent_class = NULL;
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);
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);
106 static void gst_vtenc_clear_cached_caps_downstream (GstVTEnc * self);
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);
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);
140 static GstVTEncFrame *gst_vtenc_frame_new (GstBuffer * buf,
141 GstVideoInfo * videoinfo);
142 static void gst_vtenc_frame_free (GstVTEncFrame * frame);
144 static void gst_pixel_buffer_release_cb (void *releaseRefCon,
145 const void *dataPtr, size_t dataSize, size_t numberOfPlanes,
146 const void *planeAddresses[]);
150 static GstStaticCaps sink_caps =
151 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, I420 }"));
153 static GstStaticCaps sink_caps =
154 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ UYVY, NV12, I420 }"));
158 gst_vtenc_base_init (GstVTEncClass * klass)
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;
169 gchar *longname, *description;
171 longname = g_strdup_printf ("%s encoder", codec_details->name);
172 description = g_strdup_printf ("%s encoder", codec_details->name);
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>");
179 g_free (description);
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);
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);
195 src_template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
197 gst_element_class_add_pad_template (element_class, src_template);
198 gst_caps_unref (src_caps);
202 gst_vtenc_class_init (GstVTEncClass * klass)
204 GObjectClass *gobject_class;
205 GstVideoEncoderClass *gstvideoencoder_class;
207 gobject_class = (GObjectClass *) klass;
208 gstvideoencoder_class = (GstVideoEncoderClass *) klass;
210 parent_class = g_type_class_peek_parent (klass);
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;
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;
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));
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));
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));
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));
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));
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));
263 gst_vtenc_init (GstVTEnc * self)
265 GstVTEncClass *klass = (GstVTEncClass *) G_OBJECT_GET_CLASS (self);
266 CFStringRef keyframe_props_keys[] = { kVTEncodeFrameOptionKey_ForceKeyFrame };
267 CFBooleanRef keyframe_props_values[] = { kCFBooleanTrue };
269 self->details = GST_VTENC_CLASS_GET_CODEC_DETAILS (klass);
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;
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);
285 gst_vtenc_finalize (GObject * obj)
287 GstVTEnc *self = GST_VTENC_CAST (obj);
289 CFRelease (self->keyframe_props);
291 G_OBJECT_CLASS (parent_class)->finalize (obj);
295 gst_vtenc_get_bitrate (GstVTEnc * self)
299 GST_OBJECT_LOCK (self);
300 result = self->bitrate;
301 GST_OBJECT_UNLOCK (self);
307 gst_vtenc_set_bitrate (GstVTEnc * self, guint bitrate)
309 GST_OBJECT_LOCK (self);
311 self->bitrate = bitrate;
313 if (self->session != NULL)
314 gst_vtenc_session_configure_bitrate (self, self->session, bitrate);
316 GST_OBJECT_UNLOCK (self);
320 gst_vtenc_get_allow_frame_reordering (GstVTEnc * self)
324 GST_OBJECT_LOCK (self);
325 result = self->allow_frame_reordering;
326 GST_OBJECT_UNLOCK (self);
332 gst_vtenc_set_allow_frame_reordering (GstVTEnc * self,
333 gboolean allow_frame_reordering)
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);
341 GST_OBJECT_UNLOCK (self);
345 gst_vtenc_get_realtime (GstVTEnc * self)
349 GST_OBJECT_LOCK (self);
350 result = self->realtime;
351 GST_OBJECT_UNLOCK (self);
357 gst_vtenc_set_realtime (GstVTEnc * self, gboolean realtime)
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);
367 gst_vtenc_get_quality (GstVTEnc * self)
371 GST_OBJECT_LOCK (self);
372 result = self->quality;
373 GST_OBJECT_UNLOCK (self);
379 gst_vtenc_set_quality (GstVTEnc * self, gdouble quality)
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);
388 GST_OBJECT_UNLOCK (self);
392 gst_vtenc_get_max_keyframe_interval (GstVTEnc * self)
396 GST_OBJECT_LOCK (self);
397 result = self->max_keyframe_interval;
398 GST_OBJECT_UNLOCK (self);
404 gst_vtenc_set_max_keyframe_interval (GstVTEnc * self, gint interval)
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,
412 GST_OBJECT_UNLOCK (self);
416 gst_vtenc_get_max_keyframe_interval_duration (GstVTEnc * self)
420 GST_OBJECT_LOCK (self);
421 result = self->max_keyframe_interval_duration;
422 GST_OBJECT_UNLOCK (self);
428 gst_vtenc_set_max_keyframe_interval_duration (GstVTEnc * self,
429 GstClockTime interval)
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));
437 GST_OBJECT_UNLOCK (self);
441 gst_vtenc_get_property (GObject * obj, guint prop_id, GValue * value,
444 GstVTEnc *self = GST_VTENC_CAST (obj);
448 g_value_set_uint (value, gst_vtenc_get_bitrate (self) / 1000);
450 case PROP_ALLOW_FRAME_REORDERING:
451 g_value_set_boolean (value, gst_vtenc_get_allow_frame_reordering (self));
454 g_value_set_boolean (value, gst_vtenc_get_realtime (self));
457 g_value_set_double (value, gst_vtenc_get_quality (self));
459 case PROP_MAX_KEYFRAME_INTERVAL:
460 g_value_set_int (value, gst_vtenc_get_max_keyframe_interval (self));
462 case PROP_MAX_KEYFRAME_INTERVAL_DURATION:
463 g_value_set_uint64 (value,
464 gst_vtenc_get_max_keyframe_interval_duration (self));
467 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
473 gst_vtenc_set_property (GObject * obj, guint prop_id, const GValue * value,
476 GstVTEnc *self = GST_VTENC_CAST (obj);
480 gst_vtenc_set_bitrate (self, g_value_get_uint (value) * 1000);
482 case PROP_ALLOW_FRAME_REORDERING:
483 gst_vtenc_set_allow_frame_reordering (self, g_value_get_boolean (value));
486 gst_vtenc_set_realtime (self, g_value_get_boolean (value));
489 gst_vtenc_set_quality (self, g_value_get_double (value));
491 case PROP_MAX_KEYFRAME_INTERVAL:
492 gst_vtenc_set_max_keyframe_interval (self, g_value_get_int (value));
494 case PROP_MAX_KEYFRAME_INTERVAL_DURATION:
495 gst_vtenc_set_max_keyframe_interval_duration (self,
496 g_value_get_uint64 (value));
499 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
505 gst_vtenc_finish_encoding (GstVTEnc * self, gboolean is_flushing)
507 GST_DEBUG_OBJECT (self,
508 "complete encoding and clean buffer queue, is flushing %d", is_flushing);
509 GstVideoCodecFrame *outframe;
510 GstFlowReturn ret = GST_FLOW_OK;
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");
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",
529 while ((outframe = g_async_queue_try_pop (self->cur_outframes))) {
531 GST_DEBUG_OBJECT (self, "flushing frame number %d",
532 outframe->system_frame_number);
533 gst_video_codec_frame_unref (outframe);
535 GST_DEBUG_OBJECT (self, "finish frame number %d",
536 outframe->system_frame_number);
538 gst_video_encoder_finish_frame (GST_VIDEO_ENCODER_CAST (self),
543 GST_DEBUG_OBJECT (self, "buffer queue cleaned");
549 gst_vtenc_start (GstVideoEncoder * enc)
551 GstVTEnc *self = GST_VTENC_CAST (enc);
553 self->cur_outframes = g_async_queue_new ();
559 gst_vtenc_stop (GstVideoEncoder * enc)
561 GstVTEnc *self = GST_VTENC_CAST (enc);
563 GST_VIDEO_ENCODER_STREAM_LOCK (self);
564 gst_vtenc_flush (enc);
565 GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
567 GST_OBJECT_LOCK (self);
568 gst_vtenc_destroy_session (self, &self->session);
569 GST_OBJECT_UNLOCK (self);
571 if (self->profile_level)
572 CFRelease (self->profile_level);
573 self->profile_level = NULL;
575 if (self->input_state)
576 gst_video_codec_state_unref (self->input_state);
577 self->input_state = NULL;
579 self->negotiated_width = self->negotiated_height = 0;
580 self->negotiated_fps_n = self->negotiated_fps_d = 0;
582 gst_vtenc_clear_cached_caps_downstream (self);
584 g_async_queue_unref (self->cur_outframes);
585 self->cur_outframes = NULL;
591 gst_vtenc_profile_level_key (GstVTEnc * self, const gchar * profile,
592 const gchar * level_arg)
596 CFStringRef ret = NULL;
600 if (level_arg == NULL)
601 level_arg = "AutoLevel";
602 strncpy (level, level_arg, sizeof (level));
604 if (!strcmp (profile, "constrained-baseline") ||
605 !strcmp (profile, "baseline")) {
606 profile = "Baseline";
607 } else if (g_str_has_prefix (profile, "high")) {
609 } else if (!strcmp (profile, "main")) {
612 g_assert_not_reached ();
615 if (strlen (level) == 1) {
618 } else if (strlen (level) == 3) {
622 key = g_strdup_printf ("H264_%s_%s", profile, level);
623 ret = CFStringCreateWithBytes (NULL, (const guint8 *) key, strlen (key),
624 kCFStringEncodingASCII, 0);
626 GST_INFO_OBJECT (self, "negotiated profile and level %s", key);
634 gst_vtenc_negotiate_profile_and_level (GstVideoEncoder * enc)
636 GstVTEnc *self = GST_VTENC_CAST (enc);
637 GstCaps *allowed_caps = NULL;
639 const gchar *profile = NULL;
640 const gchar *level = NULL;
642 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (enc));
646 if (gst_caps_is_empty (allowed_caps)) {
647 GST_ERROR_OBJECT (self, "no allowed downstream caps");
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);
655 profile = gst_structure_get_string (s, "profile");
656 level = gst_structure_get_string (s, "level");
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");
669 gst_caps_unref (allowed_caps);
679 gst_vtenc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
681 GstVTEnc *self = GST_VTENC_CAST (enc);
682 VTCompressionSessionRef session;
684 if (self->input_state)
685 gst_video_codec_state_unref (self->input_state);
686 self->input_state = gst_video_codec_state_ref (state);
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;
694 GST_OBJECT_LOCK (self);
695 gst_vtenc_destroy_session (self, &self->session);
696 GST_OBJECT_UNLOCK (self);
698 gst_vtenc_negotiate_profile_and_level (enc);
700 session = gst_vtenc_create_session (self);
701 GST_OBJECT_LOCK (self);
702 self->session = session;
703 GST_OBJECT_UNLOCK (self);
705 return session != NULL;
709 gst_vtenc_is_negotiated (GstVTEnc * self)
711 return self->negotiated_width != 0;
715 gst_vtenc_negotiate_downstream (GstVTEnc * self, CMSampleBufferRef sbuf)
720 GstVideoCodecState *state;
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) {
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);
738 if (self->details->format_id == kCMVideoCodecType_H264) {
739 CMFormatDescriptionRef fmt;
740 CFDictionaryRef atoms;
744 gsize codec_data_size;
745 GstBuffer *codec_data_buf;
748 fmt = CMSampleBufferGetFormatDescription (sbuf);
749 atoms = CMFormatDescriptionGetExtension (fmt,
750 kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms);
751 avccKey = CFStringCreateWithCString (NULL, "avcC", kCFStringEncodingUTF8);
752 avcc = CFDictionaryGetValue (atoms, 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);
759 gst_structure_set (s, "codec_data", GST_TYPE_BUFFER, codec_data_buf, NULL);
761 sps[0] = codec_data[1];
762 sps[1] = codec_data[2] & ~0xDF;
763 sps[2] = codec_data[3];
765 gst_codec_utils_h264_caps_set_level_and_profile (caps, sps, 3);
767 gst_buffer_unref (codec_data_buf);
771 gst_video_encoder_set_output_state (GST_VIDEO_ENCODER_CAST (self), caps,
773 gst_video_codec_state_unref (state);
774 result = gst_video_encoder_negotiate (GST_VIDEO_ENCODER_CAST (self));
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;
785 gst_vtenc_clear_cached_caps_downstream (GstVTEnc * self)
787 self->caps_width = self->caps_height = 0;
788 self->caps_fps_n = self->caps_fps_d = 0;
792 gst_vtenc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame)
794 GstVTEnc *self = GST_VTENC_CAST (enc);
796 if (!gst_vtenc_is_negotiated (self))
799 return gst_vtenc_encode_frame (self, frame);
802 gst_video_codec_frame_unref (frame);
803 return GST_FLOW_NOT_NEGOTIATED;
807 gst_vtenc_finish (GstVideoEncoder * enc)
809 GstVTEnc *self = GST_VTENC_CAST (enc);
810 return gst_vtenc_finish_encoding (self, FALSE);
814 gst_vtenc_flush (GstVideoEncoder * enc)
816 GstVTEnc *self = GST_VTENC_CAST (enc);
819 ret = gst_vtenc_finish_encoding (self, TRUE);
821 return (ret == GST_FLOW_OK);
824 static VTCompressionSessionRef
825 gst_vtenc_create_session (GstVTEnc * self)
827 VTCompressionSessionRef session = NULL;
828 CFMutableDictionaryRef encoder_spec = NULL, pb_attrs;
832 const GstVTEncoderDetails *codec_details =
833 GST_VTENC_CLASS_GET_CODEC_DETAILS (G_OBJECT_GET_CLASS (self));
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,
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);
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",
865 gst_vtenc_session_configure_expected_framerate (self, session,
866 (gdouble) self->negotiated_fps_n / (gdouble) self->negotiated_fps_d);
868 status = VTSessionSetProperty (session,
869 kVTCompressionPropertyKey_ProfileLevel, self->profile_level);
870 GST_DEBUG_OBJECT (self, "kVTCompressionPropertyKey_ProfileLevel => %d",
873 status = VTSessionSetProperty (session,
874 kVTCompressionPropertyKey_AllowTemporalCompression, kCFBooleanTrue);
875 GST_DEBUG_OBJECT (self,
876 "kVTCompressionPropertyKey_AllowTemporalCompression => %d", (int) status);
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));
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);
891 if (self->dump_properties) {
892 gst_vtenc_session_dump_properties (self, session);
893 self->dump_properties = FALSE;
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",
908 CFRelease (encoder_spec);
909 CFRelease (pb_attrs);
915 gst_vtenc_destroy_session (GstVTEnc * self, VTCompressionSessionRef * session)
917 VTCompressionSessionInvalidate (*session);
918 if (*session != NULL) {
919 CFRelease (*session);
927 VTCompressionSessionRef session;
931 gst_vtenc_session_dump_property (CFStringRef prop_name,
932 CFDictionaryRef prop_attrs, GstVTDumpPropCtx * dpc)
935 CFTypeRef prop_value;
938 name_str = gst_vtutil_string_to_utf8 (prop_name);
939 if (dpc->self->dump_attributes) {
942 attrs_str = gst_vtutil_object_to_string (prop_attrs);
943 GST_DEBUG_OBJECT (dpc->self, "%s = %s", name_str, attrs_str);
947 status = VTSessionCopyProperty (dpc->session, prop_name, NULL, &prop_value);
948 if (status == noErr) {
951 value_str = gst_vtutil_object_to_string (prop_value);
952 GST_DEBUG_OBJECT (dpc->self, "%s = %s", name_str, value_str);
955 if (prop_value != NULL)
956 CFRelease (prop_value);
958 GST_DEBUG_OBJECT (dpc->self, "%s = <failed to query: %d>",
959 name_str, (int) status);
966 gst_vtenc_session_dump_properties (GstVTEnc * self,
967 VTCompressionSessionRef session)
969 GstVTDumpPropCtx dpc = { self, session };
970 CFDictionaryRef dict;
973 status = VTSessionCopySupportedPropertyDictionary (session, &dict);
976 CFDictionaryApplyFunction (dict,
977 (CFDictionaryApplierFunction) gst_vtenc_session_dump_property, &dpc);
983 GST_WARNING_OBJECT (self, "failed to dump properties");
987 gst_vtenc_session_configure_expected_framerate (GstVTEnc * self,
988 VTCompressionSessionRef session, gdouble framerate)
990 gst_vtenc_session_configure_property_double (self, session,
991 kVTCompressionPropertyKey_ExpectedFrameRate, framerate);
995 gst_vtenc_session_configure_max_keyframe_interval (GstVTEnc * self,
996 VTCompressionSessionRef session, gint interval)
998 gst_vtenc_session_configure_property_int (self, session,
999 kVTCompressionPropertyKey_MaxKeyFrameInterval, interval);
1003 gst_vtenc_session_configure_max_keyframe_interval_duration (GstVTEnc * self,
1004 VTCompressionSessionRef session, gdouble duration)
1006 gst_vtenc_session_configure_property_double (self, session,
1007 kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, duration);
1011 gst_vtenc_session_configure_bitrate (GstVTEnc * self,
1012 VTCompressionSessionRef session, guint bitrate)
1014 gst_vtenc_session_configure_property_int (self, session,
1015 kVTCompressionPropertyKey_AverageBitRate, bitrate);
1019 gst_vtenc_session_configure_allow_frame_reordering (GstVTEnc * self,
1020 VTCompressionSessionRef session, gboolean allow_frame_reordering)
1022 VTSessionSetProperty (session, kVTCompressionPropertyKey_AllowFrameReordering,
1023 allow_frame_reordering ? kCFBooleanTrue : kCFBooleanFalse);
1027 gst_vtenc_session_configure_realtime (GstVTEnc * self,
1028 VTCompressionSessionRef session, gboolean realtime)
1030 VTSessionSetProperty (session, kVTCompressionPropertyKey_RealTime,
1031 realtime ? kCFBooleanTrue : kCFBooleanFalse);
1035 gst_vtenc_session_configure_property_int (GstVTEnc * self,
1036 VTCompressionSessionRef session, CFStringRef name, gint value)
1040 gchar name_str[128];
1042 num = CFNumberCreate (NULL, kCFNumberIntType, &value);
1043 status = VTSessionSetProperty (session, name, num);
1046 CFStringGetCString (name, name_str, sizeof (name_str), kCFStringEncodingUTF8);
1047 GST_DEBUG_OBJECT (self, "%s(%d) => %d", name_str, value, (int) status);
1053 gst_vtenc_session_configure_property_double (GstVTEnc * self,
1054 VTCompressionSessionRef session, CFStringRef name, gdouble value)
1058 gchar name_str[128];
1060 num = CFNumberCreate (NULL, kCFNumberDoubleType, &value);
1061 status = VTSessionSetProperty (session, name, num);
1064 CFStringGetCString (name, name_str, sizeof (name_str), kCFStringEncodingUTF8);
1065 GST_DEBUG_OBJECT (self, "%s(%f) => %d", name_str, value, (int) status);
1071 gst_vtenc_update_latency (GstVTEnc * self)
1076 GstClockTime frame_duration;
1077 GstClockTime latency;
1079 if (self->video_info.fps_d == 0) {
1080 GST_INFO_OBJECT (self, "framerate not known, can't set latency");
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);
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
1098 frame_duration = gst_util_uint64_scale (GST_SECOND, 1, 25);
1100 frame_duration = gst_util_uint64_scale (GST_SECOND,
1101 self->video_info.fps_d, self->video_info.fps_n);
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);
1113 static GstFlowReturn
1114 gst_vtenc_encode_frame (GstVTEnc * self, GstVideoCodecFrame * frame)
1116 CMTime ts, duration;
1117 GstCoreMediaMeta *meta;
1118 CVPixelBufferRef pbuf = NULL;
1119 GstVideoCodecFrame *outframe;
1121 GstFlowReturn ret = GST_FLOW_OK;
1122 gboolean renegotiated;
1123 CFDictionaryRef frame_props = NULL;
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;
1130 ts = CMTimeMake (frame->pts, GST_SECOND);
1131 if (frame->duration != GST_CLOCK_TIME_NONE)
1132 duration = CMTimeMake (frame->duration, GST_SECOND);
1134 duration = kCMTimeInvalid;
1136 meta = gst_buffer_get_core_media_meta (frame->input_buffer);
1138 pbuf = gst_core_media_buffer_get_pixel_buffer (frame->input_buffer);
1142 GstVideoFrame inframe, outframe;
1144 OSType pixel_format_type;
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.
1152 switch (GST_VIDEO_INFO_FORMAT (&self->video_info)) {
1153 case GST_VIDEO_FORMAT_I420:
1154 pixel_format_type = kCVPixelFormatType_420YpCbCr8Planar;
1156 case GST_VIDEO_FORMAT_NV12:
1157 pixel_format_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
1163 if (!gst_video_frame_map (&inframe, &self->video_info, frame->input_buffer,
1168 CVPixelBufferCreate (NULL, self->negotiated_width,
1169 self->negotiated_height, pixel_format_type, NULL, &pbuf);
1171 if (cv_ret != kCVReturnSuccess) {
1172 gst_video_frame_unmap (&inframe);
1177 gst_core_video_buffer_new ((CVBufferRef) pbuf, &self->video_info, NULL);
1178 if (!gst_video_frame_map (&outframe, &self->video_info, outbuf,
1180 gst_video_frame_unmap (&inframe);
1181 gst_buffer_unref (outbuf);
1182 CVPixelBufferRelease (pbuf);
1186 if (!gst_video_frame_copy (&outframe, &inframe)) {
1187 gst_video_frame_unmap (&inframe);
1188 gst_buffer_unref (outbuf);
1189 CVPixelBufferRelease (pbuf);
1193 gst_buffer_unref (outbuf);
1194 gst_video_frame_unmap (&inframe);
1195 gst_video_frame_unmap (&outframe);
1199 GstVTEncFrame *vframe;
1202 vframe = gst_vtenc_frame_new (frame->input_buffer, &self->video_info);
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;
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);
1226 switch (GST_VIDEO_INFO_FORMAT (&self->video_info)) {
1227 case GST_VIDEO_FORMAT_I420:
1228 pixel_format_type = kCVPixelFormatType_420YpCbCr8Planar;
1230 case GST_VIDEO_FORMAT_NV12:
1231 pixel_format_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
1233 case GST_VIDEO_FORMAT_UYVY:
1234 pixel_format_type = kCVPixelFormatType_422YpCbCr8;
1237 gst_vtenc_frame_free (vframe);
1241 cv_ret = CVPixelBufferCreateWithPlanarBytes (NULL,
1242 self->negotiated_width, self->negotiated_height,
1245 GST_VIDEO_FRAME_SIZE (&vframe->videoframe),
1247 plane_base_addresses,
1250 plane_bytes_per_row, gst_pixel_buffer_release_cb, vframe, NULL,
1252 if (cv_ret != kCVReturnSuccess) {
1253 gst_vtenc_frame_free (vframe);
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);
1270 if (vt_status != noErr) {
1271 GST_WARNING_OBJECT (self, "VTCompressionSessionEncodeFrame returned %d",
1275 gst_video_codec_frame_unref (frame);
1277 CVPixelBufferRelease (pbuf);
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 */
1286 if (gst_vtenc_negotiate_downstream (self, meta->sample_buf)) {
1287 renegotiated = TRUE;
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 */
1297 gst_vtenc_update_latency (self);
1300 /* releases frame, even if it has no output buffer (i.e. failed to encode) */
1302 gst_video_encoder_finish_frame (GST_VIDEO_ENCODER_CAST (self),
1310 gst_video_codec_frame_unref (frame);
1311 return GST_FLOW_ERROR;
1316 gst_vtenc_enqueue_buffer (void *outputCallbackRefCon,
1317 void *sourceFrameRefCon,
1319 VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
1321 GstVTEnc *self = outputCallbackRefCon;
1322 gboolean is_keyframe;
1323 GstVideoCodecFrame *frame;
1326 gst_video_encoder_get_frame (GST_VIDEO_ENCODER_CAST (self),
1327 GPOINTER_TO_INT (sourceFrameRefCon));
1329 if (status != noErr) {
1331 GST_ELEMENT_ERROR (self, LIBRARY, ENCODE, (NULL),
1332 ("Failed to encode frame %d: %d", frame->system_frame_number,
1335 GST_ELEMENT_ERROR (self, LIBRARY, ENCODE, (NULL),
1336 ("Failed to encode (frame unknown): %d", (int) status));
1342 GST_WARNING_OBJECT (self, "No corresponding frame found!");
1346 /* This may happen if we don't have enough bitrate */
1347 if (sampleBuffer == NULL)
1350 is_keyframe = gst_vtenc_buffer_is_keyframe (self, sampleBuffer);
1353 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
1354 gst_vtenc_clear_cached_caps_downstream (self);
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);
1362 /* needed anyway so the frame will be released */
1364 g_async_queue_push (self->cur_outframes, frame);
1368 gst_vtenc_buffer_is_keyframe (GstVTEnc * self, CMSampleBufferRef sbuf)
1370 gboolean result = FALSE;
1371 CFArrayRef attachments_for_sample;
1373 attachments_for_sample = CMSampleBufferGetSampleAttachmentsArray (sbuf, 0);
1374 if (attachments_for_sample != NULL) {
1375 CFDictionaryRef attachments;
1376 CFBooleanRef depends_on_others;
1378 attachments = CFArrayGetValueAtIndex (attachments_for_sample, 0);
1379 depends_on_others = CFDictionaryGetValue (attachments,
1380 kCMSampleAttachmentKey_DependsOnOthers);
1381 result = (depends_on_others == kCFBooleanFalse);
1388 static GstVTEncFrame *
1389 gst_vtenc_frame_new (GstBuffer * buf, GstVideoInfo * video_info)
1391 GstVTEncFrame *frame;
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);
1405 gst_vtenc_frame_free (GstVTEncFrame * frame)
1407 gst_video_frame_unmap (&frame->videoframe);
1408 gst_buffer_unref (frame->buf);
1409 g_slice_free (GstVTEncFrame, frame);
1413 gst_pixel_buffer_release_cb (void *releaseRefCon, const void *dataPtr,
1414 size_t dataSize, size_t numberOfPlanes, const void *planeAddresses[])
1416 GstVTEncFrame *frame = (GstVTEncFrame *) releaseRefCon;
1417 gst_vtenc_frame_free (frame);
1422 gst_vtenc_register (GstPlugin * plugin,
1423 const GstVTEncoderDetails * codec_details)
1425 GTypeInfo type_info = {
1426 sizeof (GstVTEncClass),
1427 (GBaseInitFunc) gst_vtenc_base_init,
1429 (GClassInitFunc) gst_vtenc_class_init,
1434 (GInstanceInitFunc) gst_vtenc_init,
1440 type_name = g_strdup_printf ("vtenc_%s", codec_details->element_name);
1443 g_type_register_static (GST_TYPE_VIDEO_ENCODER, type_name, &type_info, 0);
1445 g_type_set_qdata (type, GST_VTENC_CODEC_DETAILS_QDATA,
1446 (gpointer) codec_details);
1448 result = gst_element_register (plugin, type_name, GST_RANK_PRIMARY, type);
1450 GST_ERROR_OBJECT (plugin, "failed to register element %s", type_name);
1456 static const GstVTEncoderDetails gst_vtenc_codecs[] = {
1457 {"H.264", "h264", "video/x-h264", kCMVideoCodecType_H264, FALSE},
1459 {"H.264 (HW only)", "h264_hw", "video/x-h264", kCMVideoCodecType_H264, TRUE},
1464 gst_vtenc_register_elements (GstPlugin * plugin)
1468 GST_DEBUG_CATEGORY_INIT (gst_vtenc_debug, "vtenc",
1469 0, "Apple VideoToolbox Encoder Wrapper");
1471 for (i = 0; i != G_N_ELEMENTS (gst_vtenc_codecs); i++)
1472 gst_vtenc_register (plugin, &gst_vtenc_codecs[i]);