h264 encoder: worked in staging branch
[profile/ivi/gstreamer-vaapi.git] / gst / vaapi / gstvaapiencode.c
1 /*
2  *  gstvaapiencode.c - VA-API video encoder
3  *
4  *  Copyright (C) 2011 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 #include "config.h"
23 #include "gstvaapiencode.h"
24
25 #include <string.h>
26 #include <X11/Xlib.h>
27 #include <gst/video/videocontext.h>
28
29 #include "gst/vaapi/gstvaapivideobuffer.h"
30 #include "gst/vaapi/gstvaapisurfacepool.h"
31 #include "gst/vaapi/gstvaapiencode_h264.h"
32 #include "gst/vaapi/gstvaapiencode_h263.h"
33 #include "gst/vaapi/gstvaapiencode_mpeg4.h"
34 #include "gst/vaapi/gstvaapibaseencoder.h"
35 #include "gst/vaapi/gstvaapipluginutil.h"
36
37 #if USE_VAAPI_GLX
38 #include <gst/vaapi/gstvaapivideobuffer_glx.h>
39 #define gst_vaapi_video_buffer_new(display) \
40     gst_vaapi_video_buffer_glx_new(GST_VAAPI_DISPLAY_GLX(display))
41 #endif
42
43 /* gst_debug
44      GST_DEBUG_CATEGORY_STATIC (gst_vaapi_encode_debug)
45      #define GST_CAT_DEFAULT gst_vaapi_encode_debug
46          //class_init
47          GST_DEBUG_CATEGORY_INIT (gst_vaapi_encode_debug, "vaapiencode", 0,
48           "vaapiencode element");
49 */
50 GST_DEBUG_CATEGORY_STATIC (gst_vaapi_encode_debug);
51 #define GST_CAT_DEFAULT gst_vaapi_encode_debug
52
53 #define GST_VAAPI_ENCODE_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_VAAPI_ENCODE, GstVaapiEncodePrivate))
54
55 typedef struct _GstVaapiEncodePrivate GstVaapiEncodePrivate;
56
57
58 #define GstVideoContextClass GstVideoContextInterface
59 GST_BOILERPLATE_WITH_INTERFACE(
60     GstVaapiEncode,
61     gst_vaapi_encode,
62     GstElement,
63     GST_TYPE_ELEMENT,
64     GstVideoContext,
65     GST_TYPE_VIDEO_CONTEXT,
66     gst_video_context);
67
68
69 enum {
70     PROP_0,
71 };
72
73
74 /*static extern*/
75 static void     gst_vaapi_encode_finalize(GObject *object);
76 static void     gst_vaapi_encode_set_property(GObject *object, guint prop_id,
77     const GValue *value, GParamSpec *pspec);
78 static void     gst_vaapi_encode_get_property (GObject * object, guint prop_id,
79     GValue * value, GParamSpec * pspec);
80
81 static gboolean gst_vaapi_encode_set_caps(GstPad *sink_pad, GstCaps *caps);
82 static GstCaps *gst_vaapi_encode_get_caps(GstPad *sink_pad);
83 static GstFlowReturn        gst_vaapi_encode_chain(GstPad *sink_pad, GstBuffer *buf);
84 static GstStateChangeReturn gst_vaapi_encode_change_state(GstElement *element, GstStateChange transition);
85 static GstFlowReturn        gst_vaapi_encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
86                            GstCaps * caps, GstBuffer ** buf);
87
88 static char*    _encode_dump_caps(GstCaps *cpas);
89
90 /* context(display) interface */
91 static void
92 gst_vaapi_encode_set_video_context(GstVideoContext *context, const gchar *type,
93     const GValue *value)
94 {
95     GstVaapiEncode *encode = GST_VAAPI_ENCODE (context);
96     GstVaapiDisplay *display = NULL;
97     gst_vaapi_set_display (type, value, &display);
98     gst_vaapi_encoder_set_display(encode->encoder, display);
99 }
100
101 static gboolean
102 gst_video_context_supported (GstVaapiEncode *decode, GType iface_type)
103 {
104   return (iface_type == GST_TYPE_VIDEO_CONTEXT);
105 }
106
107 static void
108 gst_video_context_interface_init(GstVideoContextInterface *iface)
109 {
110     iface->set_context = gst_vaapi_encode_set_video_context;
111 }
112
113 static gboolean
114 gst_vaapi_encode_query (GstPad *pad, GstQuery *query) {
115     GstVaapiEncode *encode = GST_VAAPI_ENCODE (gst_pad_get_parent_element (pad));
116     gboolean res;
117
118     if (encode->encoder && gst_vaapi_reply_to_query(query, ENCODER_DISPLAY(encode->encoder)))
119       res = TRUE;
120     else
121       res = gst_pad_query_default (pad, query);
122
123     g_object_unref (encode);
124     return res;
125 }
126
127 /*gst fix functions*/
128
129 static void
130 gst_vaapi_encode_base_init(gpointer klass)
131 {
132   #if 0
133   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
134
135   gst_element_class_set_details(element_class, &gst_vaapi_encode_details);
136
137   /* sink pad */
138   gst_element_class_add_pad_template(
139       element_class,
140       gst_static_pad_template_get(&gst_vaapi_encode_sink_factory)
141   );
142
143   /* src pad */
144   gst_element_class_add_pad_template(
145       element_class,
146       gst_static_pad_template_get(&gst_vaapi_encode_src_factory)
147   );
148   #endif
149 }
150
151
152 static void
153 gst_vaapi_encode_class_init(GstVaapiEncodeClass *klass)
154 {
155   GObjectClass * const object_class = G_OBJECT_CLASS(klass);
156   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
157
158   object_class->finalize      = gst_vaapi_encode_finalize;
159   object_class->set_property  = gst_vaapi_encode_set_property;
160   object_class->get_property  = gst_vaapi_encode_get_property;
161
162   GST_DEBUG_CATEGORY_INIT (gst_vaapi_encode_debug, "vaapiencode", 0,
163       "vaapiencode element");
164
165   element_class->change_state = gst_vaapi_encode_change_state;
166
167   klass->set_encoder_src_caps = NULL;
168
169   /* Registering debug symbols for function pointers */
170   GST_DEBUG_REGISTER_FUNCPTR (gst_vaapi_encode_change_state);
171   GST_DEBUG_REGISTER_FUNCPTR (gst_vaapi_encode_get_caps);
172   GST_DEBUG_REGISTER_FUNCPTR (gst_vaapi_encode_set_caps);
173   GST_DEBUG_REGISTER_FUNCPTR (gst_vaapi_encode_chain);
174   GST_DEBUG_REGISTER_FUNCPTR (gst_vaapi_encode_buffer_alloc);
175 }
176
177 static void
178 gst_vaapi_encode_finalize(GObject *object)
179 {
180   GstVaapiEncode * const encode = GST_VAAPI_ENCODE(object);
181
182   if (encode->sinkpad_caps) {
183     gst_caps_unref(encode->sinkpad_caps);
184     encode->sinkpad_caps = NULL;
185   }
186   encode->sinkpad = NULL;
187
188   if (encode->srcpad_caps) {
189     gst_caps_unref(encode->srcpad_caps);
190     encode->srcpad_caps = NULL;
191   }
192   encode->srcpad = NULL;
193
194   if (encode->encoder) {
195       gst_vaapi_encoder_close(encode->encoder);
196       gst_vaapi_encoder_uninitialize(encode->encoder);
197       gst_vaapi_encoder_unref(encode->encoder);
198       encode->encoder = NULL;
199   }
200
201   G_OBJECT_CLASS(parent_class)->finalize(object);
202 }
203
204 static void
205 gst_vaapi_encode_init(GstVaapiEncode *encode, GstVaapiEncodeClass *klass)
206 {
207   GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
208
209   encode->sinkpad_caps       = NULL;
210   encode->srcpad_caps        = NULL;
211   encode->first_sink_frame   = TRUE;
212   encode->first_src_frame    = TRUE;
213
214   encode->encoder = NULL;
215
216   /*sink pad */
217   encode->sinkpad = gst_pad_new_from_template(
218       gst_element_class_get_pad_template(element_class, "sink"),
219       "sink"
220   );
221   gst_pad_set_getcaps_function(encode->sinkpad, gst_vaapi_encode_get_caps);
222   gst_pad_set_setcaps_function(encode->sinkpad, gst_vaapi_encode_set_caps);
223   gst_pad_set_chain_function(encode->sinkpad, gst_vaapi_encode_chain);
224   gst_pad_set_bufferalloc_function(encode->sinkpad, gst_vaapi_encode_buffer_alloc);
225   /*gst_pad_set_event_function(encode->sinkpad, gst_vaapi_encode_sink_event); */
226   /*gst_pad_use_fixed_caps(encode->sinkpad);*/
227   gst_pad_set_query_function(encode->sinkpad, gst_vaapi_encode_query);
228   gst_element_add_pad(GST_ELEMENT(encode), encode->sinkpad);
229
230   /* src pad */
231   encode->srcpad = gst_pad_new_from_template(
232       gst_element_class_get_pad_template(element_class, "src"),
233       "src"
234   );
235   encode->srcpad_caps = NULL;
236
237   gst_pad_use_fixed_caps(encode->srcpad);
238   /*gst_pad_set_event_function(encode->srcpad, gst_vaapi_encode_src_event);*/
239   gst_pad_set_query_function(encode->sinkpad, gst_vaapi_encode_query);
240   gst_element_add_pad(GST_ELEMENT(encode), encode->srcpad);
241 }
242
243
244 static void
245 gst_vaapi_encode_set_property(GObject *object, guint prop_id,
246     const GValue *value, GParamSpec *pspec)
247 {
248   GstVaapiEncode *encode = GST_VAAPI_ENCODE(object);
249   ENCODER_ASSERT(encode->encoder);
250
251   switch (prop_id) {
252   }
253 }
254
255 static void
256 gst_vaapi_encode_get_property (GObject * object, guint prop_id,
257     GValue * value, GParamSpec * pspec)
258 {
259   GstVaapiEncode *encode = GST_VAAPI_ENCODE(object);
260   ENCODER_ASSERT(encode->encoder);
261
262   switch (prop_id) {
263   }
264 }
265
266 static gboolean
267 gst_vaapi_encode_set_caps(GstPad *sink_pad, GstCaps *caps)
268 {
269   GstVaapiEncode *encode = GST_VAAPI_ENCODE(GST_OBJECT_PARENT(sink_pad));
270   GstStructure *structure = NULL, *src_struct = NULL;
271   gint width = 0, height = 0;
272   gint fps_n = 0, fps_d = 0;
273   const GValue *fps_value = NULL, *format_value;
274   guint32 format = 0;
275   gboolean ret = TRUE;
276   EncoderStatus encoder_ret = ENCODER_NO_ERROR;
277
278   encode->sinkpad_caps = caps;
279   gst_caps_ref(caps);
280   ENCODER_LOG_INFO("gst_vaapi_encode_set_caps,\n%s", _encode_dump_caps(caps));
281
282   structure = gst_caps_get_structure (caps, 0);
283   if (gst_structure_get_int (structure, "width", &width)) {
284     encode->encoder->width = width;
285   }
286   if (gst_structure_get_int (structure, "height", &height)) {
287     encode->encoder->height = height;
288   }
289   fps_value = gst_structure_get_value (structure, "framerate");
290   if (fps_value) {
291     fps_n = gst_value_get_fraction_numerator (fps_value);
292     fps_d = gst_value_get_fraction_denominator (fps_value);
293     encode->encoder->frame_rate = fps_n/fps_d;
294   }
295   format_value = gst_structure_get_value (structure, "format");
296   if (format_value && GST_IS_VAAPI_ENCODE_H264(encode)) {
297     ENCODER_CHECK_STATUS(format_value && GST_TYPE_FOURCC == G_VALUE_TYPE(format_value),
298                          FALSE, "1st buffer caps' format type is not fourcc.");
299     format = gst_value_get_fourcc (format_value);
300     if (format) {
301       gst_vaapi_base_encoder_set_input_format(GST_VAAPI_BASE_ENCODER(encode->encoder), format);
302     }
303   }
304
305   /*set src pad caps*/
306   if (encode->srcpad_caps) {
307     gst_caps_unref(encode->srcpad_caps);
308   }
309   encode->srcpad_caps = gst_caps_copy(gst_pad_get_pad_template_caps(encode->srcpad));
310   src_struct = gst_caps_get_structure(encode->srcpad_caps, 0);
311   gst_structure_set(src_struct, "width", G_TYPE_INT, width,
312                     "height", G_TYPE_INT, height,
313                     "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
314   GstVaapiEncodeClass *encode_class = GST_VAAPI_ENCODE_GET_CLASS(encode);
315   if (encode_class->set_encoder_src_caps) {
316     encode_class->set_encoder_src_caps(encode, encode->srcpad_caps);
317   }
318
319   /*set display and initialize encoder*/
320   ENCODER_CHECK_STATUS(gst_vaapi_ensure_display(encode, &ENCODER_DISPLAY(encode->encoder)),
321                          FALSE, "encoder ensure display failed on setting caps.")
322   encoder_ret = gst_vaapi_encoder_initialize(encode->encoder);
323   ENCODER_CHECK_STATUS (ENCODER_NO_ERROR == encoder_ret,
324                          FALSE, "gst_vaapi_encoder_initialize failed.");
325   encoder_ret = gst_vaapi_encoder_open(encode->encoder, NULL);
326   ENCODER_CHECK_STATUS (ENCODER_NO_ERROR == encoder_ret,
327                          FALSE, "gst_vaapi_encoder_open failed.");
328 end:
329   return ret;
330 }
331
332 static GstCaps *
333 gst_vaapi_encode_get_caps(GstPad *sink_pad)
334 {
335   GstCaps *caps = NULL;
336   GstVaapiEncode * const encode = GST_VAAPI_ENCODE(GST_OBJECT_PARENT(sink_pad));
337   if (encode->sinkpad_caps) {
338     gst_caps_ref(encode->sinkpad_caps);
339     ENCODER_LOG_INFO("get caps,\n%s", _encode_dump_caps(encode->sinkpad_caps));
340     return encode->sinkpad_caps;
341   }
342   caps = gst_caps_copy(gst_pad_get_pad_template_caps(sink_pad));
343   return caps;
344 }
345
346 static GstStateChangeReturn
347 gst_vaapi_encode_change_state(GstElement *element, GstStateChange transition)
348 {
349   GstVaapiEncode * const encode = GST_VAAPI_ENCODE(element);
350   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
351
352   switch (transition) {
353   case GST_STATE_CHANGE_READY_TO_PAUSED:
354     break;
355   case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
356     break;
357   default:
358     break;
359   }
360
361   ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
362   if (ret != GST_STATE_CHANGE_SUCCESS)
363     return ret;
364
365   switch (transition) {
366   case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
367     break;
368   case GST_STATE_CHANGE_PAUSED_TO_READY: {
369     gst_vaapi_encoder_close(encode->encoder);
370   }
371     break;
372   default:
373     break;
374   }
375   return GST_STATE_CHANGE_SUCCESS;
376 }
377
378
379 static GstFlowReturn
380 gst_vaapi_encode_chain(GstPad *sink_pad, GstBuffer *buf)
381 {
382   GstFlowReturn ret = GST_FLOW_OK;
383   GstVaapiEncode *encode = GST_VAAPI_ENCODE(GST_OBJECT_PARENT(sink_pad));
384   EncoderStatus encoder_ret = ENCODER_NO_ERROR;
385   GList *out_buffers = NULL;
386   GstBuffer *tmp_buffer = NULL;
387
388   ENCODER_ASSERT(encode && encode->encoder);
389   if (encode->first_sink_frame) {
390     /* get first buffer caps and set encoder values */
391     if (GST_VAAPI_IS_VIDEO_BUFFER(buf)) {
392       GstVaapiVideoBuffer *video_buffer = GST_VAAPI_VIDEO_BUFFER(buf);
393       ENCODER_ASSERT(video_buffer);
394       ENCODER_ASSERT(gst_vaapi_video_buffer_get_display(video_buffer) == ENCODER_DISPLAY(encode->encoder));
395     }
396     encode->first_sink_frame = FALSE;
397   }
398
399   /*encoding frames*/
400   ENCODER_ASSERT(gst_vaapi_encoder_get_state(encode->encoder) >= VAAPI_ENC_OPENED);
401   encoder_ret = gst_vaapi_encoder_encode(encode->encoder, buf, &out_buffers);
402   ENCODER_CHECK_STATUS (ENCODER_NO_ERROR == encoder_ret, GST_FLOW_ERROR, "gst_vaapi_encoder_encode failed.");
403
404   /*check results*/
405   while (out_buffers) {
406     tmp_buffer = out_buffers->data;
407     out_buffers = g_list_remove(out_buffers, tmp_buffer);
408     if (encode->first_src_frame) {
409       GstBuffer *codec_data = NULL;
410       ENCODER_ASSERT(encode->srcpad_caps);
411       /*replace codec data in src pad caps*/
412       if (ENCODER_NO_ERROR == gst_vaapi_encoder_get_codec_data(encode->encoder, &codec_data) && codec_data) {
413         gst_caps_set_simple(encode->srcpad_caps, "codec_data",GST_TYPE_BUFFER, codec_data, NULL);
414       }
415       gst_pad_set_caps (encode->srcpad, encode->srcpad_caps);
416       GST_BUFFER_CAPS(tmp_buffer) = gst_caps_ref(encode->srcpad_caps);
417       ENCODER_LOG_INFO("gst_vaapi_encode_chain 1st push-buffer caps,\n%s", _encode_dump_caps(encode->srcpad_caps));
418       encode->first_src_frame = FALSE;
419     }
420     ENCODER_LOG_DEBUG("output:%" GST_TIME_FORMAT ", 0x%s",
421                    GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(tmp_buffer)),
422                    vaapi_encoder_dump_bytes(GST_BUFFER_DATA(tmp_buffer),
423                                   (GST_BUFFER_SIZE(tmp_buffer) > 16? 16: GST_BUFFER_SIZE(tmp_buffer))));
424     gst_pad_push(encode->srcpad, tmp_buffer);
425   }
426
427 end:
428   gst_buffer_unref(buf);
429   return ret;
430
431 }
432
433 static GstFlowReturn
434 gst_vaapi_encode_buffer_alloc(GstPad * pad, guint64 offset, guint size,
435                            GstCaps * caps, GstBuffer ** buf)
436 {
437   GstVaapiEncode * const encode = GST_VAAPI_ENCODE(GST_OBJECT_PARENT(pad));
438   GstStructure *structure = NULL;
439   GstBuffer *buffer;
440   GstVaapiDisplay* display = NULL;
441   GstFlowReturn ret = GST_FLOW_ERROR;
442
443   if (caps) {
444     structure = gst_caps_get_structure(caps, 0);
445   }
446   if (!structure || gst_structure_has_name(structure, "video/x-vaapi-surface")) {
447     ENCODER_ASSERT(encode->encoder);
448     ENCODER_CHECK_STATUS(gst_vaapi_ensure_display(encode, &ENCODER_DISPLAY(encode->encoder)),
449                          GST_FLOW_ERROR, "encoder ensure display failed.")
450     display = ENCODER_DISPLAY(encode->encoder);
451     buffer = gst_vaapi_video_buffer_new(display);
452   } else { /* video/x-raw-yuv */
453     buffer = gst_buffer_new_and_alloc(size);
454   }
455
456   ENCODER_CHECK_STATUS(buffer, GST_FLOW_ERROR, "gst_vaapi_encode_buffer_alloc failed.");
457
458   GST_BUFFER_OFFSET (buffer) = offset;
459   if (caps) {
460     gst_buffer_set_caps(buffer, caps);
461   }
462   *buf = buffer;
463   ret = GST_FLOW_OK;
464
465 end:
466   return ret;
467 }
468
469
470 static char*
471 _encode_dump_caps(GstCaps *cpas)
472 {
473   guint i = 0, j = 0;
474   GstStructure const *structure;
475   GValue const *value;
476   static char caps_string[4096*5];
477   char *tmp;
478
479   char *cur = caps_string;
480   memset(caps_string, 0, sizeof(caps_string));
481   for (i = 0; i < gst_caps_get_size(cpas); i++) {
482     structure = gst_caps_get_structure(cpas, i);
483     const char* caps_name = gst_structure_get_name (structure);
484     sprintf(cur, "cap_%02d:%s\n", i, caps_name);
485     cur += strlen(cur);
486
487     for (j = 0; j < gst_structure_n_fields(structure); j++) {
488       const char* name = gst_structure_nth_field_name(structure, j);
489       value = gst_structure_get_value(structure, name);
490       tmp = gst_value_serialize(value);
491       sprintf(cur, "\t%s:%s(%s)\n", name, tmp, G_VALUE_TYPE_NAME(value));
492       cur += strlen(cur);
493       g_free(tmp);
494     }
495   }
496
497   return caps_string;
498 }
499