diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/x264/gstencoderbitrateprofilemanager.c | 221 | ||||
-rw-r--r-- | ext/x264/gstencoderbitrateprofilemanager.h | 46 | ||||
-rw-r--r-- | ext/x264/gstx264enc.c | 70 | ||||
-rw-r--r-- | ext/x264/gstx264enc.h | 3 | ||||
-rw-r--r-- | ext/x264/meson.build | 1 |
5 files changed, 329 insertions, 12 deletions
diff --git a/ext/x264/gstencoderbitrateprofilemanager.c b/ext/x264/gstencoderbitrateprofilemanager.c new file mode 100644 index 00000000..ef99b0e2 --- /dev/null +++ b/ext/x264/gstencoderbitrateprofilemanager.c @@ -0,0 +1,221 @@ +/* GStreamer + * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com> + * + * gstencoderbitrateprofilemanager.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstencoderbitrateprofilemanager.h" + +GST_DEBUG_CATEGORY_STATIC (encoderbitratemanager_debug); +#define GST_CAT_DEFAULT encoderbitratemanager_debug + +typedef struct +{ + gchar *name; + gsize n_vals; + GstEncoderBitrateTargetForPixelsMap *map; +} GstEncoderBitrateProfile; + +struct _GstEncoderBitrateProfileManager +{ + GList *profiles; + gchar *preset; + guint bitrate; + + gboolean setting_preset; + gboolean user_bitrate; +}; + +/* *INDENT-OFF* */ +/* Copied from https://support.google.com/youtube/answer/1722171?hl=en */ +static const GstEncoderBitrateTargetForPixelsMap youtube_bitrate_profiles[] = { + { + .n_pixels = 3840 * 2160, + .low_framerate_bitrate = 40000, + .high_framerate_bitrate = 60000, + }, + { + .n_pixels = 2560 * 1440, + .low_framerate_bitrate = 16000, + .high_framerate_bitrate = 24000, + }, + { + .n_pixels = 1920 * 1080, + .low_framerate_bitrate = 8000, + .high_framerate_bitrate = 12000, + }, + { + .n_pixels = 1080 * 720, + .low_framerate_bitrate = 5000, + .high_framerate_bitrate = 7500, + }, + { + .n_pixels = 640 * 480, + .low_framerate_bitrate = 2500, + .high_framerate_bitrate = 4000, + }, + { + .n_pixels = 0, + .low_framerate_bitrate = 2500, + .high_framerate_bitrate = 4000, + }, + { + .n_pixels = 0, + .low_framerate_bitrate = 0, + .high_framerate_bitrate = 0, + }, +}; +/* *INDENT-ON* */ + +static void +gst_encoder_bitrate_profile_free (GstEncoderBitrateProfile * profile) +{ + g_free (profile->name); + g_free (profile->map); + g_free (profile); +} + +void +gst_encoder_bitrate_profile_manager_add_profile (GstEncoderBitrateProfileManager + * self, const gchar * profile_name, + const GstEncoderBitrateTargetForPixelsMap * map) +{ + gint n_vals; + GstEncoderBitrateProfile *profile; + + for (n_vals = 0; + map[n_vals].low_framerate_bitrate != 0 + && map[n_vals].high_framerate_bitrate != 0; n_vals++); + n_vals++; + + profile = g_new0 (GstEncoderBitrateProfile, 1); + profile->name = g_strdup (profile_name); + profile->n_vals = n_vals; + profile->map + = g_memdup (map, sizeof (GstEncoderBitrateTargetForPixelsMap) * n_vals); + self->profiles = g_list_prepend (self->profiles, profile); +} + +guint +gst_encoder_bitrate_profile_manager_get_bitrate (GstEncoderBitrateProfileManager + * self, GstVideoInfo * info) +{ + gint i; + gboolean high_fps; + guint num_pix; + GList *tmp; + + GstEncoderBitrateProfile *profile = NULL; + + g_return_val_if_fail (self != NULL, -1); + + if (!info || info->finfo == NULL + || info->finfo->format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_INFO ("Video info %p not usable, returning current bitrate", info); + return self->bitrate; + } + + if (!self->preset) { + GST_INFO ("No preset used, returning current bitrate"); + return self->bitrate; + + } + + for (tmp = self->profiles; tmp; tmp = tmp->next) { + GstEncoderBitrateProfile *tmpprof = tmp->data; + if (!g_strcmp0 (tmpprof->name, self->preset)) { + profile = tmpprof; + break; + } + } + + if (!profile) { + GST_INFO ("Could not find map for profile: %s", self->preset); + + return self->bitrate; + } + + high_fps = GST_VIDEO_INFO_FPS_N (info) / GST_VIDEO_INFO_FPS_D (info) > 30.0; + num_pix = GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info); + for (i = 0; i < profile->n_vals; i++) { + GstEncoderBitrateTargetForPixelsMap *bitrate_values = &profile->map[i]; + + if (num_pix < bitrate_values->n_pixels) + continue; + + self->bitrate = + high_fps ? bitrate_values-> + high_framerate_bitrate : bitrate_values->low_framerate_bitrate; + GST_INFO ("Using %s bitrate! %d", self->preset, self->bitrate); + return self->bitrate; + } + + return -1; +} + +void gst_encoder_bitrate_profile_manager_start_loading_preset + (GstEncoderBitrateProfileManager * self) +{ + self->setting_preset = TRUE; +} + +void gst_encoder_bitrate_profile_manager_end_loading_preset + (GstEncoderBitrateProfileManager * self, const gchar * preset) +{ + self->setting_preset = FALSE; + g_free (self->preset); + self->preset = g_strdup (preset); +} + +void +gst_encoder_bitrate_profile_manager_set_bitrate (GstEncoderBitrateProfileManager + * self, guint bitrate) +{ + self->bitrate = bitrate; + self->user_bitrate = !self->setting_preset; +} + +void +gst_encoder_bitrate_profile_manager_free (GstEncoderBitrateProfileManager * + self) +{ + g_free (self->preset); + g_list_free_full (self->profiles, + (GDestroyNotify) gst_encoder_bitrate_profile_free); + g_free (self); +} + +GstEncoderBitrateProfileManager * +gst_encoder_bitrate_profile_manager_new (guint default_bitrate) +{ + GstEncoderBitrateProfileManager *self = + g_new0 (GstEncoderBitrateProfileManager, 1); + static volatile gsize _init = 0; + + if (g_once_init_enter (&_init)) { + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "encoderbitratemanager", 0, + "Encoder bitrate manager"); + g_once_init_leave (&_init, 1); + } + + self->bitrate = default_bitrate; + gst_encoder_bitrate_profile_manager_add_profile (self, + "Profile YouTube", youtube_bitrate_profiles); + + return self; +} diff --git a/ext/x264/gstencoderbitrateprofilemanager.h b/ext/x264/gstencoderbitrateprofilemanager.h new file mode 100644 index 00000000..d6db733b --- /dev/null +++ b/ext/x264/gstencoderbitrateprofilemanager.h @@ -0,0 +1,46 @@ +/* GStreamer + * Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com> + * + * gstencoderbitrateprofilemanager.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include <gst/gst.h> +#include <gst/video/video.h> + +typedef struct _GstEncoderBitrateProfileManager GstEncoderBitrateProfileManager; + +typedef struct _GstEncoderBitrateTargetForPixelsMap +{ + guint n_pixels; + guint low_framerate_bitrate; + guint high_framerate_bitrate; + + gpointer _gst_reserved[GST_PADDING_LARGE]; +} GstEncoderBitrateTargetForPixelsMap; + +void +gst_encoder_bitrate_profile_manager_add_profile(GstEncoderBitrateProfileManager* self, + const gchar* profile_name, const GstEncoderBitrateTargetForPixelsMap* map); +guint gst_encoder_bitrate_profile_manager_get_bitrate(GstEncoderBitrateProfileManager* self, GstVideoInfo* info); +void gst_encoder_bitrate_profile_manager_start_loading_preset (GstEncoderBitrateProfileManager* self); +void gst_encoder_bitrate_profile_manager_end_loading_preset(GstEncoderBitrateProfileManager* self, const gchar* preset); +void gst_encoder_bitrate_profile_manager_set_bitrate(GstEncoderBitrateProfileManager* self, guint bitrate); +GstEncoderBitrateProfileManager* gst_encoder_bitrate_profile_manager_new(guint default_bitrate); +void gst_encoder_bitrate_profile_manager_free(GstEncoderBitrateProfileManager* self);
\ No newline at end of file diff --git a/ext/x264/gstx264enc.c b/ext/x264/gstx264enc.c index 7cf7fdcd..ef1c28f2 100644 --- a/ext/x264/gstx264enc.c +++ b/ext/x264/gstx264enc.c @@ -724,9 +724,36 @@ static void gst_x264_enc_set_property (GObject * object, guint prop_id, static void gst_x264_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +typedef gboolean (*LoadPresetFunc) (GstPreset * preset, const gchar * name); + +LoadPresetFunc parent_load_preset = NULL; + +static gboolean +gst_x264_enc_load_preset (GstPreset * preset, const gchar * name) +{ + GstX264Enc *enc = GST_X264_ENC (preset); + gboolean res; + + gst_encoder_bitrate_profile_manager_start_loading_preset + (enc->bitrate_manager); + res = parent_load_preset (preset, name); + gst_encoder_bitrate_profile_manager_end_loading_preset (enc->bitrate_manager, + res ? name : NULL); + + return res; +} + +static void +gst_x264_enc_preset_interface_init (GstPresetInterface * iface) +{ + parent_load_preset = iface->load_preset; + iface->load_preset = gst_x264_enc_load_preset; +} + #define gst_x264_enc_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstX264Enc, gst_x264_enc, GST_TYPE_VIDEO_ENCODER, - G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL)); + G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, + gst_x264_enc_preset_interface_init)); /* don't forget to free the string after use */ static const gchar * @@ -1227,7 +1254,6 @@ gst_x264_enc_init (GstX264Enc * encoder) encoder->quantizer = ARG_QUANTIZER_DEFAULT; encoder->mp_cache_file = g_strdup (ARG_MULTIPASS_CACHE_FILE_DEFAULT); encoder->byte_stream = ARG_BYTE_STREAM_DEFAULT; - encoder->bitrate = ARG_BITRATE_DEFAULT; encoder->intra_refresh = ARG_INTRA_REFRESH_DEFAULT; encoder->vbv_buf_capacity = ARG_VBV_BUF_CAPACITY_DEFAULT; encoder->me = ARG_ME_DEFAULT; @@ -1260,6 +1286,9 @@ gst_x264_enc_init (GstX264Enc * encoder) encoder->tune = ARG_TUNE_DEFAULT; encoder->frame_packing = ARG_FRAME_PACKING_DEFAULT; encoder->insert_vui = ARG_INSERT_VUI_DEFAULT; + + encoder->bitrate_manager = + gst_encoder_bitrate_profile_manager_new (ARG_BITRATE_DEFAULT); } typedef struct @@ -1384,6 +1413,7 @@ gst_x264_enc_finalize (GObject * object) FREE_STRING (encoder->tunings); FREE_STRING (encoder->option_string); FREE_STRING (encoder->option_string_prop); + gst_encoder_bitrate_profile_manager_free (encoder->bitrate_manager); #undef FREE_STRING @@ -1497,6 +1527,7 @@ gst_x264_enc_init_encoder (GstX264Enc * encoder) { guint pass = 0; GstVideoInfo *info; + guint bitrate; if (!encoder->input_state) { GST_DEBUG_OBJECT (encoder, "Have no input state yet"); @@ -1661,6 +1692,10 @@ skip_vui_parameters: encoder->x264param.analyse.b_psnr = 0; + bitrate = + gst_encoder_bitrate_profile_manager_get_bitrate (encoder->bitrate_manager, + encoder->input_state ? &encoder->input_state->info : NULL); + /* FIXME 2.0 make configuration more sane and consistent with x264 cmdline: * + split pass property into a pass property (pass1/2/3 enum) and rc-method * + bitrate property should only be used in case of CBR method @@ -1677,7 +1712,7 @@ skip_vui_parameters: case GST_X264_ENC_PASS_QUAL: encoder->x264param.rc.i_rc_method = X264_RC_CRF; encoder->x264param.rc.f_rf_constant = encoder->quantizer; - encoder->x264param.rc.i_vbv_max_bitrate = encoder->bitrate; + encoder->x264param.rc.i_vbv_max_bitrate = bitrate; encoder->x264param.rc.i_vbv_buffer_size = encoder->x264param.rc.i_vbv_max_bitrate * encoder->vbv_buf_capacity / 1000; @@ -1688,8 +1723,8 @@ skip_vui_parameters: case GST_X264_ENC_PASS_PASS3: default: encoder->x264param.rc.i_rc_method = X264_RC_ABR; - encoder->x264param.rc.i_bitrate = encoder->bitrate; - encoder->x264param.rc.i_vbv_max_bitrate = encoder->bitrate; + encoder->x264param.rc.i_bitrate = bitrate; + encoder->x264param.rc.i_vbv_max_bitrate = bitrate; encoder->x264param.rc.i_vbv_buffer_size = encoder->x264param.rc.i_vbv_max_bitrate * encoder->vbv_buf_capacity / 1000; @@ -2031,6 +2066,9 @@ gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstCaps * caps) GstStructure *structure; GstVideoCodecState *state; GstTagList *tags; + guint bitrate = + gst_encoder_bitrate_profile_manager_get_bitrate (encoder->bitrate_manager, + encoder->input_state ? &encoder->input_state->info : NULL); outcaps = gst_caps_new_empty_simple ("video/x-h264"); structure = gst_caps_get_structure (outcaps, 0); @@ -2099,8 +2137,8 @@ gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstCaps * caps) tags = gst_tag_list_new_empty (); gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "x264", GST_TAG_ENCODER_VERSION, X264_BUILD, - GST_TAG_MAXIMUM_BITRATE, encoder->bitrate * 1024, - GST_TAG_NOMINAL_BITRATE, encoder->bitrate * 1024, NULL); + GST_TAG_MAXIMUM_BITRATE, bitrate * 1024, + GST_TAG_NOMINAL_BITRATE, bitrate * 1024, NULL); gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder), tags, GST_TAG_MERGE_REPLACE); gst_tag_list_unref (tags); @@ -2545,13 +2583,18 @@ gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send) static void gst_x264_enc_reconfig (GstX264Enc * encoder) { + guint bitrate; + if (!encoder->vtable) return; + bitrate = + gst_encoder_bitrate_profile_manager_get_bitrate (encoder->bitrate_manager, + encoder->input_state ? &encoder->input_state->info : NULL); switch (encoder->pass) { case GST_X264_ENC_PASS_QUAL: encoder->x264param.rc.f_rf_constant = encoder->quantizer; - encoder->x264param.rc.i_vbv_max_bitrate = encoder->bitrate; + encoder->x264param.rc.i_vbv_max_bitrate = bitrate; encoder->x264param.rc.i_vbv_buffer_size = encoder->x264param.rc.i_vbv_max_bitrate * encoder->vbv_buf_capacity / 1000; @@ -2561,8 +2604,8 @@ gst_x264_enc_reconfig (GstX264Enc * encoder) case GST_X264_ENC_PASS_PASS2: case GST_X264_ENC_PASS_PASS3: default: - encoder->x264param.rc.i_bitrate = encoder->bitrate; - encoder->x264param.rc.i_vbv_max_bitrate = encoder->bitrate; + encoder->x264param.rc.i_bitrate = bitrate; + encoder->x264param.rc.i_vbv_max_bitrate = bitrate; encoder->x264param.rc.i_vbv_buffer_size = encoder->x264param.rc.i_vbv_max_bitrate * encoder->vbv_buf_capacity / 1000; @@ -2601,7 +2644,8 @@ gst_x264_enc_set_property (GObject * object, guint prop_id, gst_x264_enc_reconfig (encoder); break; case ARG_BITRATE: - encoder->bitrate = g_value_get_uint (value); + gst_encoder_bitrate_profile_manager_set_bitrate (encoder->bitrate_manager, + g_value_get_uint (value)); gst_x264_enc_reconfig (encoder); break; case ARG_VBV_BUF_CAPACITY: @@ -2820,7 +2864,9 @@ gst_x264_enc_get_property (GObject * object, guint prop_id, g_value_set_boolean (value, encoder->byte_stream); break; case ARG_BITRATE: - g_value_set_uint (value, encoder->bitrate); + g_value_set_uint (value, + gst_encoder_bitrate_profile_manager_get_bitrate + (encoder->bitrate_manager, NULL)); break; case ARG_INTRA_REFRESH: g_value_set_boolean (value, encoder->intra_refresh); diff --git a/ext/x264/gstx264enc.h b/ext/x264/gstx264enc.h index c7de48cf..e1a091fc 100644 --- a/ext/x264/gstx264enc.h +++ b/ext/x264/gstx264enc.h @@ -25,6 +25,7 @@ #include <gst/gst.h> #include <gst/video/video.h> #include <gst/video/gstvideoencoder.h> +#include "gstencoderbitrateprofilemanager.h" #ifdef HAVE_STDINT_H #include <stdint.h> @@ -126,6 +127,8 @@ struct _GstX264Enc /* cached values to set x264_picture_t */ gint x264_nplanes; + + GstEncoderBitrateProfileManager *bitrate_manager; }; struct _GstX264EncClass diff --git a/ext/x264/meson.build b/ext/x264/meson.build index 99a69202..fdbfeebc 100644 --- a/ext/x264/meson.build +++ b/ext/x264/meson.build @@ -1,5 +1,6 @@ x264_sources = [ 'gstx264enc.c', + 'gstencoderbitrateprofilemanager.c', ] x264_dep = dependency('x264', required : get_option('x264'), |