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