696bea5608a47ef3697b437ea7cbc3725002daf0
[profile/ivi/gstreamer-vaapi.git] / gst / vaapiencode / gsth264encode.c
1 #include "gsth264encode.h"
2
3 #include <string.h>
4 #include <X11/Xlib.h>
5
6 #include "gst/vaapi/gstvaapivideobuffer.h"
7
8
9 #define PACKAGE "libgsth264encode"
10 #define VERSION "0.1.0"
11
12 #define GST_H264_ENCODE_CHECK_STATUS(exp, err_num, err_reason, ...)  \
13   H264_ASSERT(exp);                             \
14   if (!(exp)) {                                 \
15     ret_num = err_num;                              \
16     H264_LOG_ERROR(err_reason, ## __VA_ARGS__);                 \
17     goto finish;                                 \
18   }
19
20
21 #define GST_H264ENCODE_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_H264ENCODE, GstH264EncodePrivate))
22
23 typedef struct _GstH264EncodePrivate GstH264EncodePrivate;
24
25 static const GstElementDetails gst_h264encode_details =
26     GST_ELEMENT_DETAILS(
27         "VA-API h264 encoder",
28         "Codec/Encoder/Video",
29         "A VA-API based h264 encoder",
30         "Yuan Feng <feng.yuan@intel.com>");
31
32 /* Default templates */
33 #define GST_CAPS_CODEC(CODEC)                   \
34     CODEC ", "                                  \
35     "width  = (int) [ 1, MAX ], "               \
36     "height = (int) [ 1, MAX ]; "
37
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 ")
42     GST_CAPS_CODEC("video/x-raw-va")
43     ;
44
45 static const char gst_h264encode_src_caps_str[] =
46     GST_CAPS_CODEC("video/x-h264");
47
48 static GstStaticPadTemplate gst_h264encode_sink_factory =
49     GST_STATIC_PAD_TEMPLATE(
50         "sink",
51         GST_PAD_SINK,
52         GST_PAD_ALWAYS,
53         GST_STATIC_CAPS(gst_h264encode_sink_caps_str));
54
55 static GstStaticPadTemplate gst_h264encode_src_factory =
56     GST_STATIC_PAD_TEMPLATE(
57         "src",
58         GST_PAD_SRC,
59         GST_PAD_ALWAYS,
60         GST_STATIC_CAPS(gst_h264encode_src_caps_str));
61
62 GST_BOILERPLATE(
63     GstH264Encode,
64     gst_h264encode,
65     GstElement,
66     GST_TYPE_ELEMENT);
67
68 enum {
69     PROP_0,
70 };
71
72
73 /*static extern*/
74 static void gst_h264encode_finalize(GObject *object);
75 static void gst_h264encode_set_property(GObject *object, guint prop_id,
76     const GValue *value, GParamSpec *pspec);
77 static void gst_h264encode_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79
80 static gboolean gst_h264encode_set_caps(GstPad *sink_pad, GstCaps *caps);
81 static GstCaps *gst_h264encode_get_caps(GstPad *sink_pad);
82 static GstFlowReturn gst_h264encode_chain(GstPad *sink_pad, GstBuffer *buf);
83 static GstStateChangeReturn gst_h264encode_change_state(GstElement *element, GstStateChange transition);
84 static GstFlowReturn gst_h264encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
85                            GstCaps * caps, GstBuffer ** buf);
86
87 static char* _h264_dump_caps(GstCaps *cpas);
88
89 /*gst fix functions*/
90
91 static void
92 gst_h264encode_base_init(gpointer klass)
93 {
94   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
95
96   gst_element_class_set_details(element_class, &gst_h264encode_details);
97
98   /* sink pad */
99   gst_element_class_add_pad_template(
100       element_class,
101       gst_static_pad_template_get(&gst_h264encode_sink_factory)
102   );
103
104   /* src pad */
105   gst_element_class_add_pad_template(
106       element_class,
107       gst_static_pad_template_get(&gst_h264encode_src_factory)
108   );
109 }
110
111
112 static void
113 gst_h264encode_class_init(GstH264EncodeClass *klass)
114 {
115   GObjectClass * const object_class = G_OBJECT_CLASS(klass);
116   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
117
118   object_class->finalize      = gst_h264encode_finalize;
119   object_class->set_property  = gst_h264encode_set_property;
120   object_class->get_property  = gst_h264encode_get_property;
121
122   element_class->change_state = gst_h264encode_change_state;
123 }
124
125 static void
126 gst_h264encode_finalize(GObject *object)
127 {
128   GstH264Encode * const encode = GST_H264ENCODE(object);
129
130   if (encode->sinkpad_caps) {
131     gst_caps_unref(encode->sinkpad_caps);
132     encode->sinkpad_caps = NULL;
133   }
134   encode->sinkpad = NULL;
135
136   if (encode->srcpad_caps) {
137     gst_caps_unref(encode->srcpad_caps);
138     encode->srcpad_caps = NULL;
139   }
140   encode->srcpad = NULL;
141
142   if (encode->encoder) {
143       gst_h264_encoder_close(encode->encoder);
144       gst_h264_encoder_uninitialize(encode->encoder);
145       gst_h264_encoder_unref(encode->encoder);
146       encode->encoder = NULL;
147   }
148
149   if (encode->x_display) {
150     XCloseDisplay(encode->x_display);
151     encode->x_display = NULL;
152   }
153
154   G_OBJECT_CLASS(parent_class)->finalize(object);
155 }
156
157 static void
158 gst_h264encode_init(GstH264Encode *encode, GstH264EncodeClass *klass)
159 {
160   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
161
162   encode->sinkpad_caps       = NULL;
163   encode->srcpad_caps        = NULL;
164   encode->first_sink_frame   = TRUE;
165   encode->first_src_frame    = TRUE;
166   encode->x_display = NULL;
167
168   encode->encoder = gst_h264_encoder_new();
169   H264_ASSERT(encode->encoder);
170
171   /*sink pad */
172   encode->sinkpad = gst_pad_new_from_template(
173       gst_element_class_get_pad_template(element_class, "sink"),
174       "sink"
175   );
176   gst_pad_set_getcaps_function(encode->sinkpad, gst_h264encode_get_caps);
177   gst_pad_set_setcaps_function(encode->sinkpad, gst_h264encode_set_caps);
178   gst_pad_set_chain_function(encode->sinkpad, gst_h264encode_chain);
179   gst_pad_set_bufferalloc_function(encode->sinkpad, gst_h264encode_buffer_alloc);
180   /*gst_pad_set_event_function(encode->sinkpad, gst_h264encode_sink_event); */
181   /*gst_pad_use_fixed_caps(encode->sinkpad);*/
182   gst_element_add_pad(GST_ELEMENT(encode), encode->sinkpad);
183
184   /* src pad */
185   encode->srcpad = gst_pad_new_from_template(
186       gst_element_class_get_pad_template(element_class, "src"),
187       "src"
188   );
189   encode->srcpad_caps = NULL;
190
191   gst_pad_use_fixed_caps(encode->srcpad);
192   /*gst_pad_set_event_function(encode->srcpad, gst_h264encode_src_event);*/
193   gst_element_add_pad(GST_ELEMENT(encode), encode->srcpad);
194 }
195
196
197 static void
198 gst_h264encode_set_property(GObject *object, guint prop_id,
199     const GValue *value, GParamSpec *pspec)
200 {
201 }
202
203 static void
204 gst_h264encode_get_property (GObject * object, guint prop_id,
205     GValue * value, GParamSpec * pspec)
206 {
207 }
208
209 static gboolean
210 gst_h264encode_set_caps(GstPad *sink_pad, GstCaps *caps)
211 {
212   GstH264Encode *encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
213   encode->sinkpad_caps = caps;
214   H264_LOG_INFO("gst_h264encode_set_caps,\n%s", _h264_dump_caps(caps));
215   gst_caps_ref(caps);
216   return TRUE;
217 }
218
219 static GstCaps *
220 gst_h264encode_get_caps(GstPad *sink_pad)
221 {
222   GstCaps *caps = NULL;
223   GstH264Encode * const encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
224   if (encode->sinkpad_caps) {
225     gst_caps_ref(encode->sinkpad_caps);
226     H264_LOG_INFO("get caps,\n%s", _h264_dump_caps(encode->sinkpad_caps));
227     return encode->sinkpad_caps;
228   }
229   caps = gst_caps_copy(gst_pad_get_pad_template_caps(sink_pad));
230   return caps;
231 }
232
233 static GstStateChangeReturn
234 gst_h264encode_change_state(GstElement *element, GstStateChange transition)
235 {
236   GstH264Encode * const encode = GST_H264ENCODE(element);
237   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
238
239   switch (transition) {
240   case GST_STATE_CHANGE_READY_TO_PAUSED:
241     break;
242   case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
243     break;
244   default:
245     break;
246   }
247
248   ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
249   if (ret != GST_STATE_CHANGE_SUCCESS)
250     return ret;
251
252   switch (transition) {
253   case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
254     break;
255   case GST_STATE_CHANGE_PAUSED_TO_READY: {
256     gst_h264_encoder_close(encode->encoder);
257   }
258     break;
259   default:
260     break;
261   }
262   return GST_STATE_CHANGE_SUCCESS;
263 }
264
265
266 static GstFlowReturn
267 gst_h264encode_chain(GstPad *sink_pad, GstBuffer *buf)
268 {
269   GstFlowReturn ret_num = GST_FLOW_OK;
270   GstH264Encode *encode = GST_H264ENCODE(GST_OBJECT_PARENT(sink_pad));
271   H264Status h264ret = H264_NO_ERROR;
272   GList *out_buffers = NULL;
273   GstBuffer *tmp_buffer = NULL;
274
275   static guint input_count = 0;
276   static guint output_count = 0;
277
278   H264_ASSERT(encode && encode->encoder);
279   if (encode->first_sink_frame) {
280     /* get first buffer caps and set encoder values */
281     GstStructure *recv_struct, *src_struct;
282     GstCaps *recv_caps = GST_BUFFER_CAPS(buf);
283     gint width, height;
284     GValue const *framerate, *format_value;
285     gint fps_n, fps_d;
286     guint32 format;
287     GstVaapiSurfacePool *surface_pool = NULL;
288
289     H264_LOG_INFO("gst_h264encode_chain 1st recv-buffer caps,\n%s", _h264_dump_caps(recv_caps));
290
291     recv_struct = gst_caps_get_structure (recv_caps, 0);
292     GST_H264_ENCODE_CHECK_STATUS(NULL != recv_caps, GST_FLOW_ERROR, "gst_h264encode_chain, 1st buffer didn't have detailed caps.\n");
293     gst_structure_get_int (recv_struct, "width", &width);
294     gst_structure_get_int (recv_struct, "height", &height);
295     framerate = gst_structure_get_value (recv_struct, "framerate");
296     fps_n = gst_value_get_fraction_numerator (framerate);
297     fps_d = gst_value_get_fraction_denominator (framerate);
298     format_value = gst_structure_get_value (recv_struct, "format");
299     if (format_value) {
300       GST_H264_ENCODE_CHECK_STATUS(format_value && GST_TYPE_FOURCC == G_VALUE_TYPE(format_value),
301                                  GST_FLOW_ERROR, "1st buffer caps' format type is not fourcc.\n");
302       format = gst_value_get_fourcc (format_value);
303     }
304
305     encode->encoder->profile = 66;
306     encode->encoder->level = 30;
307     encode->encoder->width = width;
308     encode->encoder->height = height;
309     encode->encoder->frame_rate = fps_n/fps_d;
310     encode->encoder->bitrate = 3*1000*1000; // 3M
311     encode->encoder->intra_period = 30;
312
313     /*set src pad caps*/
314     if (encode->srcpad_caps) {
315       gst_caps_unref(encode->srcpad_caps);
316     }
317     encode->srcpad_caps = gst_caps_copy(gst_pad_get_pad_template_caps(encode->srcpad));
318     src_struct = gst_caps_get_structure(encode->srcpad_caps, 0);
319     gst_structure_set(src_struct, "width", G_TYPE_INT, width,
320                       "height", G_TYPE_INT, height,
321                       "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
322
323     /*set display and initialize encoder*/
324     if (format) {
325       gst_h264_encoder_set_input_format(encode->encoder, format);
326     }
327     if (GST_VAAPI_IS_VIDEO_BUFFER(buf)) {
328       GstVaapiDisplay *display = NULL;
329       GstVaapiVideoBuffer *video_buffer = GST_VAAPI_VIDEO_BUFFER(buf);
330       H264_ASSERT(video_buffer);
331       display = gst_vaapi_video_buffer_get_display(video_buffer);
332       //need to get surface_pool and set to h264encoder->vaapi_context
333       //(video_buffer->priv->surface_pool);
334       #ifdef _MRST_
335       surface_pool = gst_vaapi_video_buffer_get_surface_pool(video_buffer);
336       #endif
337       if (display) {
338         GST_H264_ENCODE_CHECK_STATUS(gst_h264_encoder_set_display(encode->encoder,display)
339                                     , GST_FLOW_ERROR, "set display failed in gst_h264encode_chain.\n");
340       }
341     }
342     h264ret = gst_h264_encoder_initialize(encode->encoder);
343     GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "h264_encoder_initialize failed.\n");
344   #ifdef _MRST_
345     h264ret = gst_h264_encoder_open(encode->encoder, surface_pool);
346   #else
347     h264ret = gst_h264_encoder_open(encode->encoder);
348   #endif
349     GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "gst_h264_encoder_open failed.\n");
350
351     encode->first_sink_frame = FALSE;
352   }
353
354   /*encoding frames*/
355   H264_ASSERT(gst_h264_encoder_get_state(encode->encoder) >= H264_ENC_OPENED);
356   ++input_count;
357   H264_LOG_INFO("input %d\n", input_count);
358   h264ret = gst_h264_encoder_encode(encode->encoder, buf, &out_buffers);
359   GST_H264_ENCODE_CHECK_STATUS (H264_NO_ERROR == h264ret, GST_FLOW_ERROR, "h264_encoder_encode failed.\n");
360
361   /*check results*/
362   while (out_buffers) {
363     tmp_buffer = out_buffers->data;
364     out_buffers = g_list_remove(out_buffers, tmp_buffer);
365     /*out_buffers = g_list_next(out_buffers);*/
366     if (encode->first_src_frame) {
367       GstBuffer *codec_data;
368       H264_ASSERT(encode->srcpad_caps);
369       /*replace codec data in src pad caps*/
370       if (H264_NO_ERROR == gst_h264_encoder_get_avcC_codec_data(encode->encoder, &codec_data)) {
371         gst_caps_set_simple(encode->srcpad_caps, "codec_data",GST_TYPE_BUFFER, codec_data, NULL);
372       }
373       gst_pad_set_caps (encode->srcpad, encode->srcpad_caps);
374       GST_BUFFER_CAPS(tmp_buffer) = gst_caps_ref(encode->srcpad_caps);
375       H264_LOG_INFO("gst_h264encode_chain 1st push-buffer caps,\n%s", _h264_dump_caps(encode->srcpad_caps));
376       encode->first_src_frame = FALSE;
377     }
378     ++output_count;
379     H264_LOG_INFO("output:%d, %" GST_TIME_FORMAT ", 0x%s\n",
380                    output_count,
381                    GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(tmp_buffer)),
382                    h264_dump_bytes(GST_BUFFER_DATA(tmp_buffer),
383                                   (GST_BUFFER_SIZE(tmp_buffer) > 16? 16: GST_BUFFER_SIZE(tmp_buffer))));
384     gst_pad_push(encode->srcpad, tmp_buffer);
385   }
386
387 finish:
388   gst_mini_object_unref(GST_MINI_OBJECT(buf));
389   return ret_num;
390
391 }
392
393 static GstFlowReturn
394 gst_h264encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
395                            GstCaps * caps, GstBuffer ** buf)
396 {
397   GstH264Encode * const encode = GST_H264ENCODE(GST_OBJECT_PARENT(pad));
398   GstStructure *structure = NULL;
399   GstBuffer *buffer;
400   GstVaapiDisplay* display = NULL;
401   GstFlowReturn ret_num = GST_FLOW_ERROR;
402
403   if (caps) {
404     structure = gst_caps_get_structure(caps, 0);
405   }
406   if (!structure || gst_structure_has_name(structure, "video/x-vaapi-surface")) {
407     H264_ASSERT(encode->encoder);
408     display = gst_h264_encoder_get_display(encode->encoder);
409     if (!display) {
410       gst_h264_encoder_initialize(encode->encoder);
411       display = gst_h264_encoder_get_display(encode->encoder);
412       GST_H264_ENCODE_CHECK_STATUS(display, GST_FLOW_ERROR, "gst_h264_encoder_get_display failed in gst_h264encode_buffer_alloc.\n");
413     }
414     buffer = gst_vaapi_video_buffer_new(display);
415   } else { /* video/x-raw-yuv */
416     buffer = gst_buffer_new_and_alloc(size);
417   }
418
419   GST_H264_ENCODE_CHECK_STATUS(buffer, GST_FLOW_ERROR, "gst_h264encode_buffer_alloc failed.\n");
420
421   GST_BUFFER_OFFSET (buffer) = offset;
422   if (caps) {
423     gst_buffer_set_caps(buffer, caps);
424   }
425   *buf = buffer;
426   ret_num = GST_FLOW_OK;
427
428 finish:
429   if (display) {
430     g_object_unref(display);
431   }
432   return ret_num;
433 }
434
435
436
437 static char*
438 _h264_dump_caps(GstCaps *cpas)
439 {
440   guint i = 0, j = 0;
441   GstStructure const *structure;
442   GValue const *value;
443   static char caps_string[4096*5];
444   char *tmp;
445
446   char *cur = caps_string;
447   memset(caps_string, 0, sizeof(caps_string));
448   for (i = 0; i < gst_caps_get_size(cpas); i++) {
449     structure = gst_caps_get_structure(cpas, i);
450     const char* caps_name = gst_structure_get_name (structure);
451     sprintf(cur, "cap_%02d:%s\n", i, caps_name);
452     cur += strlen(cur);
453
454     for (j = 0; j < gst_structure_n_fields(structure); j++) {
455       const char* name = gst_structure_nth_field_name(structure, j);
456       value = gst_structure_get_value(structure, name);
457       tmp = gst_value_serialize(value);
458       sprintf(cur, "\t%s:%s(%s)\n", name, tmp, G_VALUE_TYPE_NAME(value));
459       cur += strlen(cur);
460       g_free(tmp);
461     }
462   }
463
464   return caps_string;
465 }
466
467
468
469
470 /* plugin register*/
471 static gboolean
472 h264encode_init (GstPlugin * plugin)
473 {
474   return gst_element_register (plugin, "vah264encode", GST_RANK_PRIMARY,
475       GST_TYPE_H264ENCODE);
476 }
477
478 /* gstreamer looks for this structure to register mrstcamsrc */
479 GST_PLUGIN_DEFINE (
480     GST_VERSION_MAJOR,
481     GST_VERSION_MINOR,
482     "vaapiencode",
483     "Vaapi Encoder",
484     h264encode_init,
485     VERSION,
486     "LGPL",
487     "GStreamer",
488     "http://gstreamer.net/")
489
490