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