2 * gstvaapiencode_h264.c - VA-API H.264 encoder
4 * Copyright (C) 2012-2014 Intel Corporation
5 * Author: Wind Yuan <feng.yuan@intel.com>
6 * Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1
11 * of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA
25 * SECTION:element-vaapih264enc
26 * @short_description: A VA-API based H.264 video encoder
28 * Encodes raw video streams into H.264 bitstreams.
31 * <title>Example launch line</title>
33 * gst-launch-1.0 -ev videotestsrc num-buffers=60 ! timeoverlay ! vaapih264enc ! h264parse ! mp4mux ! filesink location=test.mp4
38 * <title>Region of Interest</title>
39 * Since libva supports Region Of Interest for avc encoding depending on H/W,
40 * GStreamer VA-API supports it by #GstEvent.
41 * To enable ROI, an application must send an event of type GST_EVENT_CUSTOM_DOWNSTREAM,
42 * having a structure of name "GstVaapiEncoderRegionOfInterest" with fields set
43 * according to the following table:
47 * <colspec colname='Name' />
48 * <colspec colname='Type' />
49 * <colspec colname='Purpose' />
53 * <entry>GType</entry>
54 * <entry>Description</entry>
59 * <entry>roi-value</entry>
60 * <entry>G_TYPE_INT</entry>
61 * <entry>specifies ROI delta QP or ROI priority.
62 * ROI delta QP is the value that will be added on top of the frame level QP.
63 * ROI priority specifies the priority of a region, it can be positive (more important)
64 * or negative (less important) values and is compared with non-ROI region (taken as value 0),
69 * <entry>roi-x</entry>
70 * <entry>G_TYPE_UINT</entry>
74 * <entry>roi-y</entry>
75 * <entry>G_TYPE_UINT</entry>
79 * <entry>roi-width</entry>
80 * <entry>G_TYPE_UINT</entry>
81 * <entry>width</entry>
84 * <entry>roi-height</entry>
85 * <entry>G_TYPE_UINT</entry>
86 * <entry>height</entry>
92 * For example, the following code informs the encoder to enable ROI
93 * with a region for ROI.
94 * Note that if an application wants to disable the region,
95 * just send an event with roi-value=0 and same coordination.
98 * GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
99 * gst_structure_new ("GstVaapiEncoderRegionOfInterest",
100 * "roi-x", G_TYPE_UINT, 1820,
101 * "roi-y", G_TYPE_UINT, 980,
102 * "roi-width", G_TYPE_UINT, 100,
103 * "roi-height", G_TYPE_UINT, 100,
104 * "roi-value", G_TYPE_INT, 4, NULL));
106 * gst_element_send_event (pipeline, event);
112 #include "gstcompat.h"
113 #include <gst/vaapi/gstvaapidisplay.h>
114 #include <gst/vaapi/gstvaapiencoder_h264.h>
115 #include <gst/vaapi/gstvaapiutils_h264.h>
116 #include "gstvaapiencode_h264.h"
117 #include "gstvaapipluginutil.h"
118 #include "gstvaapivideomemory.h"
120 #define GST_PLUGIN_NAME "vaapih264enc"
121 #define GST_PLUGIN_DESC "A VA-API based H264 video encoder"
123 GST_DEBUG_CATEGORY_STATIC (gst_vaapi_h264_encode_debug);
124 #define GST_CAT_DEFAULT gst_vaapi_h264_encode_debug
126 #define GST_CODEC_CAPS \
128 "stream-format = (string) { avc, byte-stream }, " \
129 "alignment = (string) au"
132 static const char gst_vaapiencode_h264_sink_caps_str[] =
133 GST_VAAPI_MAKE_SURFACE_CAPS ", "
134 GST_CAPS_INTERLACED_FALSE "; "
135 GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) ", "
136 GST_CAPS_INTERLACED_FALSE;
140 static const char gst_vaapiencode_h264_src_caps_str[] =
142 "profile = (string) { constrained-baseline, baseline, main, high, multiview-high, stereo-high }";
146 static GstStaticPadTemplate gst_vaapiencode_h264_sink_factory =
147 GST_STATIC_PAD_TEMPLATE ("sink",
150 GST_STATIC_CAPS (gst_vaapiencode_h264_sink_caps_str));
154 static GstStaticPadTemplate gst_vaapiencode_h264_src_factory =
155 GST_STATIC_PAD_TEMPLATE ("src",
158 GST_STATIC_CAPS (gst_vaapiencode_h264_src_caps_str));
162 G_DEFINE_TYPE (GstVaapiEncodeH264, gst_vaapiencode_h264, GST_TYPE_VAAPIENCODE);
165 gst_vaapiencode_h264_init (GstVaapiEncodeH264 * encode)
167 gst_vaapiencode_init_properties (GST_VAAPIENCODE_CAST (encode));
171 gst_vaapiencode_h264_finalize (GObject * object)
173 GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (object);
175 gst_caps_replace (&encode->available_caps, NULL);
176 G_OBJECT_CLASS (gst_vaapiencode_h264_parent_class)->finalize (object);
180 gst_vaapiencode_h264_set_property (GObject * object,
181 guint prop_id, const GValue * value, GParamSpec * pspec)
183 GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_GET_CLASS (object);
184 GstVaapiEncode *const base_encode = GST_VAAPIENCODE_CAST (object);
188 if (!encode_class->set_property (base_encode, prop_id, value))
189 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195 gst_vaapiencode_h264_get_property (GObject * object,
196 guint prop_id, GValue * value, GParamSpec * pspec)
198 GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_GET_CLASS (object);
199 GstVaapiEncode *const base_encode = GST_VAAPIENCODE_CAST (object);
203 if (!encode_class->get_property (base_encode, prop_id, value))
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209 static GstVaapiProfile
210 gst_vaapiencode_h264_get_profile (GstCaps * caps)
214 for (i = 0; i < gst_caps_get_size (caps); i++) {
215 GstStructure *const structure = gst_caps_get_structure (caps, i);
216 const GValue *const value = gst_structure_get_value (structure, "profile");
218 if (value && G_VALUE_HOLDS_STRING (value)) {
219 const gchar *str = g_value_get_string (value);
221 return gst_vaapi_utils_h264_get_profile_from_string (str);
225 return GST_VAAPI_PROFILE_UNKNOWN;
230 GstVaapiProfile best_profile;
232 } FindBestProfileData;
235 find_best_profile_value (FindBestProfileData * data, const GValue * value)
238 GstVaapiProfile profile;
241 if (!value || !G_VALUE_HOLDS_STRING (value))
244 str = g_value_get_string (value);
247 profile = gst_vaapi_utils_h264_get_profile_from_string (str);
250 score = gst_vaapi_utils_h264_get_profile_score (profile);
251 if (score < data->best_score)
253 data->best_profile = profile;
254 data->best_score = score;
257 static GstVaapiProfile
258 find_best_profile (GstCaps * caps)
260 FindBestProfileData data;
261 guint i, j, num_structures, num_values;
263 data.best_profile = GST_VAAPI_PROFILE_UNKNOWN;
266 num_structures = gst_caps_get_size (caps);
267 for (i = 0; i < num_structures; i++) {
268 GstStructure *const structure = gst_caps_get_structure (caps, i);
269 const GValue *const value = gst_structure_get_value (structure, "profile");
273 if (G_VALUE_HOLDS_STRING (value))
274 find_best_profile_value (&data, value);
275 else if (GST_VALUE_HOLDS_LIST (value)) {
276 num_values = gst_value_list_get_size (value);
277 for (j = 0; j < num_values; j++)
278 find_best_profile_value (&data, gst_value_list_get_value (value, j));
281 return data.best_profile;
285 get_available_caps (GstVaapiEncodeH264 * encode)
289 GstVaapiProfile profile;
290 const gchar *profile_str;
291 GValue profile_v = G_VALUE_INIT;
292 GValue profile_list = G_VALUE_INIT;
295 if (encode->available_caps)
296 return encode->available_caps;
298 g_value_init (&profile_list, GST_TYPE_LIST);
299 g_value_init (&profile_v, G_TYPE_STRING);
302 gst_vaapi_display_get_encode_profiles
303 (GST_VAAPI_PLUGIN_BASE_DISPLAY (encode));
307 for (i = 0; i < profiles->len; i++) {
308 profile = g_array_index (profiles, GstVaapiProfile, i);
309 if (gst_vaapi_profile_get_codec (profile) != GST_VAAPI_CODEC_H264)
311 profile_str = gst_vaapi_profile_get_name (profile);
314 g_value_set_string (&profile_v, profile_str);
315 gst_value_list_append_value (&profile_list, &profile_v);
317 g_array_unref (profiles);
319 out_caps = gst_caps_from_string (GST_CODEC_CAPS);
320 gst_caps_set_value (out_caps, "profile", &profile_list);
321 g_value_unset (&profile_list);
322 g_value_unset (&profile_v);
324 encode->available_caps = out_caps;
326 return encode->available_caps;
330 gst_vaapiencode_h264_set_config (GstVaapiEncode * base_encode)
332 GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (base_encode);
333 GstVaapiEncoderH264 *const encoder =
334 GST_VAAPI_ENCODER_H264 (base_encode->encoder);
335 GstCaps *template_caps, *allowed_caps;
339 gst_static_pad_template_get_caps (&gst_vaapiencode_h264_src_factory);
341 gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
343 if (allowed_caps == template_caps) {
344 GST_INFO_OBJECT (encode, "downstream has ANY caps, outputting byte-stream");
345 encode->is_avc = FALSE;
346 gst_caps_unref (allowed_caps);
347 } else if (!allowed_caps) {
348 GST_INFO_OBJECT (encode,
349 "downstream has NULL caps, outputting byte-stream");
350 encode->is_avc = FALSE;
351 } else if (gst_caps_is_empty (allowed_caps)) {
352 GST_INFO_OBJECT (encode, "downstream has EMPTY caps");
353 gst_caps_unref (template_caps);
354 gst_caps_unref (allowed_caps);
357 const char *stream_format = NULL;
358 GstStructure *structure;
359 guint i, num_structures;
360 GstVaapiProfile profile;
361 GstCaps *available_caps;
363 available_caps = get_available_caps (encode);
364 if (!available_caps) {
365 gst_caps_unref (template_caps);
366 gst_caps_unref (allowed_caps);
369 if (!gst_caps_can_intersect (allowed_caps, available_caps)) {
370 GST_INFO_OBJECT (encode, "downstream requested an unsupported profile, "
371 "but encoder will output a compatible one");
374 /* Check whether "stream-format" is avcC mode */
375 num_structures = gst_caps_get_size (allowed_caps);
376 for (i = 0; !stream_format && i < num_structures; i++) {
377 structure = gst_caps_get_structure (allowed_caps, i);
378 if (!gst_structure_has_field_typed (structure, "stream-format",
381 stream_format = gst_structure_get_string (structure, "stream-format");
383 encode->is_avc = stream_format && strcmp (stream_format, "avc") == 0;
385 /* Check for the largest profile that is supported */
386 profile = find_best_profile (allowed_caps);
387 if (profile != GST_VAAPI_PROFILE_UNKNOWN) {
388 GST_INFO ("using %s profile as target decoder constraints",
389 gst_vaapi_utils_h264_get_profile_string (profile));
390 ret = gst_vaapi_encoder_h264_set_max_profile (encoder, profile);
393 gst_caps_unref (allowed_caps);
395 gst_caps_unref (template_caps);
397 base_encode->need_codec_data = encode->is_avc;
403 set_compatible_profile (GstVaapiEncodeH264 * encode, GstCaps * caps,
404 GstVaapiProfile profile)
406 GstCaps *allowed_caps, *tmp_caps;
407 gboolean ret = FALSE;
410 gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode));
411 if (!allowed_caps || gst_caps_is_empty (allowed_caps)) {
413 gst_caps_unref (allowed_caps);
417 tmp_caps = gst_caps_from_string (GST_CODEC_CAPS);
419 /* If profile doesn't exist in the allowed caps, let's find
420 * compatible profile in the caps.
422 * If there is one, we can set it as a compatible profile and make
423 * the negotiation. We consider baseline compatible with
424 * constrained-baseline, which is a strict subset of baseline
428 gst_caps_set_simple (tmp_caps, "profile", G_TYPE_STRING,
429 gst_vaapi_utils_h264_get_profile_string (profile), NULL);
431 if (!gst_caps_can_intersect (allowed_caps, tmp_caps)) {
432 if (profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE) {
433 profile = GST_VAAPI_PROFILE_H264_BASELINE;
437 gst_caps_set_simple (caps, "profile", G_TYPE_STRING,
438 gst_vaapi_utils_h264_get_profile_string (profile), NULL);
443 GST_LOG ("There is no compatible profile in the requested caps.");
445 gst_caps_unref (tmp_caps);
446 gst_caps_unref (allowed_caps);
451 gst_vaapiencode_h264_get_caps (GstVaapiEncode * base_encode)
453 GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (base_encode);
454 GstVaapiEncoderH264 *const encoder =
455 GST_VAAPI_ENCODER_H264 (base_encode->encoder);
456 GstVaapiProfile profile;
459 caps = gst_caps_from_string (GST_CODEC_CAPS);
461 gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING,
462 encode->is_avc ? "avc" : "byte-stream", NULL);
464 /* Update profile determined by encoder */
465 gst_vaapi_encoder_h264_get_profile_and_level (encoder, &profile, NULL);
466 if (profile != GST_VAAPI_PROFILE_UNKNOWN)
467 set_compatible_profile (encode, caps, profile);
469 /* XXX: update level information */
473 static GstVaapiEncoder *
474 gst_vaapiencode_h264_alloc_encoder (GstVaapiEncode * base,
475 GstVaapiDisplay * display)
477 return gst_vaapi_encoder_h264_new (display);
480 /* h264 NAL byte stream operations */
482 _h264_byte_stream_next_nal (guint8 * buffer, guint32 len, guint32 * nal_size)
484 const guint8 *cur = buffer;
485 const guint8 *const end = buffer + len;
486 guint8 *nal_start = NULL;
487 guint32 flag = 0xFFFFFFFF;
488 guint32 nal_start_len = 0;
490 g_assert (len >= 0 && buffer && nal_size);
493 nal_start = (len ? buffer : NULL);
497 /*locate head postion */
498 if (!buffer[0] && !buffer[1]) {
499 if (buffer[2] == 1) { /* 0x000001 */
501 } else if (!buffer[2] && len >= 4 && buffer[3] == 1) { /* 0x00000001 */
505 nal_start = buffer + nal_start_len;
508 /*find next nal start position */
510 flag = ((flag << 8) | ((*cur++) & 0xFF));
511 if ((flag & 0x00FFFFFF) == 0x00000001) {
512 if (flag == 0x00000001)
513 *nal_size = cur - 4 - nal_start;
515 *nal_size = cur - 3 - nal_start;
520 *nal_size = end - nal_start;
521 if (nal_start >= end) {
529 _start_code_to_size (guint8 nal_start_code[4], guint32 nal_size)
531 nal_start_code[0] = ((nal_size >> 24) & 0xFF);
532 nal_start_code[1] = ((nal_size >> 16) & 0xFF);
533 nal_start_code[2] = ((nal_size >> 8) & 0xFF);
534 nal_start_code[3] = (nal_size & 0xFF);
538 _h264_convert_byte_stream_to_avc (GstBuffer * buf)
542 guint8 *nal_start_code, *nal_body;
547 if (!gst_buffer_map (buf, &info, GST_MAP_READ | GST_MAP_WRITE))
550 nal_start_code = info.data;
551 frame_end = info.data + info.size;
554 while ((frame_end > nal_start_code) &&
555 (nal_body = _h264_byte_stream_next_nal (nal_start_code,
556 frame_end - nal_start_code, &nal_size)) != NULL) {
560 g_assert (nal_body - nal_start_code == 4);
561 _start_code_to_size (nal_start_code, nal_size);
562 nal_start_code = nal_body + nal_size;
564 gst_buffer_unmap (buf, &info);
570 gst_buffer_unmap (buf, &info);
576 gst_vaapiencode_h264_alloc_buffer (GstVaapiEncode * base_encode,
577 GstVaapiCodedBuffer * coded_buf, GstBuffer ** out_buffer_ptr)
579 GstVaapiEncodeH264 *const encode = GST_VAAPIENCODE_H264_CAST (base_encode);
580 GstVaapiEncoderH264 *const encoder =
581 GST_VAAPI_ENCODER_H264 (base_encode->encoder);
584 g_return_val_if_fail (encoder != NULL, GST_FLOW_ERROR);
587 GST_VAAPIENCODE_CLASS (gst_vaapiencode_h264_parent_class)->alloc_buffer
588 (base_encode, coded_buf, out_buffer_ptr);
589 if (ret != GST_FLOW_OK)
595 /* Convert to avcC format */
596 if (!_h264_convert_byte_stream_to_avc (*out_buffer_ptr))
597 goto error_convert_buffer;
601 error_convert_buffer:
603 GST_ERROR ("failed to convert from bytestream format to avcC format");
604 gst_buffer_replace (out_buffer_ptr, NULL);
605 return GST_FLOW_ERROR;
610 gst_vaapiencode_h264_class_init (GstVaapiEncodeH264Class * klass)
612 GObjectClass *const object_class = G_OBJECT_CLASS (klass);
613 GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
614 GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_CLASS (klass);
616 GST_DEBUG_CATEGORY_INIT (gst_vaapi_h264_encode_debug,
617 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
619 object_class->finalize = gst_vaapiencode_h264_finalize;
620 object_class->set_property = gst_vaapiencode_h264_set_property;
621 object_class->get_property = gst_vaapiencode_h264_get_property;
623 encode_class->get_properties = gst_vaapi_encoder_h264_get_default_properties;
624 encode_class->get_profile = gst_vaapiencode_h264_get_profile;
625 encode_class->set_config = gst_vaapiencode_h264_set_config;
626 encode_class->get_caps = gst_vaapiencode_h264_get_caps;
627 encode_class->alloc_encoder = gst_vaapiencode_h264_alloc_encoder;
628 encode_class->alloc_buffer = gst_vaapiencode_h264_alloc_buffer;
630 gst_element_class_set_static_metadata (element_class,
631 "VA-API H264 encoder",
632 "Codec/Encoder/Video",
633 GST_PLUGIN_DESC, "Wind Yuan <feng.yuan@intel.com>");
636 gst_element_class_add_static_pad_template (element_class,
637 &gst_vaapiencode_h264_sink_factory);
640 gst_element_class_add_static_pad_template (element_class,
641 &gst_vaapiencode_h264_src_factory);
643 gst_vaapiencode_class_init_properties (encode_class);