Userful gstreamer-vaapi
[vaapi:cpu-gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiencoder.c
1 /*
2  *  gstvaapiencoder.c - VA encoder abstraction
3  *
4  *  Copyright (C) 2013-2014 Intel Corporation
5  *    Author: Wind Yuan <feng.yuan@intel.com>
6  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public License
10  *  as published by the Free Software Foundation; either version 2.1
11  *  of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free
20  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301 USA
22  */
23
24 #include "sysdeps.h"
25 #include "gstvaapicompat.h"
26 #include "gstvaapiencoder.h"
27 #include "gstvaapiencoder_priv.h"
28 #include "gstvaapicontext.h"
29 #include "gstvaapidisplay_priv.h"
30 #include "gstvaapiutils.h"
31 #include "gstvaapiutils_core.h"
32 #include "gstvaapivalue.h"
33
34 #define DEBUG 1
35 #include "gstvaapidebug.h"
36
37 /* Helper function to create a new encoder property object */
38 static GstVaapiEncoderPropData *
39 prop_new (gint id, GParamSpec * pspec)
40 {
41   GstVaapiEncoderPropData *prop;
42
43   if (!id || !pspec)
44     return NULL;
45
46   prop = g_slice_new (GstVaapiEncoderPropData);
47   if (!prop)
48     return NULL;
49
50   prop->prop = id;
51   prop->pspec = g_param_spec_ref_sink (pspec);
52   return prop;
53 }
54
55 /* Helper function to release a property object and any memory held herein */
56 static void
57 prop_free (GstVaapiEncoderPropData * prop)
58 {
59   if (!prop)
60     return;
61
62   if (prop->pspec) {
63     g_param_spec_unref (prop->pspec);
64     prop->pspec = NULL;
65   }
66   g_slice_free (GstVaapiEncoderPropData, prop);
67 }
68
69 /* Helper function to lookup the supplied property specification */
70 static GParamSpec *
71 prop_find_pspec (GstVaapiEncoder * encoder, gint prop_id)
72 {
73   GPtrArray *const props = encoder->properties;
74   guint i;
75
76   if (props) {
77     for (i = 0; i < props->len; i++) {
78       GstVaapiEncoderPropInfo *const prop = g_ptr_array_index (props, i);
79       if (prop->prop == prop_id)
80         return prop->pspec;
81     }
82   }
83   return NULL;
84 }
85
86 /* Create a new array of properties, or NULL on error */
87 GPtrArray *
88 gst_vaapi_encoder_properties_append (GPtrArray * props, gint prop_id,
89     GParamSpec * pspec)
90 {
91   GstVaapiEncoderPropData *prop;
92
93   if (!props) {
94     props = g_ptr_array_new_with_free_func ((GDestroyNotify) prop_free);
95     if (!props)
96       return NULL;
97   }
98
99   prop = prop_new (prop_id, pspec);
100   if (!prop)
101     goto error_allocation_failed;
102   g_ptr_array_add (props, prop);
103   return props;
104
105   /* ERRORS */
106 error_allocation_failed:
107   {
108     GST_ERROR ("failed to allocate encoder property info structure");
109     g_ptr_array_unref (props);
110     return NULL;
111   }
112 }
113
114 /* Generate the common set of encoder properties */
115 GPtrArray *
116 gst_vaapi_encoder_properties_get_default (const GstVaapiEncoderClass * klass)
117 {
118   const GstVaapiEncoderClassData *const cdata = klass->class_data;
119   GPtrArray *props = NULL;
120
121   g_assert (cdata->rate_control_get_type != NULL);
122
123   /**
124    * GstVaapiEncoder:rate-control:
125    *
126    * The desired rate control mode, expressed as a #GstVaapiRateControl.
127    */
128   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
129       GST_VAAPI_ENCODER_PROP_RATECONTROL,
130       g_param_spec_enum ("rate-control",
131           "Rate Control", "Rate control mode",
132           cdata->rate_control_get_type (), cdata->default_rate_control,
133           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134
135   /**
136    * GstVaapiEncoder:bitrate:
137    *
138    * The desired bitrate, expressed in kbps.
139    */
140   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
141       GST_VAAPI_ENCODER_PROP_BITRATE,
142       g_param_spec_uint ("bitrate",
143           "Bitrate (kbps)",
144           "The desired bitrate expressed in kbps (0: auto-calculate)",
145           0, 100 * 1024, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146
147   /**
148    * GstVaapiEncoder:keyframe-period:
149    *
150    * The maximal distance between two keyframes.
151    */
152   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
153       GST_VAAPI_ENCODER_PROP_KEYFRAME_PERIOD,
154       g_param_spec_uint ("keyframe-period",
155           "Keyframe Period",
156           "Maximal distance between two keyframes (0: auto-calculate)", 1, 300,
157           30, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158
159   /**
160    * GstVaapiEncoder:tune:
161    *
162    * The desired encoder tuning option.
163    */
164   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
165       GST_VAAPI_ENCODER_PROP_TUNE,
166       g_param_spec_enum ("tune",
167           "Encoder Tuning",
168           "Encoder tuning option",
169           cdata->encoder_tune_get_type (), cdata->default_encoder_tune,
170           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
171
172   return props;
173 }
174
175 /**
176  * gst_vaapi_encoder_ref:
177  * @encoder: a #GstVaapiEncoder
178  *
179  * Atomically increases the reference count of the given @encoder by one.
180  *
181  * Returns: The same @encoder argument
182  */
183 GstVaapiEncoder *
184 gst_vaapi_encoder_ref (GstVaapiEncoder * encoder)
185 {
186   return gst_vaapi_object_ref (encoder);
187 }
188
189 /**
190  * gst_vaapi_encoder_unref:
191  * @encoder: a #GstVaapiEncoder
192  *
193  * Atomically decreases the reference count of the @encoder by one. If
194  * the reference count reaches zero, the encoder will be free'd.
195  */
196 void
197 gst_vaapi_encoder_unref (GstVaapiEncoder * encoder)
198 {
199   gst_vaapi_object_unref (encoder);
200 }
201
202 /**
203  * gst_vaapi_encoder_replace:
204  * @old_encoder_ptr: a pointer to a #GstVaapiEncoder
205  * @new_encoder: a #GstVaapiEncoder
206  *
207  * Atomically replaces the encoder encoder held in @old_encoder_ptr
208  * with @new_encoder. This means that @old_encoder_ptr shall reference
209  * a valid encoder. However, @new_encoder can be NULL.
210  */
211 void
212 gst_vaapi_encoder_replace (GstVaapiEncoder ** old_encoder_ptr,
213     GstVaapiEncoder * new_encoder)
214 {
215   gst_vaapi_object_replace (old_encoder_ptr, new_encoder);
216 }
217
218 /* Notifies gst_vaapi_encoder_create_coded_buffer() that a new buffer is free */
219 static void
220 _coded_buffer_proxy_released_notify (GstVaapiEncoder * encoder)
221 {
222   g_mutex_lock (&encoder->mutex);
223   g_cond_signal (&encoder->codedbuf_free);
224   g_mutex_unlock (&encoder->mutex);
225 }
226
227 /* Creates a new VA coded buffer object proxy, backed from a pool */
228 static GstVaapiCodedBufferProxy *
229 gst_vaapi_encoder_create_coded_buffer (GstVaapiEncoder * encoder)
230 {
231   GstVaapiCodedBufferPool *const pool =
232       GST_VAAPI_CODED_BUFFER_POOL (encoder->codedbuf_pool);
233   GstVaapiCodedBufferProxy *codedbuf_proxy;
234
235   g_mutex_lock (&encoder->mutex);
236   do {
237     codedbuf_proxy = gst_vaapi_coded_buffer_proxy_new_from_pool (pool);
238     if (codedbuf_proxy)
239       break;
240
241     /* Wait for a free coded buffer to become available */
242     g_cond_wait (&encoder->codedbuf_free, &encoder->mutex);
243     codedbuf_proxy = gst_vaapi_coded_buffer_proxy_new_from_pool (pool);
244   } while (0);
245   g_mutex_unlock (&encoder->mutex);
246   if (!codedbuf_proxy)
247     return NULL;
248
249   gst_vaapi_coded_buffer_proxy_set_destroy_notify (codedbuf_proxy,
250       (GDestroyNotify) _coded_buffer_proxy_released_notify, encoder);
251   return codedbuf_proxy;
252 }
253
254 /* Notifies gst_vaapi_encoder_create_surface() that a new surface is free */
255 static void
256 _surface_proxy_released_notify (GstVaapiEncoder * encoder)
257 {
258   g_mutex_lock (&encoder->mutex);
259   g_cond_signal (&encoder->surface_free);
260   g_mutex_unlock (&encoder->mutex);
261 }
262
263 /* Creates a new VA surface object proxy, backed from a pool and
264    useful to allocate reconstructed surfaces */
265 GstVaapiSurfaceProxy *
266 gst_vaapi_encoder_create_surface (GstVaapiEncoder * encoder)
267 {
268   GstVaapiSurfaceProxy *proxy;
269
270   g_return_val_if_fail (encoder->context != NULL, NULL);
271
272   g_mutex_lock (&encoder->mutex);
273   for (;;) {
274     proxy = gst_vaapi_context_get_surface_proxy (encoder->context);
275     if (proxy)
276       break;
277
278     /* Wait for a free surface proxy to become available */
279     g_cond_wait (&encoder->surface_free, &encoder->mutex);
280   }
281   g_mutex_unlock (&encoder->mutex);
282
283   gst_vaapi_surface_proxy_set_destroy_notify (proxy,
284       (GDestroyNotify) _surface_proxy_released_notify, encoder);
285   return proxy;
286 }
287
288 /**
289  * gst_vaapi_encoder_put_frame:
290  * @encoder: a #GstVaapiEncoder
291  * @frame: a #GstVideoCodecFrame
292  *
293  * Queues a #GstVideoCodedFrame to the HW encoder. The encoder holds
294  * an extra reference to the @frame.
295  *
296  * Return value: a #GstVaapiEncoderStatus
297  */
298 GstVaapiEncoderStatus
299 gst_vaapi_encoder_put_frame (GstVaapiEncoder * encoder,
300     GstVideoCodecFrame * frame)
301 {
302   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
303   GstVaapiEncoderStatus status;
304   GstVaapiEncPicture *picture;
305   GstVaapiCodedBufferProxy *codedbuf_proxy;
306
307   for (;;) {
308     picture = NULL;
309     status = klass->reordering (encoder, frame, &picture);
310     if (status == GST_VAAPI_ENCODER_STATUS_NO_SURFACE)
311       break;
312     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
313       goto error_reorder_frame;
314
315     codedbuf_proxy = gst_vaapi_encoder_create_coded_buffer (encoder);
316     if (!codedbuf_proxy)
317       goto error_create_coded_buffer;
318
319     status = klass->encode (encoder, picture, codedbuf_proxy);
320     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
321       goto error_encode;
322
323     gst_vaapi_coded_buffer_proxy_set_user_data (codedbuf_proxy,
324         picture, (GDestroyNotify) gst_vaapi_mini_object_unref);
325     g_async_queue_push (encoder->codedbuf_queue, codedbuf_proxy);
326     encoder->num_codedbuf_queued++;
327
328     /* Try again with any pending reordered frame now available for encoding */
329     frame = NULL;
330   }
331   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
332
333   /* ERRORS */
334 error_reorder_frame:
335   {
336     GST_ERROR ("failed to process reordered frames");
337     return status;
338   }
339 error_create_coded_buffer:
340   {
341     GST_ERROR ("failed to allocate coded buffer");
342     gst_vaapi_enc_picture_unref (picture);
343     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
344   }
345 error_encode:
346   {
347     GST_ERROR ("failed to encode frame (status = %d)", status);
348     gst_vaapi_enc_picture_unref (picture);
349     gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
350     return status;
351   }
352 }
353
354 /**
355  * gst_vaapi_encoder_get_buffer_with_timeout:
356  * @encoder: a #GstVaapiEncoder
357  * @out_codedbuf_proxy_ptr: the next coded buffer as a #GstVaapiCodedBufferProxy
358  * @timeout: the number of microseconds to wait for the coded buffer, at most
359  *
360  * Upon successful return, *@out_codedbuf_proxy_ptr contains the next
361  * coded buffer as a #GstVaapiCodedBufferProxy. The caller owns this
362  * object, so gst_vaapi_coded_buffer_proxy_unref() shall be called
363  * after usage. Otherwise, @GST_VAAPI_DECODER_STATUS_ERROR_NO_BUFFER
364  * is returned if no coded buffer is available so far (timeout).
365  *
366  * The parent frame is available as a #GstVideoCodecFrame attached to
367  * the user-data anchor of the output coded buffer. Ownership of the
368  * frame is transferred to the coded buffer.
369  *
370  * Return value: a #GstVaapiEncoderStatus
371  */
372 GstVaapiEncoderStatus
373 gst_vaapi_encoder_get_buffer_with_timeout (GstVaapiEncoder * encoder,
374     GstVaapiCodedBufferProxy ** out_codedbuf_proxy_ptr, guint64 timeout)
375 {
376   GstVaapiEncPicture *picture;
377   GstVaapiCodedBufferProxy *codedbuf_proxy;
378
379   codedbuf_proxy = g_async_queue_timeout_pop (encoder->codedbuf_queue, timeout);
380   if (!codedbuf_proxy)
381     return GST_VAAPI_ENCODER_STATUS_NO_BUFFER;
382
383   /* Wait for completion of all operations and report any error that occurred */
384   picture = gst_vaapi_coded_buffer_proxy_get_user_data (codedbuf_proxy);
385   if (!gst_vaapi_surface_sync (picture->surface))
386     goto error_invalid_buffer;
387
388   gst_vaapi_coded_buffer_proxy_set_user_data (codedbuf_proxy,
389       gst_video_codec_frame_ref (picture->frame),
390       (GDestroyNotify) gst_video_codec_frame_unref);
391
392   if (out_codedbuf_proxy_ptr)
393     *out_codedbuf_proxy_ptr = gst_vaapi_coded_buffer_proxy_ref (codedbuf_proxy);
394   gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
395   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
396
397   /* ERRORS */
398 error_invalid_buffer:
399   {
400     GST_ERROR ("failed to encode the frame");
401     gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
402     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_SURFACE;
403   }
404 }
405
406 /**
407  * gst_vaapi_encoder_flush:
408  * @encoder: a #GstVaapiEncoder
409  *
410  * Submits any pending (reordered) frame for encoding.
411  *
412  * Return value: a #GstVaapiEncoderStatus
413  */
414 GstVaapiEncoderStatus
415 gst_vaapi_encoder_flush (GstVaapiEncoder * encoder)
416 {
417   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
418
419   return klass->flush (encoder);
420 }
421
422 /**
423  * gst_vaapi_encoder_get_codec_data:
424  * @encoder: a #GstVaapiEncoder
425  * @out_codec_data_ptr: the pointer to the resulting codec-data (#GstBuffer)
426  *
427  * Returns a codec-data buffer that best represents the encoded
428  * bitstream. Upon successful return, and if the @out_codec_data_ptr
429  * contents is not NULL, then the caller function shall deallocates
430  * that buffer with gst_buffer_unref().
431  *
432  * Return value: a #GstVaapiEncoderStatus
433  */
434 GstVaapiEncoderStatus
435 gst_vaapi_encoder_get_codec_data (GstVaapiEncoder * encoder,
436     GstBuffer ** out_codec_data_ptr)
437 {
438   GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_SUCCESS;
439   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
440
441   *out_codec_data_ptr = NULL;
442   if (!klass->get_codec_data)
443     return GST_VAAPI_ENCODER_STATUS_SUCCESS;
444
445   ret = klass->get_codec_data (encoder, out_codec_data_ptr);
446   return ret;
447 }
448
449 /* Checks video info */
450 static GstVaapiEncoderStatus
451 check_video_info (GstVaapiEncoder * encoder, const GstVideoInfo * vip)
452 {
453   if (!vip->width || !vip->height)
454     goto error_invalid_resolution;
455   if (!vip->fps_n || !vip->fps_d)
456     goto error_invalid_framerate;
457   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
458
459   /* ERRORS */
460 error_invalid_resolution:
461   {
462     GST_ERROR ("invalid resolution (%dx%d)", vip->width, vip->height);
463     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
464   }
465 error_invalid_framerate:
466   {
467     GST_ERROR ("invalid framerate (%d/%d)", vip->fps_n, vip->fps_d);
468     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
469   }
470 }
471
472 /* Gets a compatible profile for the active codec */
473 static GstVaapiProfile
474 get_compatible_profile (GstVaapiEncoder * encoder)
475 {
476   const GstVaapiEncoderClassData *const cdata =
477       GST_VAAPI_ENCODER_GET_CLASS (encoder)->class_data;
478   GstVaapiProfile profile;
479   GArray *profiles;
480   guint i;
481
482   profiles = gst_vaapi_display_get_encode_profiles (encoder->display);
483   if (!profiles)
484     return GST_VAAPI_PROFILE_UNKNOWN;
485
486   // Pick a profile matching the class codec
487   for (i = 0; i < profiles->len; i++) {
488     profile = g_array_index (profiles, GstVaapiProfile, i);
489     if (gst_vaapi_profile_get_codec (profile) == cdata->codec)
490       break;
491   }
492   if (i == profiles->len)
493     profile = GST_VAAPI_PROFILE_UNKNOWN;
494
495   g_array_unref (profiles);
496   return profile;
497 }
498
499 /* Gets a supported profile for the active codec */
500 static GstVaapiProfile
501 get_profile (GstVaapiEncoder * encoder)
502 {
503   if (!encoder->profile)
504     encoder->profile = get_compatible_profile (encoder);
505   return encoder->profile;
506 }
507
508 /* Gets config attribute for the supplied profile */
509 static gboolean
510 get_config_attribute (GstVaapiEncoder * encoder, VAConfigAttribType type,
511     guint * out_value_ptr)
512 {
513   GstVaapiProfile profile;
514   VAProfile va_profile;
515
516   profile = get_profile (encoder);
517   if (!profile)
518     return FALSE;
519
520   va_profile = gst_vaapi_profile_get_va_profile (profile);
521   return gst_vaapi_get_config_attribute (encoder->display, va_profile,
522       VAEntrypointEncSlice, type, out_value_ptr);
523 }
524
525 /* Determines the set of supported packed headers */
526 static guint
527 get_packed_headers (GstVaapiEncoder * encoder)
528 {
529   const GstVaapiEncoderClassData *const cdata =
530       GST_VAAPI_ENCODER_GET_CLASS (encoder)->class_data;
531   guint value;
532
533   if (encoder->got_packed_headers)
534     return encoder->packed_headers;
535
536   if (!get_config_attribute (encoder, VAConfigAttribEncPackedHeaders, &value))
537     value = 0;
538   GST_INFO ("supported packed headers: 0x%08x", value);
539
540   encoder->got_packed_headers = TRUE;
541   encoder->packed_headers = cdata->packed_headers & value;
542   return encoder->packed_headers;
543 }
544
545 /* Updates video context */
546 static void
547 set_context_info (GstVaapiEncoder * encoder)
548 {
549   GstVaapiContextInfo *const cip = &encoder->context_info;
550   GstVaapiConfigInfoEncoder *const config = &cip->config.encoder;
551
552   cip->usage = GST_VAAPI_CONTEXT_USAGE_ENCODE;
553   cip->profile = encoder->profile;
554   cip->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
555   cip->width = GST_VAAPI_ENCODER_WIDTH (encoder);
556   cip->height = GST_VAAPI_ENCODER_HEIGHT (encoder);
557   cip->ref_frames = encoder->num_ref_frames;
558
559   memset (config, 0, sizeof (*config));
560   config->rc_mode = GST_VAAPI_ENCODER_RATE_CONTROL (encoder);
561   config->packed_headers = get_packed_headers (encoder);
562 }
563
564 /* Ensures the underlying VA context for encoding is created */
565 static gboolean
566 gst_vaapi_encoder_ensure_context (GstVaapiEncoder * encoder)
567 {
568   GstVaapiContextInfo *const cip = &encoder->context_info;
569
570   set_context_info (encoder);
571
572   if (encoder->context) {
573     if (!gst_vaapi_context_reset (encoder->context, cip))
574       return FALSE;
575   } else {
576     encoder->context = gst_vaapi_context_new (encoder->display, cip);
577     if (!encoder->context)
578       return FALSE;
579   }
580   encoder->va_context = gst_vaapi_context_get_id (encoder->context);
581   return TRUE;
582 }
583
584 /* Reconfigures the encoder with the new properties */
585 static GstVaapiEncoderStatus
586 gst_vaapi_encoder_reconfigure_internal (GstVaapiEncoder * encoder)
587 {
588   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
589   GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
590   GstVaapiEncoderStatus status;
591   GstVaapiVideoPool *pool;
592   guint codedbuf_size;
593
594   /* Generate a keyframe every second */
595   if (!encoder->keyframe_period)
596     encoder->keyframe_period = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;
597
598   status = klass->reconfigure (encoder);
599   if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
600     return status;
601
602   if (!gst_vaapi_encoder_ensure_context (encoder))
603     goto error_reset_context;
604
605   codedbuf_size = encoder->codedbuf_pool ?
606       gst_vaapi_coded_buffer_pool_get_buffer_size (GST_VAAPI_CODED_BUFFER_POOL
607       (encoder)) : 0;
608   if (codedbuf_size != encoder->codedbuf_size) {
609     pool = gst_vaapi_coded_buffer_pool_new (encoder, encoder->codedbuf_size);
610     if (!pool)
611       goto error_alloc_codedbuf_pool;
612     gst_vaapi_video_pool_set_capacity (pool, 5);
613     gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, pool);
614     gst_vaapi_video_pool_unref (pool);
615   }
616   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
617
618   /* ERRORS */
619 error_alloc_codedbuf_pool:
620   {
621     GST_ERROR ("failed to initialize coded buffer pool");
622     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
623   }
624 error_reset_context:
625   {
626     GST_ERROR ("failed to update VA context");
627     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
628   }
629 }
630
631 /**
632  * gst_vaapi_encoder_set_codec_state:
633  * @encoder: a #GstVaapiEncoder
634  * @state : a #GstVideoCodecState
635  *
636  * Notifies the encoder about the source surface properties. The
637  * accepted set of properties is: video resolution, colorimetry,
638  * pixel-aspect-ratio and framerate.
639  *
640  * This function is a synchronization point for codec configuration.
641  * This means that, at this point, the encoder is reconfigured to
642  * match the new properties and any other change beyond this point has
643  * zero effect.
644  *
645  * Return value: a #GstVaapiEncoderStatus
646  */
647 GstVaapiEncoderStatus
648 gst_vaapi_encoder_set_codec_state (GstVaapiEncoder * encoder,
649     GstVideoCodecState * state)
650 {
651   GstVaapiEncoderStatus status;
652
653   g_return_val_if_fail (encoder != NULL,
654       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
655   g_return_val_if_fail (state != NULL,
656       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
657
658   if (encoder->num_codedbuf_queued > 0)
659     goto error_operation_failed;
660
661   if (!gst_video_info_is_equal (&state->info, &encoder->video_info)) {
662     status = check_video_info (encoder, &state->info);
663     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
664       return status;
665     encoder->video_info = state->info;
666   }
667   return gst_vaapi_encoder_reconfigure_internal (encoder);
668
669   /* ERRORS */
670 error_operation_failed:
671   {
672     GST_ERROR ("could not change codec state after encoding started");
673     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
674   }
675 }
676
677 /**
678  * gst_vaapi_encoder_set_property:
679  * @encoder: a #GstVaapiEncoder
680  * @prop_id: the id of the property to change
681  * @value: the new value to set
682  *
683  * Update the requested property, designed by @prop_id, with the
684  * supplied @value. A @NULL value argument resets the property to its
685  * default value.
686  *
687  * Return value: a #GstVaapiEncoderStatus
688  */
689 static GstVaapiEncoderStatus
690 set_property (GstVaapiEncoder * encoder, gint prop_id, const GValue * value)
691 {
692   GstVaapiEncoderStatus status =
693       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
694
695   g_assert (value != NULL);
696
697   /* Handle codec-specific properties */
698   if (prop_id < 0) {
699     GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
700
701     if (klass->set_property) {
702       if (encoder->num_codedbuf_queued > 0)
703         goto error_operation_failed;
704       status = klass->set_property (encoder, prop_id, value);
705     }
706     return status;
707   }
708
709   /* Handle common properties */
710   switch (prop_id) {
711     case GST_VAAPI_ENCODER_PROP_RATECONTROL:
712       status = gst_vaapi_encoder_set_rate_control (encoder,
713           g_value_get_enum (value));
714       break;
715     case GST_VAAPI_ENCODER_PROP_BITRATE:
716       status = gst_vaapi_encoder_set_bitrate (encoder,
717           g_value_get_uint (value));
718       break;
719     case GST_VAAPI_ENCODER_PROP_KEYFRAME_PERIOD:
720       status = gst_vaapi_encoder_set_keyframe_period (encoder,
721           g_value_get_uint (value));
722       break;
723     case GST_VAAPI_ENCODER_PROP_TUNE:
724       status = gst_vaapi_encoder_set_tuning (encoder, g_value_get_enum (value));
725       break;
726   }
727   return status;
728
729   /* ERRORS */
730 error_operation_failed:
731   {
732     GST_ERROR ("could not change codec state after encoding started");
733     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
734   }
735 }
736
737 GstVaapiEncoderStatus
738 gst_vaapi_encoder_set_property (GstVaapiEncoder * encoder, gint prop_id,
739     const GValue * value)
740 {
741   GstVaapiEncoderStatus status;
742   GValue default_value = G_VALUE_INIT;
743
744   g_return_val_if_fail (encoder != NULL,
745       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
746
747   if (!value) {
748     GParamSpec *const pspec = prop_find_pspec (encoder, prop_id);
749     if (!pspec)
750       goto error_invalid_property;
751
752     g_value_init (&default_value, pspec->value_type);
753     g_param_value_set_default (pspec, &default_value);
754     value = &default_value;
755   }
756
757   status = set_property (encoder, prop_id, value);
758
759   if (default_value.g_type)
760     g_value_unset (&default_value);
761   return status;
762
763   /* ERRORS */
764 error_invalid_property:
765   {
766     GST_ERROR ("unsupported property (%d)", prop_id);
767     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
768   }
769 }
770
771 /* Determine the supported rate control modes */
772 static guint
773 get_rate_control_mask (GstVaapiEncoder * encoder)
774 {
775   const GstVaapiEncoderClassData *const cdata =
776       GST_VAAPI_ENCODER_GET_CLASS (encoder)->class_data;
777   guint i, value, rate_control_mask = 0;
778
779   if (encoder->got_rate_control_mask)
780     return encoder->rate_control_mask;
781
782   if (get_config_attribute (encoder, VAConfigAttribRateControl, &value)) {
783     for (i = 0; i < 32; i++) {
784       if (!(value & (1U << i)))
785         continue;
786       rate_control_mask |= 1 << to_GstVaapiRateControl (1 << i);
787     }
788   }
789   GST_INFO ("supported rate controls: 0x%08x", rate_control_mask);
790
791   encoder->got_rate_control_mask = TRUE;
792   encoder->rate_control_mask = cdata->rate_control_mask & rate_control_mask;
793   return encoder->rate_control_mask;
794 }
795
796 /**
797  * gst_vaapi_encoder_set_rate_control:
798  * @encoder: a #GstVaapiEncoder
799  * @rate_control: the requested rate control
800  *
801  * Notifies the @encoder to use the supplied @rate_control mode.
802  *
803  * If the underlying encoder does not support that rate control mode,
804  * then @GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_RATE_CONTROL is
805  * returned.
806  *
807  * The rate control mode can only be specified before the first frame
808  * is to be encoded. Afterwards, any change to this parameter is
809  * invalid and @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED is
810  * returned.
811  *
812  * Return value: a #GstVaapiEncoderStatus
813  */
814 GstVaapiEncoderStatus
815 gst_vaapi_encoder_set_rate_control (GstVaapiEncoder * encoder,
816     GstVaapiRateControl rate_control)
817 {
818   guint32 rate_control_mask;
819
820   g_return_val_if_fail (encoder != NULL,
821       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
822
823   if (encoder->rate_control != rate_control && encoder->num_codedbuf_queued > 0)
824     goto error_operation_failed;
825
826   rate_control_mask = get_rate_control_mask (encoder);
827   if (!(rate_control_mask & (1U << rate_control)))
828     goto error_unsupported_rate_control;
829
830   encoder->rate_control = rate_control;
831   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
832
833   /* ERRORS */
834 error_operation_failed:
835   {
836     GST_ERROR ("could not change rate control mode after encoding started");
837     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
838   }
839 error_unsupported_rate_control:
840   {
841     GST_ERROR ("unsupported rate control mode (%d)", rate_control);
842     return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_RATE_CONTROL;
843   }
844 }
845
846 /**
847  * gst_vaapi_encoder_set_bitrate:
848  * @encoder: a #GstVaapiEncoder
849  * @bitrate: the requested bitrate (in kbps)
850  *
851  * Notifies the @encoder to use the supplied @bitrate value.
852  *
853  * Note: currently, the bitrate can only be specified before the first
854  * frame is encoded. Afterwards, any change to this parameter is
855  * invalid and @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED is
856  * returned.
857  *
858  * Return value: a #GstVaapiEncoderStatus
859  */
860 GstVaapiEncoderStatus
861 gst_vaapi_encoder_set_bitrate (GstVaapiEncoder * encoder, guint bitrate)
862 {
863   g_return_val_if_fail (encoder != NULL, 0);
864
865   if (encoder->bitrate != bitrate && encoder->num_codedbuf_queued > 0)
866     goto error_operation_failed;
867
868   encoder->bitrate = bitrate;
869   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
870
871   /* ERRORS */
872 error_operation_failed:
873   {
874     GST_ERROR ("could not change bitrate value after encoding started");
875     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
876   }
877 }
878
879 /**
880  * gst_vaapi_encoder_set_keyframe_period:
881  * @encoder: a #GstVaapiEncoder
882  * @keyframe_period: the maximal distance between two keyframes
883  *
884  * Notifies the @encoder to use the supplied @keyframe_period value.
885  *
886  * Note: currently, the keyframe period can only be specified before
887  * the last call to gst_vaapi_encoder_set_codec_state(), which shall
888  * occur before the first frame is encoded. Afterwards, any change to
889  * this parameter causes gst_vaapi_encoder_set_keyframe_period() to
890  * return @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED.
891  *
892  * Return value: a #GstVaapiEncoderStatus
893  */
894 GstVaapiEncoderStatus
895 gst_vaapi_encoder_set_keyframe_period (GstVaapiEncoder * encoder,
896     guint keyframe_period)
897 {
898   g_return_val_if_fail (encoder != NULL, 0);
899
900   if (encoder->keyframe_period != keyframe_period
901       && encoder->num_codedbuf_queued > 0)
902     goto error_operation_failed;
903
904   encoder->keyframe_period = keyframe_period;
905   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
906
907   /* ERRORS */
908 error_operation_failed:
909   {
910     GST_ERROR ("could not change keyframe period after encoding started");
911     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
912   }
913 }
914
915 /**
916  * gst_vaapi_encoder_set_tuning:
917  * @encoder: a #GstVaapiEncoder
918  * @tuning: the #GstVaapiEncoderTune option
919  *
920  * Notifies the @encoder to use the supplied @tuning option.
921  *
922  * Note: currently, the tuning option can only be specified before the
923  * last call to gst_vaapi_encoder_set_codec_state(), which shall occur
924  * before the first frame is encoded. Afterwards, any change to this
925  * parameter causes gst_vaapi_encoder_set_tuning() to return
926  * @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED.
927  *
928  * Return value: a #GstVaapiEncoderStatus
929  */
930 GstVaapiEncoderStatus
931 gst_vaapi_encoder_set_tuning (GstVaapiEncoder * encoder,
932     GstVaapiEncoderTune tuning)
933 {
934   g_return_val_if_fail (encoder != NULL, 0);
935
936   if (encoder->tune != tuning && encoder->num_codedbuf_queued > 0)
937     goto error_operation_failed;
938
939   encoder->tune = tuning;
940   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
941
942   /* ERRORS */
943 error_operation_failed:
944   {
945     GST_ERROR ("could not change tuning options after encoding started");
946     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
947   }
948 }
949
950 /* Initialize default values for configurable properties */
951 static gboolean
952 gst_vaapi_encoder_init_properties (GstVaapiEncoder * encoder)
953 {
954   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
955   GPtrArray *props;
956   guint i;
957
958   props = klass->get_default_properties ();
959   if (!props)
960     return FALSE;
961
962   encoder->properties = props;
963   for (i = 0; i < props->len; i++) {
964     GstVaapiEncoderPropInfo *const prop = g_ptr_array_index (props, i);
965
966     if (gst_vaapi_encoder_set_property (encoder, prop->prop,
967             NULL) != GST_VAAPI_ENCODER_STATUS_SUCCESS)
968       return FALSE;
969   }
970   return TRUE;
971 }
972
973 /* Base encoder initialization (internal) */
974 static gboolean
975 gst_vaapi_encoder_init (GstVaapiEncoder * encoder, GstVaapiDisplay * display)
976 {
977   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
978
979   g_return_val_if_fail (display != NULL, FALSE);
980
981 #define CHECK_VTABLE_HOOK(FUNC) do {            \
982     if (!klass->FUNC)                           \
983       goto error_invalid_vtable;                \
984   } while (0)
985
986   CHECK_VTABLE_HOOK (init);
987   CHECK_VTABLE_HOOK (finalize);
988   CHECK_VTABLE_HOOK (get_default_properties);
989   CHECK_VTABLE_HOOK (reconfigure);
990   CHECK_VTABLE_HOOK (encode);
991   CHECK_VTABLE_HOOK (reordering);
992   CHECK_VTABLE_HOOK (flush);
993
994 #undef CHECK_VTABLE_HOOK
995
996   encoder->display = gst_vaapi_display_ref (display);
997   encoder->va_display = gst_vaapi_display_get_display (display);
998   encoder->va_context = VA_INVALID_ID;
999
1000   gst_video_info_init (&encoder->video_info);
1001
1002   g_mutex_init (&encoder->mutex);
1003   g_cond_init (&encoder->surface_free);
1004   g_cond_init (&encoder->codedbuf_free);
1005
1006   encoder->codedbuf_queue = g_async_queue_new_full ((GDestroyNotify)
1007       gst_vaapi_coded_buffer_proxy_unref);
1008   if (!encoder->codedbuf_queue)
1009     return FALSE;
1010
1011   if (!klass->init (encoder))
1012     return FALSE;
1013   if (!gst_vaapi_encoder_init_properties (encoder))
1014     return FALSE;
1015   return TRUE;
1016
1017   /* ERRORS */
1018 error_invalid_vtable:
1019   {
1020     GST_ERROR ("invalid subclass hook (internal error)");
1021     return FALSE;
1022   }
1023 }
1024
1025 /* Base encoder cleanup (internal) */
1026 void
1027 gst_vaapi_encoder_finalize (GstVaapiEncoder * encoder)
1028 {
1029   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
1030
1031   klass->finalize (encoder);
1032
1033   gst_vaapi_object_replace (&encoder->context, NULL);
1034   gst_vaapi_display_replace (&encoder->display, NULL);
1035   encoder->va_display = NULL;
1036
1037   if (encoder->properties) {
1038     g_ptr_array_unref (encoder->properties);
1039     encoder->properties = NULL;
1040   }
1041
1042   gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, NULL);
1043   if (encoder->codedbuf_queue) {
1044     g_async_queue_unref (encoder->codedbuf_queue);
1045     encoder->codedbuf_queue = NULL;
1046   }
1047   g_cond_clear (&encoder->surface_free);
1048   g_cond_clear (&encoder->codedbuf_free);
1049   g_mutex_clear (&encoder->mutex);
1050 }
1051
1052 /* Helper function to create new GstVaapiEncoder instances (internal) */
1053 GstVaapiEncoder *
1054 gst_vaapi_encoder_new (const GstVaapiEncoderClass * klass,
1055     GstVaapiDisplay * display)
1056 {
1057   GstVaapiEncoder *encoder;
1058
1059   encoder = (GstVaapiEncoder *)
1060       gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS (klass));
1061   if (!encoder)
1062     return NULL;
1063
1064   if (!gst_vaapi_encoder_init (encoder, display))
1065     goto error;
1066   return encoder;
1067
1068 error:
1069   gst_vaapi_encoder_unref (encoder);
1070   return NULL;
1071 }
1072
1073 /** Returns a GType for the #GstVaapiEncoderTune set */
1074 GType
1075 gst_vaapi_encoder_tune_get_type (void)
1076 {
1077   static volatile gsize g_type = 0;
1078
1079   static const GEnumValue encoder_tune_values[] = {
1080     /* *INDENT-OFF* */
1081     { GST_VAAPI_ENCODER_TUNE_NONE,
1082       "None", "none" },
1083     { GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION,
1084       "High compression", "high-compression" },
1085     { GST_VAAPI_ENCODER_TUNE_LOW_LATENCY,
1086       "Low latency", "low-latency" },
1087     { GST_VAAPI_ENCODER_TUNE_LOW_POWER,
1088       "Low power mode", "low-power" },
1089     { 0, NULL, NULL },
1090     /* *INDENT-ON* */
1091   };
1092
1093   if (g_once_init_enter (&g_type)) {
1094     GType type =
1095         g_enum_register_static ("GstVaapiEncoderTune", encoder_tune_values);
1096     g_once_init_leave (&g_type, type);
1097   }
1098   return g_type;
1099 }