1 #include "gsth264encode.h"
6 #include "gst/vaapi/gstvaapivideobuffer.h"
9 #define PACKAGE "libgsth264encode"
10 #define VERSION "0.1.0"
12 #define GST_H264_ENCODE_CHECK_STATUS(exp, err_num, err_reason, ...) \
16 H264_LOG_ERROR(err_reason, ## __VA_ARGS__); \
21 #define GST_H264ENCODE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_H264ENCODE, GstH264EncodePrivate))
23 typedef struct _GstH264EncodePrivate GstH264EncodePrivate;
25 static const GstElementDetails gst_h264encode_details =
27 "VA-API h264 encoder",
28 "Codec/Encoder/Video",
29 "A VA-API based h264 encoder",
30 "Yuan Feng <feng.yuan@intel.com>");
32 /* Default templates */
33 #define GST_CAPS_CODEC(CODEC) \
35 "width = (int) [ 1, MAX ], " \
36 "height = (int) [ 1, MAX ]; "
38 static const char gst_h264encode_sink_caps_str[] =
39 GST_CAPS_CODEC("video/x-raw-yuv, " "format = (fourcc) { I420 } ")
40 GST_CAPS_CODEC("video/x-raw-yuv, " "format = (fourcc) { NV12 } ")
41 GST_CAPS_CODEC("video/x-vaapi-surface ")
44 static const char gst_h264encode_src_caps_str[] =
45 GST_CAPS_CODEC("video/x-h264");
47 static GstStaticPadTemplate gst_h264encode_sink_factory =
48 GST_STATIC_PAD_TEMPLATE(
52 GST_STATIC_CAPS(gst_h264encode_sink_caps_str));
54 static GstStaticPadTemplate gst_h264encode_src_factory =
55 GST_STATIC_PAD_TEMPLATE(
59 GST_STATIC_CAPS(gst_h264encode_src_caps_str));
80 static void gst_h264encode_finalize(GObject *object);
81 static void gst_h264encode_set_property(GObject *object, guint prop_id,
82 const GValue *value, GParamSpec *pspec);
83 static void gst_h264encode_get_property (GObject * object, guint prop_id,
84 GValue * value, GParamSpec * pspec);
86 static gboolean gst_h264encode_set_caps(GstPad *sink_pad, GstCaps *caps);
87 static GstCaps *gst_h264encode_get_caps(GstPad *sink_pad);
88 static GstFlowReturn gst_h264encode_chain(GstPad *sink_pad, GstBuffer *buf);
89 static GstStateChangeReturn gst_h264encode_change_state(GstElement *element, GstStateChange transition);
90 static GstFlowReturn gst_h264encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
91 GstCaps * caps, GstBuffer ** buf);
93 static char* _h264_dump_caps(GstCaps *cpas);
94 static gboolean _h264_check_valid_profile(guint profile);
95 static gboolean _h264_check_valid_level(guint level);
101 gst_h264encode_base_init(gpointer klass)
103 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
105 gst_element_class_set_details(element_class, &gst_h264encode_details);
108 gst_element_class_add_pad_template(
110 gst_static_pad_template_get(&gst_h264encode_sink_factory)
114 gst_element_class_add_pad_template(
116 gst_static_pad_template_get(&gst_h264encode_src_factory)
122 gst_h264encode_class_init(GstH264EncodeClass *klass)
124 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
125 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
127 object_class->finalize = gst_h264encode_finalize;
128 object_class->set_property = gst_h264encode_set_property;
129 object_class->get_property = gst_h264encode_get_property;
131 g_object_class_install_property (object_class, PROP_PROFILE,
132 g_param_spec_uint ("profile",
134 "Profile supports: 66(Baseline), 77(Main), 100(High)",
135 H264_PROFILE_BASELINE,
137 H264_DEFAULT_PROFILE,
139 g_object_class_install_property (object_class, PROP_LEVEL,
140 g_param_spec_uint ("level",
142 "Level idc supports: 10, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41",
147 g_object_class_install_property (object_class, PROP_BITRATE,
148 g_param_spec_uint ("bitrate",
149 "H264 encoding bitrate",
150 "H264 encoding bitrate, 10k~100M, (0, auto-calculate)",
155 g_object_class_install_property (object_class, PROP_INTRA_PERIOD,
156 g_param_spec_uint ("intra-period",
157 "H264 encoding intra-period",
158 "H264 encoding intra-period",
161 H264_DEFAULT_INTRA_PERIOD,
163 g_object_class_install_property (object_class, PROP_INIT_QP,
164 g_param_spec_uint ("init-qp",
169 H264_DEFAULT_INIT_QP,
171 g_object_class_install_property (object_class, PROP_MIN_QP,
172 g_param_spec_uint ("min-qp",
179 g_object_class_install_property (object_class, PROP_SLICE_NUM,
180 g_param_spec_uint ("slice-num",
188 element_class->change_state = gst_h264encode_change_state;
192 gst_h264encode_finalize(GObject *object)
194 GstH264Encode * const encode = GST_H264ENCODE(object);
196 if (encode->sinkpad_caps) {
197 gst_caps_unref(encode->sinkpad_caps);
198 encode->sinkpad_caps = NULL;
200 encode->sinkpad = NULL;
202 if (encode->srcpad_caps) {
203 gst_caps_unref(encode->srcpad_caps);
204 encode->srcpad_caps = NULL;
206 encode->srcpad = NULL;
208 if (encode->encoder) {
209 gst_h264_encoder_close(encode->encoder);
210 gst_h264_encoder_uninitialize(encode->encoder);
211 gst_h264_encoder_unref(encode->encoder);
212 encode->encoder = NULL;
215 G_OBJECT_CLASS(parent_class)->finalize(object);
219 gst_h264encode_init(GstH264Encode *encode, GstH264EncodeClass *klass)
221 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
223 encode->sinkpad_caps = NULL;
224 encode->srcpad_caps = NULL;
225 encode->first_sink_frame = TRUE;
226 encode->first_src_frame = TRUE;
228 encode->encoder = gst_h264_encoder_new();
229 H264_ASSERT(encode->encoder);
232 encode->sinkpad = gst_pad_new_from_template(
233 gst_element_class_get_pad_template(element_class, "sink"),
236 gst_pad_set_getcaps_function(encode->sinkpad, gst_h264encode_get_caps);
237 gst_pad_set_setcaps_function(encode->sinkpad, gst_h264encode_set_caps);
238 gst_pad_set_chain_function(encode->sinkpad, gst_h264encode_chain);
239 gst_pad_set_bufferalloc_function(encode->sinkpad, gst_h264encode_buffer_alloc);
240 /*gst_pad_set_event_function(encode->sinkpad, gst_h264encode_sink_event); */
241 /*gst_pad_use_fixed_caps(encode->sinkpad);*/
242 gst_element_add_pad(GST_ELEMENT(encode), encode->sinkpad);
245 encode->srcpad = gst_pad_new_from_template(
246 gst_element_class_get_pad_template(element_class, "src"),
249 encode->srcpad_caps = NULL;
251 gst_pad_use_fixed_caps(encode->srcpad);
252 /*gst_pad_set_event_function(encode->srcpad, gst_h264encode_src_event);*/
253 gst_element_add_pad(GST_ELEMENT(encode), encode->srcpad);
258 gst_h264encode_set_property(GObject *object, guint prop_id,
259 const GValue *value, GParamSpec *pspec)
261 GstH264Encode *encode = GST_H264ENCODE(object);
262 H264_ASSERT(encode->encoder);
266 guint profile = g_value_get_uint(value);
267 if (_h264_check_valid_profile(profile)) {
268 encode->encoder->profile = profile;
270 H264_LOG_ERROR("h264encode set property <profile> failed.\n");
276 guint level = g_value_get_uint(value);
277 if (_h264_check_valid_level(level)) {
278 encode->encoder->level= level;
280 H264_LOG_ERROR("h264encode set property <level> failed.\n");
286 encode->encoder->bitrate = g_value_get_uint(value);
290 case PROP_INTRA_PERIOD: {
291 encode->encoder->intra_period = g_value_get_uint(value);
296 encode->encoder->init_qp = g_value_get_uint(value);
301 encode->encoder->min_qp = g_value_get_uint(value);
305 case PROP_SLICE_NUM: {
306 encode->encoder->slice_num= g_value_get_uint(value);
311 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
317 gst_h264encode_get_property (GObject * object, guint prop_id,
318 GValue * value, GParamSpec * pspec)
320 GstH264Encode *encode = GST_H264ENCODE(object);
321 H264_ASSERT(encode->encoder);
325 g_value_set_uint (value, encode->encoder->profile);
329 g_value_set_uint (value, encode->encoder->level);
333 g_value_set_uint (value, encode->encoder->bitrate);
336 case PROP_INTRA_PERIOD:
337 g_value_set_uint (value, encode->encoder->intra_period);
341 g_value_set_uint (value, encode->encoder->init_qp);
345 g_value_set_uint (value, encode->encoder->min_qp);
349 g_value_set_uint (value, encode->encoder->slice_num);
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
359 gst_h264encode_set_caps(GstPad *sink_pad, GstCaps *caps)
361 GstH264Encode *encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
362 GstStructure *structure;
363 gint width = 0, height = 0;
364 gint fps_n = 0, fps_d = 0;
365 const GValue *fps_value = NULL;
366 encode->sinkpad_caps = caps;
368 H264_LOG_INFO("gst_h264encode_set_caps,\n%s", _h264_dump_caps(caps));
370 structure = gst_caps_get_structure (caps, 0);
371 if (gst_structure_get_int (structure, "width", &width)) {
372 encode->encoder->width = width;
374 if (gst_structure_get_int (structure, "height", &height)) {
375 encode->encoder->height = height;
377 fps_value = gst_structure_get_value (structure, "framerate");
379 fps_n = gst_value_get_fraction_numerator (fps_value);
380 fps_d = gst_value_get_fraction_denominator (fps_value);
381 encode->encoder->frame_rate = fps_n/fps_d;
387 gst_h264encode_get_caps(GstPad *sink_pad)
389 GstCaps *caps = NULL;
390 GstH264Encode * const encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
391 if (encode->sinkpad_caps) {
392 gst_caps_ref(encode->sinkpad_caps);
393 H264_LOG_INFO("get caps,\n%s", _h264_dump_caps(encode->sinkpad_caps));
394 return encode->sinkpad_caps;
396 caps = gst_caps_copy(gst_pad_get_pad_template_caps(sink_pad));
400 static GstStateChangeReturn
401 gst_h264encode_change_state(GstElement *element, GstStateChange transition)
403 GstH264Encode * const encode = GST_H264ENCODE(element);
404 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
406 switch (transition) {
407 case GST_STATE_CHANGE_READY_TO_PAUSED:
409 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
415 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
416 if (ret != GST_STATE_CHANGE_SUCCESS)
419 switch (transition) {
420 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
422 case GST_STATE_CHANGE_PAUSED_TO_READY: {
423 gst_h264_encoder_close(encode->encoder);
429 return GST_STATE_CHANGE_SUCCESS;
434 gst_h264encode_chain(GstPad *sink_pad, GstBuffer *buf)
436 GstFlowReturn ret_num = GST_FLOW_OK;
437 GstH264Encode *encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
438 H264Status h264ret = H264_NO_ERROR;
439 GList *out_buffers = NULL;
440 GstBuffer *tmp_buffer = NULL;
442 static guint input_count = 0;
443 static guint output_count = 0;
445 H264_ASSERT(encode && encode->encoder);
446 if (encode->first_sink_frame) {
447 /* get first buffer caps and set encoder values */
448 GstStructure *recv_struct, *src_struct;
449 GstCaps *recv_caps = GST_BUFFER_CAPS(buf);
451 GValue const *framerate, *format_value;
454 GstVaapiSurfacePool *surface_pool = NULL;
456 H264_LOG_INFO("gst_h264encode_chain 1st recv-buffer caps,\n%s", _h264_dump_caps(recv_caps));
458 recv_struct = gst_caps_get_structure (recv_caps, 0);
459 GST_H264_ENCODE_CHECK_STATUS(NULL != recv_caps, GST_FLOW_ERROR, "gst_h264encode_chain, 1st buffer didn't have detailed caps.\n");
460 if (gst_structure_get_int (recv_struct, "width", &width)) {
461 encode->encoder->width = width;
463 if (gst_structure_get_int (recv_struct, "height", &height)) {
464 encode->encoder->height = height;
466 framerate = gst_structure_get_value (recv_struct, "framerate");
468 fps_n = gst_value_get_fraction_numerator (framerate);
469 fps_d = gst_value_get_fraction_denominator (framerate);
470 encode->encoder->frame_rate = fps_n/fps_d;
472 format_value = gst_structure_get_value (recv_struct, "format");
474 GST_H264_ENCODE_CHECK_STATUS(format_value && GST_TYPE_FOURCC == G_VALUE_TYPE(format_value),
475 GST_FLOW_ERROR, "1st buffer caps' format type is not fourcc.\n");
476 format = gst_value_get_fourcc (format_value);
478 gst_h264_encoder_set_input_format(encode->encoder, format);
483 if (encode->srcpad_caps) {
484 gst_caps_unref(encode->srcpad_caps);
486 encode->srcpad_caps = gst_caps_copy(gst_pad_get_pad_template_caps(encode->srcpad));
487 src_struct = gst_caps_get_structure(encode->srcpad_caps, 0);
488 gst_structure_set(src_struct, "width", G_TYPE_INT, width,
489 "height", G_TYPE_INT, height,
490 "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
492 /*set display and initialize encoder*/
493 if (GST_VAAPI_IS_VIDEO_BUFFER(buf)) {
494 GstVaapiDisplay *display = NULL;
495 GstVaapiVideoBuffer *video_buffer = GST_VAAPI_VIDEO_BUFFER(buf);
496 H264_ASSERT(video_buffer);
497 display = gst_vaapi_video_buffer_get_display(video_buffer);
498 //need to get surface_pool and set to h264encoder->vaapi_context
499 //(video_buffer->priv->surface_pool);
501 surface_pool = GST_VAAPI_SURFACE_POOL(gst_vaapi_video_buffer_get_surface_pool(video_buffer));
504 GST_H264_ENCODE_CHECK_STATUS(gst_h264_encoder_set_display(encode->encoder,display)
505 , GST_FLOW_ERROR, "set display failed in gst_h264encode_chain.\n");
508 h264ret = gst_h264_encoder_initialize(encode->encoder);
509 GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "h264_encoder_initialize failed.\n");
511 h264ret = gst_h264_encoder_open(encode->encoder, surface_pool);
513 h264ret = gst_h264_encoder_open(encode->encoder);
515 GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "gst_h264_encoder_open failed.\n");
517 encode->first_sink_frame = FALSE;
521 H264_ASSERT(gst_h264_encoder_get_state(encode->encoder) >= H264_ENC_OPENED);
523 H264_LOG_INFO("input %d\n", input_count);
524 h264ret = gst_h264_encoder_encode(encode->encoder, buf, &out_buffers);
525 GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "h264_encoder_encode failed.\n");
528 while (out_buffers) {
529 tmp_buffer = out_buffers->data;
530 out_buffers = g_list_remove(out_buffers, tmp_buffer);
531 /*out_buffers = g_list_next(out_buffers);*/
532 if (encode->first_src_frame) {
533 GstBuffer *codec_data;
534 H264_ASSERT(encode->srcpad_caps);
535 /*replace codec data in src pad caps*/
536 if (H264_NO_ERROR == gst_h264_encoder_get_avcC_codec_data(encode->encoder, &codec_data)) {
537 gst_caps_set_simple(encode->srcpad_caps, "codec_data",GST_TYPE_BUFFER, codec_data, NULL);
539 gst_pad_set_caps (encode->srcpad, encode->srcpad_caps);
540 GST_BUFFER_CAPS(tmp_buffer) = gst_caps_ref(encode->srcpad_caps);
541 H264_LOG_INFO("gst_h264encode_chain 1st push-buffer caps,\n%s", _h264_dump_caps(encode->srcpad_caps));
542 encode->first_src_frame = FALSE;
545 H264_LOG_INFO("output:%d, %" GST_TIME_FORMAT ", 0x%s\n",
547 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(tmp_buffer)),
548 h264_dump_bytes(GST_BUFFER_DATA(tmp_buffer),
549 (GST_BUFFER_SIZE(tmp_buffer) > 16? 16: GST_BUFFER_SIZE(tmp_buffer))));
550 gst_pad_push(encode->srcpad, tmp_buffer);
554 gst_mini_object_unref(GST_MINI_OBJECT(buf));
560 gst_h264encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
561 GstCaps * caps, GstBuffer ** buf)
563 GstH264Encode * const encode = GST_H264ENCODE(GST_OBJECT_PARENT(pad));
564 GstStructure *structure = NULL;
566 GstVaapiDisplay* display = NULL;
567 GstFlowReturn ret_num = GST_FLOW_ERROR;
570 structure = gst_caps_get_structure(caps, 0);
572 if (!structure || gst_structure_has_name(structure, "video/x-vaapi-surface")) {
573 H264_ASSERT(encode->encoder);
574 display = gst_h264_encoder_get_display(encode->encoder);
576 gst_h264_encoder_initialize(encode->encoder);
577 display = gst_h264_encoder_get_display(encode->encoder);
578 GST_H264_ENCODE_CHECK_STATUS(display, GST_FLOW_ERROR, "gst_h264_encoder_get_display failed in gst_h264encode_buffer_alloc.\n");
580 buffer = gst_vaapi_video_buffer_new(display);
581 } else { /* video/x-raw-yuv */
582 buffer = gst_buffer_new_and_alloc(size);
585 GST_H264_ENCODE_CHECK_STATUS(buffer, GST_FLOW_ERROR, "gst_h264encode_buffer_alloc failed.\n");
587 GST_BUFFER_OFFSET (buffer) = offset;
589 gst_buffer_set_caps(buffer, caps);
592 ret_num = GST_FLOW_OK;
596 g_object_unref(display);
601 static gboolean _h264_check_valid_profile(guint profile)
603 static const limit_profiles[] = {
604 H264_PROFILE_BASELINE,
608 guint n_profiles = sizeof(limit_profiles)/sizeof(limit_profiles[0]);
610 for (i = 0; i < n_profiles; ++i) {
611 if (limit_profiles[i] == profile)
617 static gboolean _h264_check_valid_level(guint level)
619 static const limit_levels[] = {
636 guint n_levels = sizeof(limit_levels)/sizeof(limit_levels[0]);
638 for (i = 0; i < n_levels; ++i) {
639 if (limit_levels[i] == level)
647 _h264_dump_caps(GstCaps *cpas)
650 GstStructure const *structure;
652 static char caps_string[4096*5];
655 char *cur = caps_string;
656 memset(caps_string, 0, sizeof(caps_string));
657 for (i = 0; i < gst_caps_get_size(cpas); i++) {
658 structure = gst_caps_get_structure(cpas, i);
659 const char* caps_name = gst_structure_get_name (structure);
660 sprintf(cur, "cap_%02d:%s\n", i, caps_name);
663 for (j = 0; j < gst_structure_n_fields(structure); j++) {
664 const char* name = gst_structure_nth_field_name(structure, j);
665 value = gst_structure_get_value(structure, name);
666 tmp = gst_value_serialize(value);
667 sprintf(cur, "\t%s:%s(%s)\n", name, tmp, G_VALUE_TYPE_NAME(value));
681 h264encode_init (GstPlugin * plugin)
683 return gst_element_register (plugin, "vah264encode", GST_RANK_PRIMARY,
684 GST_TYPE_H264ENCODE);
687 /* gstreamer looks for this structure to register mrstcamsrc */
697 "http://gstreamer.net/")