/* VP8 * Copyright (C) 2006 David Schleef * Copyright (C) 2010 Entropy Wave Inc * Copyright (C) 2010-2012 Sebastian Dröge * * 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 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. * */ /** * SECTION:element-vp8enc * @see_also: vp8dec, webmmux, oggmux * * This element encodes raw video into a VP8 stream. * VP8 is a royalty-free * video codec maintained by Google * . It's the successor of On2 VP3, which was the base of the * Theora video codec. * * To control the quality of the encoding, the #GstVP8Enc::target-bitrate, * #GstVP8Enc::min-quantizer, #GstVP8Enc::max-quantizer or #GstVP8Enc::cq-level * properties can be used. Which one is used depends on the mode selected by * the #GstVP8Enc::end-usage property. * See Encoder Parameters * for explanation, examples for useful encoding parameters and more details * on the encoding parameters. * * * Example pipeline * |[ * gst-launch-1.0 -v videotestsrc num-buffers=1000 ! vp8enc ! webmmux ! filesink location=videotestsrc.webm * ]| This example pipeline will encode a test video source to VP8 muxed in an * WebM container. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_VP8_ENCODER /* glib decided in 2.32 it would be a great idea to deprecated GValueArray without * providing an alternative * * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 * */ #define GLIB_DISABLE_DEPRECATION_WARNINGS #include #include #include #include "gstvp8utils.h" #include "gstvp8enc.h" GST_DEBUG_CATEGORY_STATIC (gst_vp8enc_debug); #define GST_CAT_DEFAULT gst_vp8enc_debug typedef struct { vpx_image_t *image; GList *invisible; } GstVP8EncUserData; static void _gst_mini_object_unref0 (GstMiniObject * obj) { if (obj) gst_mini_object_unref (obj); } static void gst_vp8_enc_user_data_free (GstVP8EncUserData * user_data) { if (user_data->image) g_slice_free (vpx_image_t, user_data->image); g_list_foreach (user_data->invisible, (GFunc) _gst_mini_object_unref0, NULL); g_list_free (user_data->invisible); g_slice_free (GstVP8EncUserData, user_data); } /* From vp8/vp8_cx_iface.c */ #define DEFAULT_PROFILE 0 #define DEFAULT_RC_END_USAGE VPX_VBR #define DEFAULT_RC_TARGET_BITRATE 256000 #define DEFAULT_RC_MIN_QUANTIZER 4 #define DEFAULT_RC_MAX_QUANTIZER 63 #define DEFAULT_RC_DROPFRAME_THRESH 0 #define DEFAULT_RC_RESIZE_ALLOWED 0 #define DEFAULT_RC_RESIZE_UP_THRESH 30 #define DEFAULT_RC_RESIZE_DOWN_THRESH 60 #define DEFAULT_RC_UNDERSHOOT_PCT 100 #define DEFAULT_RC_OVERSHOOT_PCT 100 #define DEFAULT_RC_BUF_SZ 6000 #define DEFAULT_RC_BUF_INITIAL_SZ 4000 #define DEFAULT_RC_BUF_OPTIMAL_SZ 5000 #define DEFAULT_RC_2PASS_VBR_BIAS_PCT 50 #define DEFAULT_RC_2PASS_VBR_MINSECTION_PCT 0 #define DEFAULT_RC_2PASS_VBR_MAXSECTION_PCT 400 #define DEFAULT_KF_MODE VPX_KF_AUTO #define DEFAULT_KF_MAX_DIST 128 #define DEFAULT_MULTIPASS_MODE VPX_RC_ONE_PASS #define DEFAULT_MULTIPASS_CACHE_FILE "multipass.cache" #define DEFAULT_TS_NUMBER_LAYERS 1 #define DEFAULT_TS_TARGET_BITRATE NULL #define DEFAULT_TS_RATE_DECIMATOR NULL #define DEFAULT_TS_PERIODICITY 0 #define DEFAULT_TS_LAYER_ID NULL #define DEFAULT_ERROR_RESILIENT 0 #define DEFAULT_LAG_IN_FRAMES 0 #define DEFAULT_THREADS 0 #define DEFAULT_H_SCALING_MODE VP8E_NORMAL #define DEFAULT_V_SCALING_MODE VP8E_NORMAL #define DEFAULT_CPU_USED 0 #define DEFAULT_ENABLE_AUTO_ALT_REF FALSE #define DEFAULT_DEADLINE VPX_DL_BEST_QUALITY #define DEFAULT_NOISE_SENSITIVITY 0 #define DEFAULT_SHARPNESS 0 #define DEFAULT_STATIC_THRESHOLD 0 #define DEFAULT_TOKEN_PARTITIONS 0 #define DEFAULT_ARNR_MAXFRAMES 0 #define DEFAULT_ARNR_STRENGTH 3 #define DEFAULT_ARNR_TYPE 3 #define DEFAULT_TUNING VP8_TUNE_PSNR #define DEFAULT_CQ_LEVEL 10 #define DEFAULT_MAX_INTRA_BITRATE_PCT 0 #define DEFAULT_TIMEBASE_N 0 #define DEFAULT_TIMEBASE_D 1 enum { PROP_0, PROP_RC_END_USAGE, PROP_RC_TARGET_BITRATE, PROP_RC_MIN_QUANTIZER, PROP_RC_MAX_QUANTIZER, PROP_RC_DROPFRAME_THRESH, PROP_RC_RESIZE_ALLOWED, PROP_RC_RESIZE_UP_THRESH, PROP_RC_RESIZE_DOWN_THRESH, PROP_RC_UNDERSHOOT_PCT, PROP_RC_OVERSHOOT_PCT, PROP_RC_BUF_SZ, PROP_RC_BUF_INITIAL_SZ, PROP_RC_BUF_OPTIMAL_SZ, PROP_RC_2PASS_VBR_BIAS_PCT, PROP_RC_2PASS_VBR_MINSECTION_PCT, PROP_RC_2PASS_VBR_MAXSECTION_PCT, PROP_KF_MODE, PROP_KF_MAX_DIST, PROP_TS_NUMBER_LAYERS, PROP_TS_TARGET_BITRATE, PROP_TS_RATE_DECIMATOR, PROP_TS_PERIODICITY, PROP_TS_LAYER_ID, PROP_MULTIPASS_MODE, PROP_MULTIPASS_CACHE_FILE, PROP_ERROR_RESILIENT, PROP_LAG_IN_FRAMES, PROP_THREADS, PROP_DEADLINE, PROP_H_SCALING_MODE, PROP_V_SCALING_MODE, PROP_CPU_USED, PROP_ENABLE_AUTO_ALT_REF, PROP_NOISE_SENSITIVITY, PROP_SHARPNESS, PROP_STATIC_THRESHOLD, PROP_TOKEN_PARTITIONS, PROP_ARNR_MAXFRAMES, PROP_ARNR_STRENGTH, PROP_ARNR_TYPE, PROP_TUNING, PROP_CQ_LEVEL, PROP_MAX_INTRA_BITRATE_PCT, PROP_TIMEBASE }; #define GST_VP8_ENC_END_USAGE_TYPE (gst_vp8_enc_end_usage_get_type()) static GType gst_vp8_enc_end_usage_get_type (void) { static const GEnumValue values[] = { {VPX_VBR, "Variable Bit Rate (VBR) mode", "vbr"}, {VPX_CBR, "Constant Bit Rate (CBR) mode", "cbr"}, {VPX_CQ, "Constant Quality Mode (CQ) mode", "cq"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncEndUsage", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_MULTIPASS_MODE_TYPE (gst_vp8_enc_multipass_mode_get_type()) static GType gst_vp8_enc_multipass_mode_get_type (void) { static const GEnumValue values[] = { {VPX_RC_ONE_PASS, "One pass encoding (default)", "one-pass"}, {VPX_RC_FIRST_PASS, "First pass of multipass encoding", "first-pass"}, {VPX_RC_LAST_PASS, "Last pass of multipass encoding", "last-pass"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncMultipassMode", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_KF_MODE_TYPE (gst_vp8_enc_kf_mode_get_type()) static GType gst_vp8_enc_kf_mode_get_type (void) { static const GEnumValue values[] = { {VPX_KF_AUTO, "Determine optimal placement automatically", "auto"}, {VPX_KF_DISABLED, "Don't automatically place keyframes", "disabled"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncKfMode", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_TUNING_TYPE (gst_vp8_enc_tuning_get_type()) static GType gst_vp8_enc_tuning_get_type (void) { static const GEnumValue values[] = { {VP8_TUNE_PSNR, "Tune for PSNR", "psnr"}, {VP8_TUNE_SSIM, "Tune for SSIM", "ssim"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncTuning", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_SCALING_MODE_TYPE (gst_vp8_enc_scaling_mode_get_type()) static GType gst_vp8_enc_scaling_mode_get_type (void) { static const GEnumValue values[] = { {VP8E_NORMAL, "Normal", "normal"}, {VP8E_FOURFIVE, "4:5", "4:5"}, {VP8E_THREEFIVE, "3:5", "3:5"}, {VP8E_ONETWO, "1:2", "1:2"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncScalingMode", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_TOKEN_PARTITIONS_TYPE (gst_vp8_enc_token_partitions_get_type()) static GType gst_vp8_enc_token_partitions_get_type (void) { static const GEnumValue values[] = { {VP8_ONE_TOKENPARTITION, "One token partition", "1"}, {VP8_TWO_TOKENPARTITION, "Two token partitions", "2"}, {VP8_FOUR_TOKENPARTITION, "Four token partitions", "4"}, {VP8_EIGHT_TOKENPARTITION, "Eight token partitions", "8"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_enum_register_static ("GstVP8EncTokenPartitions", values); g_once_init_leave ((gsize *) & id, _id); } return id; } #define GST_VP8_ENC_ER_FLAGS_TYPE (gst_vp8_enc_er_flags_get_type()) static GType gst_vp8_enc_er_flags_get_type (void) { static const GFlagsValue values[] = { {VPX_ERROR_RESILIENT_DEFAULT, "Default error resilience", "default"}, {VPX_ERROR_RESILIENT_PARTITIONS, "Allow partitions to be decoded independently", "partitions"}, {0, NULL, NULL} }; static volatile GType id = 0; if (g_once_init_enter ((gsize *) & id)) { GType _id; _id = g_flags_register_static ("GstVP8EncErFlags", values); g_once_init_leave ((gsize *) & id, _id); } return id; } static void gst_vp8_enc_finalize (GObject * object); static void gst_vp8_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_vp8_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_vp8_enc_start (GstVideoEncoder * encoder); static gboolean gst_vp8_enc_stop (GstVideoEncoder * encoder); static gboolean gst_vp8_enc_set_format (GstVideoEncoder * video_encoder, GstVideoCodecState * state); static GstFlowReturn gst_vp8_enc_finish (GstVideoEncoder * video_encoder); static gboolean gst_vp8_enc_flush (GstVideoEncoder * video_encoder); static GstFlowReturn gst_vp8_enc_drain (GstVideoEncoder * video_encoder); static GstFlowReturn gst_vp8_enc_handle_frame (GstVideoEncoder * video_encoder, GstVideoCodecFrame * frame); static GstFlowReturn gst_vp8_enc_pre_push (GstVideoEncoder * encoder, GstVideoCodecFrame * frame); static gboolean gst_vp8_enc_sink_event (GstVideoEncoder * video_encoder, GstEvent * event); static gboolean gst_vp8_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query); static GstStaticPadTemplate gst_vp8_enc_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw, " "format = (string) \"I420\", " "width = (int) [1, 16383], " "height = (int) [1, 16383], framerate = (fraction) [ 0/1, MAX ]") ); static GstStaticPadTemplate gst_vp8_enc_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-vp8, " "profile = (string) {0, 1, 2, 3}") ); #define parent_class gst_vp8_enc_parent_class G_DEFINE_TYPE_WITH_CODE (GstVP8Enc, gst_vp8_enc, GST_TYPE_VIDEO_ENCODER, G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL); G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL);); static void gst_vp8_enc_class_init (GstVP8EncClass * klass) { GObjectClass *gobject_class; GstElementClass *element_class; GstVideoEncoderClass *video_encoder_class; gobject_class = G_OBJECT_CLASS (klass); element_class = GST_ELEMENT_CLASS (klass); video_encoder_class = GST_VIDEO_ENCODER_CLASS (klass); gobject_class->set_property = gst_vp8_enc_set_property; gobject_class->get_property = gst_vp8_enc_get_property; gobject_class->finalize = gst_vp8_enc_finalize; gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_vp8_enc_src_template)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_vp8_enc_sink_template)); gst_element_class_set_static_metadata (element_class, "On2 VP8 Encoder", "Codec/Encoder/Video", "Encode VP8 video streams", "David Schleef , " "Sebastian Dröge "); video_encoder_class->start = gst_vp8_enc_start; video_encoder_class->stop = gst_vp8_enc_stop; video_encoder_class->handle_frame = gst_vp8_enc_handle_frame; video_encoder_class->set_format = gst_vp8_enc_set_format; video_encoder_class->flush = gst_vp8_enc_flush; video_encoder_class->finish = gst_vp8_enc_finish; video_encoder_class->pre_push = gst_vp8_enc_pre_push; video_encoder_class->sink_event = gst_vp8_enc_sink_event; video_encoder_class->propose_allocation = gst_vp8_enc_propose_allocation; g_object_class_install_property (gobject_class, PROP_RC_END_USAGE, g_param_spec_enum ("end-usage", "Rate control mode", "Rate control mode", GST_VP8_ENC_END_USAGE_TYPE, DEFAULT_RC_END_USAGE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_TARGET_BITRATE, g_param_spec_int ("target-bitrate", "Target bitrate", "Target bitrate (in bits/sec)", 0, G_MAXINT, DEFAULT_RC_TARGET_BITRATE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_MIN_QUANTIZER, g_param_spec_int ("min-quantizer", "Minimum Quantizer", "Minimum Quantizer (best)", 0, 63, DEFAULT_RC_MIN_QUANTIZER, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_MAX_QUANTIZER, g_param_spec_int ("max-quantizer", "Maximum Quantizer", "Maximum Quantizer (worst)", 0, 63, DEFAULT_RC_MAX_QUANTIZER, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_DROPFRAME_THRESH, g_param_spec_int ("dropframe-threshold", "Drop Frame Threshold", "Temporal resampling threshold (buf %)", 0, 100, DEFAULT_RC_DROPFRAME_THRESH, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_RESIZE_ALLOWED, g_param_spec_boolean ("resize-allowed", "Resize Allowed", "Allow spatial resampling", DEFAULT_RC_RESIZE_ALLOWED, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_RESIZE_UP_THRESH, g_param_spec_int ("resize-up-threshold", "Resize Up Threshold", "Upscale threshold (buf %)", 0, 100, DEFAULT_RC_RESIZE_UP_THRESH, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_RESIZE_DOWN_THRESH, g_param_spec_int ("resize-down-threshold", "Resize Down Threshold", "Downscale threshold (buf %)", 0, 100, DEFAULT_RC_RESIZE_DOWN_THRESH, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_UNDERSHOOT_PCT, g_param_spec_int ("undershoot", "Undershoot PCT", "Datarate undershoot (min) target (%)", 0, 1000, DEFAULT_RC_UNDERSHOOT_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_OVERSHOOT_PCT, g_param_spec_int ("overshoot", "Overshoot PCT", "Datarate overshoot (max) target (%)", 0, 1000, DEFAULT_RC_OVERSHOOT_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_BUF_SZ, g_param_spec_int ("buffer-size", "Buffer size", "Client buffer size (ms)", 0, G_MAXINT, DEFAULT_RC_BUF_SZ, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_BUF_INITIAL_SZ, g_param_spec_int ("buffer-initial-size", "Buffer initial size", "Initial client buffer size (ms)", 0, G_MAXINT, DEFAULT_RC_BUF_INITIAL_SZ, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_BUF_OPTIMAL_SZ, g_param_spec_int ("buffer-optimal-size", "Buffer optimal size", "Optimal client buffer size (ms)", 0, G_MAXINT, DEFAULT_RC_BUF_OPTIMAL_SZ, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_2PASS_VBR_BIAS_PCT, g_param_spec_int ("twopass-vbr-bias", "2-pass VBR bias", "CBR/VBR bias (0=CBR, 100=VBR)", 0, 100, DEFAULT_RC_2PASS_VBR_BIAS_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_2PASS_VBR_MINSECTION_PCT, g_param_spec_int ("twopass-vbr-minsection", "2-pass GOP min bitrate", "GOP minimum bitrate (% target)", 0, G_MAXINT, DEFAULT_RC_2PASS_VBR_MINSECTION_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_RC_2PASS_VBR_MAXSECTION_PCT, g_param_spec_int ("twopass-vbr-maxsection", "2-pass GOP max bitrate", "GOP maximum bitrate (% target)", 0, G_MAXINT, DEFAULT_RC_2PASS_VBR_MINSECTION_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_KF_MODE, g_param_spec_enum ("keyframe-mode", "Keyframe Mode", "Keyframe placement", GST_VP8_ENC_KF_MODE_TYPE, DEFAULT_KF_MODE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_KF_MAX_DIST, g_param_spec_int ("keyframe-max-dist", "Keyframe max distance", "Maximum distance between keyframes (number of frames)", 0, G_MAXINT, DEFAULT_KF_MAX_DIST, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_MULTIPASS_MODE, g_param_spec_enum ("multipass-mode", "Multipass Mode", "Multipass encode mode", GST_VP8_ENC_MULTIPASS_MODE_TYPE, DEFAULT_MULTIPASS_MODE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_MULTIPASS_CACHE_FILE, g_param_spec_string ("multipass-cache-file", "Multipass Cache File", "Multipass cache file", DEFAULT_MULTIPASS_CACHE_FILE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_TS_NUMBER_LAYERS, g_param_spec_int ("temporal-scalability-number-layers", "Number of coding layers", "Number of coding layers to use", 1, 5, DEFAULT_TS_NUMBER_LAYERS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_TS_TARGET_BITRATE, g_param_spec_value_array ("temporal-scalability-target-bitrate", "Coding layer target bitrates", "Target bitrates for coding layers (one per layer, decreasing)", g_param_spec_int ("target-bitrate", "Target bitrate", "Target bitrate", 0, G_MAXINT, DEFAULT_RC_TARGET_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_TS_RATE_DECIMATOR, g_param_spec_value_array ("temporal-scalability-rate-decimator", "Coding layer rate decimator", "Rate decimation factors for each layer", g_param_spec_int ("rate-decimator", "Rate decimator", "Rate decimator", 0, 1000000000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_TS_PERIODICITY, g_param_spec_int ("temporal-scalability-periodicity", "Coding layer periodicity", "Length of sequence that defines layer membership periodicity", 0, 16, DEFAULT_TS_PERIODICITY, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_TS_LAYER_ID, g_param_spec_value_array ("temporal-scalability-layer-id", "Coding layer identification", "Sequence defining coding layer membership", g_param_spec_int ("layer-id", "Layer ID", "Layer ID", 0, 4, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LAG_IN_FRAMES, g_param_spec_int ("lag-in-frames", "Lag in frames", "Maximum number of frames to lag", 0, 25, DEFAULT_LAG_IN_FRAMES, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_ERROR_RESILIENT, g_param_spec_flags ("error-resilient", "Error resilient", "Error resilience flags", GST_VP8_ENC_ER_FLAGS_TYPE, DEFAULT_ERROR_RESILIENT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_THREADS, g_param_spec_int ("threads", "Threads", "Number of threads to use", 0, 64, DEFAULT_THREADS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_DEADLINE, g_param_spec_int64 ("deadline", "Deadline", "Deadline per frame (usec, 0=disabled)", 0, G_MAXINT64, DEFAULT_DEADLINE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_H_SCALING_MODE, g_param_spec_enum ("horizontal-scaling-mode", "Horizontal scaling mode", "Horizontal scaling mode", GST_VP8_ENC_SCALING_MODE_TYPE, DEFAULT_H_SCALING_MODE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_V_SCALING_MODE, g_param_spec_enum ("vertical-scaling-mode", "Vertical scaling mode", "Vertical scaling mode", GST_VP8_ENC_SCALING_MODE_TYPE, DEFAULT_V_SCALING_MODE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_CPU_USED, g_param_spec_int ("cpu-used", "CPU used", "CPU used", -16, 16, DEFAULT_CPU_USED, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_ENABLE_AUTO_ALT_REF, g_param_spec_boolean ("auto-alt-ref", "Auto alt reference frames", "Automatically generate AltRef frames", DEFAULT_ENABLE_AUTO_ALT_REF, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_NOISE_SENSITIVITY, g_param_spec_int ("noise-sensitivity", "Noise sensitivity", "Noise sensisivity (frames to blur)", 0, 6, DEFAULT_NOISE_SENSITIVITY, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_SHARPNESS, g_param_spec_int ("sharpness", "Sharpness", "Filter sharpness", 0, 7, DEFAULT_SHARPNESS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_STATIC_THRESHOLD, g_param_spec_int ("static-threshold", "Static Threshold", "Motion detection threshold", 0, G_MAXINT, DEFAULT_STATIC_THRESHOLD, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_TOKEN_PARTITIONS, g_param_spec_enum ("token-partitions", "Token partitions", "Number of token partitions", GST_VP8_ENC_TOKEN_PARTITIONS_TYPE, DEFAULT_TOKEN_PARTITIONS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_ARNR_MAXFRAMES, g_param_spec_int ("arnr-maxframes", "AltRef max frames", "AltRef maximum number of frames", 0, 15, DEFAULT_ARNR_MAXFRAMES, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_ARNR_STRENGTH, g_param_spec_int ("arnr-strength", "AltRef strength", "AltRef strength", 0, 6, DEFAULT_ARNR_STRENGTH, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_ARNR_TYPE, g_param_spec_int ("arnr-type", "AltRef type", "AltRef type", 1, 3, DEFAULT_ARNR_TYPE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED))); g_object_class_install_property (gobject_class, PROP_TUNING, g_param_spec_enum ("tuning", "Tuning", "Tuning", GST_VP8_ENC_TUNING_TYPE, DEFAULT_TUNING, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_CQ_LEVEL, g_param_spec_int ("cq-level", "Constrained quality level", "Constrained quality level", 0, 63, DEFAULT_CQ_LEVEL, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_MAX_INTRA_BITRATE_PCT, g_param_spec_int ("max-intra-bitrate", "Max Intra bitrate", "Maximum Intra frame bitrate", 0, G_MAXINT, DEFAULT_MAX_INTRA_BITRATE_PCT, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_TIMEBASE, gst_param_spec_fraction ("timebase", "Shortest interframe time", "Fraction of one second that is the shortest interframe time - normally left as zero which will default to the framerate", 0, 1, G_MAXINT, 1, DEFAULT_TIMEBASE_N, DEFAULT_TIMEBASE_D, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); GST_DEBUG_CATEGORY_INIT (gst_vp8enc_debug, "vp8enc", 0, "VP8 Encoder"); } static void gst_vp8_enc_init (GstVP8Enc * gst_vp8_enc) { vpx_codec_err_t status; GST_DEBUG_OBJECT (gst_vp8_enc, "init"); status = vpx_codec_enc_config_default (&vpx_codec_vp8_cx_algo, &gst_vp8_enc->cfg, 0); if (status != VPX_CODEC_OK) { GST_ERROR_OBJECT (gst_vp8_enc, "Failed to get default encoder configuration: %s", gst_vpx_error_name (status)); gst_vp8_enc->have_default_config = FALSE; } else { gst_vp8_enc->have_default_config = TRUE; } gst_vp8_enc->cfg.rc_end_usage = DEFAULT_RC_END_USAGE; gst_vp8_enc->cfg.rc_target_bitrate = DEFAULT_RC_TARGET_BITRATE / 1000; gst_vp8_enc->rc_target_bitrate_set = FALSE; gst_vp8_enc->cfg.rc_min_quantizer = DEFAULT_RC_MIN_QUANTIZER; gst_vp8_enc->cfg.rc_max_quantizer = DEFAULT_RC_MAX_QUANTIZER; gst_vp8_enc->cfg.rc_dropframe_thresh = DEFAULT_RC_DROPFRAME_THRESH; gst_vp8_enc->cfg.rc_resize_allowed = DEFAULT_RC_RESIZE_ALLOWED; gst_vp8_enc->cfg.rc_resize_up_thresh = DEFAULT_RC_RESIZE_UP_THRESH; gst_vp8_enc->cfg.rc_resize_down_thresh = DEFAULT_RC_RESIZE_DOWN_THRESH; gst_vp8_enc->cfg.rc_undershoot_pct = DEFAULT_RC_UNDERSHOOT_PCT; gst_vp8_enc->cfg.rc_overshoot_pct = DEFAULT_RC_OVERSHOOT_PCT; gst_vp8_enc->cfg.rc_buf_sz = DEFAULT_RC_BUF_SZ; gst_vp8_enc->cfg.rc_buf_initial_sz = DEFAULT_RC_BUF_INITIAL_SZ; gst_vp8_enc->cfg.rc_buf_optimal_sz = DEFAULT_RC_BUF_OPTIMAL_SZ; gst_vp8_enc->cfg.rc_2pass_vbr_bias_pct = DEFAULT_RC_2PASS_VBR_BIAS_PCT; gst_vp8_enc->cfg.rc_2pass_vbr_minsection_pct = DEFAULT_RC_2PASS_VBR_MINSECTION_PCT; gst_vp8_enc->cfg.rc_2pass_vbr_maxsection_pct = DEFAULT_RC_2PASS_VBR_MAXSECTION_PCT; gst_vp8_enc->cfg.kf_mode = DEFAULT_KF_MODE; gst_vp8_enc->cfg.kf_max_dist = DEFAULT_KF_MAX_DIST; gst_vp8_enc->cfg.g_pass = DEFAULT_MULTIPASS_MODE; gst_vp8_enc->multipass_cache_file = g_strdup (DEFAULT_MULTIPASS_CACHE_FILE); gst_vp8_enc->cfg.ts_number_layers = DEFAULT_TS_NUMBER_LAYERS; gst_vp8_enc->n_ts_target_bitrate = 0; gst_vp8_enc->n_ts_rate_decimator = 0; gst_vp8_enc->cfg.ts_periodicity = DEFAULT_TS_PERIODICITY; gst_vp8_enc->n_ts_layer_id = 0; gst_vp8_enc->cfg.g_error_resilient = DEFAULT_ERROR_RESILIENT; gst_vp8_enc->cfg.g_lag_in_frames = DEFAULT_LAG_IN_FRAMES; gst_vp8_enc->cfg.g_threads = DEFAULT_THREADS; gst_vp8_enc->deadline = DEFAULT_DEADLINE; gst_vp8_enc->h_scaling_mode = DEFAULT_H_SCALING_MODE; gst_vp8_enc->v_scaling_mode = DEFAULT_V_SCALING_MODE; gst_vp8_enc->cpu_used = DEFAULT_CPU_USED; gst_vp8_enc->enable_auto_alt_ref = DEFAULT_ENABLE_AUTO_ALT_REF; gst_vp8_enc->noise_sensitivity = DEFAULT_NOISE_SENSITIVITY; gst_vp8_enc->sharpness = DEFAULT_SHARPNESS; gst_vp8_enc->static_threshold = DEFAULT_STATIC_THRESHOLD; gst_vp8_enc->token_partitions = DEFAULT_TOKEN_PARTITIONS; gst_vp8_enc->arnr_maxframes = DEFAULT_ARNR_MAXFRAMES; gst_vp8_enc->arnr_strength = DEFAULT_ARNR_STRENGTH; gst_vp8_enc->arnr_type = DEFAULT_ARNR_TYPE; gst_vp8_enc->tuning = DEFAULT_TUNING; gst_vp8_enc->cq_level = DEFAULT_CQ_LEVEL; gst_vp8_enc->max_intra_bitrate_pct = DEFAULT_MAX_INTRA_BITRATE_PCT; gst_vp8_enc->timebase_n = DEFAULT_TIMEBASE_N; gst_vp8_enc->timebase_d = DEFAULT_TIMEBASE_D; gst_vp8_enc->cfg.g_profile = DEFAULT_PROFILE; g_mutex_init (&gst_vp8_enc->encoder_lock); } static void gst_vp8_enc_finalize (GObject * object) { GstVP8Enc *gst_vp8_enc; GST_DEBUG_OBJECT (object, "finalize"); g_return_if_fail (GST_IS_VP8_ENC (object)); gst_vp8_enc = GST_VP8_ENC (object); g_free (gst_vp8_enc->multipass_cache_file); gst_vp8_enc->multipass_cache_file = NULL; if (gst_vp8_enc->input_state) gst_video_codec_state_unref (gst_vp8_enc->input_state); g_mutex_clear (&gst_vp8_enc->encoder_lock); G_OBJECT_CLASS (parent_class)->finalize (object); } static void gst_vp8_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstVP8Enc *gst_vp8_enc; gboolean global = FALSE; vpx_codec_err_t status; g_return_if_fail (GST_IS_VP8_ENC (object)); gst_vp8_enc = GST_VP8_ENC (object); GST_DEBUG_OBJECT (object, "gst_vp8_enc_set_property"); g_mutex_lock (&gst_vp8_enc->encoder_lock); switch (prop_id) { case PROP_RC_END_USAGE: gst_vp8_enc->cfg.rc_end_usage = g_value_get_enum (value); global = TRUE; break; case PROP_RC_TARGET_BITRATE: gst_vp8_enc->cfg.rc_target_bitrate = g_value_get_int (value) / 1000; gst_vp8_enc->rc_target_bitrate_set = TRUE; global = TRUE; break; case PROP_RC_MIN_QUANTIZER: gst_vp8_enc->cfg.rc_min_quantizer = g_value_get_int (value); global = TRUE; break; case PROP_RC_MAX_QUANTIZER: gst_vp8_enc->cfg.rc_max_quantizer = g_value_get_int (value); global = TRUE; break; case PROP_RC_DROPFRAME_THRESH: gst_vp8_enc->cfg.rc_dropframe_thresh = g_value_get_int (value); global = TRUE; break; case PROP_RC_RESIZE_ALLOWED: gst_vp8_enc->cfg.rc_resize_allowed = g_value_get_boolean (value); global = TRUE; break; case PROP_RC_RESIZE_UP_THRESH: gst_vp8_enc->cfg.rc_resize_up_thresh = g_value_get_int (value); global = TRUE; break; case PROP_RC_RESIZE_DOWN_THRESH: gst_vp8_enc->cfg.rc_resize_down_thresh = g_value_get_int (value); global = TRUE; break; case PROP_RC_UNDERSHOOT_PCT: gst_vp8_enc->cfg.rc_undershoot_pct = g_value_get_int (value); global = TRUE; break; case PROP_RC_OVERSHOOT_PCT: gst_vp8_enc->cfg.rc_overshoot_pct = g_value_get_int (value); global = TRUE; break; case PROP_RC_BUF_SZ: gst_vp8_enc->cfg.rc_buf_sz = g_value_get_int (value); global = TRUE; break; case PROP_RC_BUF_INITIAL_SZ: gst_vp8_enc->cfg.rc_buf_initial_sz = g_value_get_int (value); global = TRUE; break; case PROP_RC_BUF_OPTIMAL_SZ: gst_vp8_enc->cfg.rc_buf_optimal_sz = g_value_get_int (value); global = TRUE; break; case PROP_RC_2PASS_VBR_BIAS_PCT: gst_vp8_enc->cfg.rc_2pass_vbr_bias_pct = g_value_get_int (value); global = TRUE; break; case PROP_RC_2PASS_VBR_MINSECTION_PCT: gst_vp8_enc->cfg.rc_2pass_vbr_minsection_pct = g_value_get_int (value); global = TRUE; break; case PROP_RC_2PASS_VBR_MAXSECTION_PCT: gst_vp8_enc->cfg.rc_2pass_vbr_maxsection_pct = g_value_get_int (value); global = TRUE; break; case PROP_KF_MODE: gst_vp8_enc->cfg.kf_mode = g_value_get_enum (value); global = TRUE; break; case PROP_KF_MAX_DIST: gst_vp8_enc->cfg.kf_max_dist = g_value_get_int (value); global = TRUE; break; case PROP_MULTIPASS_MODE: gst_vp8_enc->cfg.g_pass = g_value_get_enum (value); global = TRUE; break; case PROP_MULTIPASS_CACHE_FILE: if (gst_vp8_enc->multipass_cache_file) g_free (gst_vp8_enc->multipass_cache_file); gst_vp8_enc->multipass_cache_file = g_value_dup_string (value); break; case PROP_TS_NUMBER_LAYERS: gst_vp8_enc->cfg.ts_number_layers = g_value_get_int (value); global = TRUE; break; case PROP_TS_TARGET_BITRATE:{ GValueArray *va = g_value_get_boxed (value); memset (&gst_vp8_enc->cfg.ts_target_bitrate, 0, sizeof (gst_vp8_enc->cfg.ts_target_bitrate)); if (va == NULL) { gst_vp8_enc->n_ts_target_bitrate = 0; } else if (va->n_values > VPX_TS_MAX_LAYERS) { g_warning ("%s: Only %d layers allowed at maximum", GST_ELEMENT_NAME (gst_vp8_enc), VPX_TS_MAX_LAYERS); } else { gint i; for (i = 0; i < va->n_values; i++) gst_vp8_enc->cfg.ts_target_bitrate[i] = g_value_get_int (g_value_array_get_nth (va, i)); gst_vp8_enc->n_ts_target_bitrate = va->n_values; } global = TRUE; break; } case PROP_TS_RATE_DECIMATOR:{ GValueArray *va = g_value_get_boxed (value); memset (&gst_vp8_enc->cfg.ts_rate_decimator, 0, sizeof (gst_vp8_enc->cfg.ts_rate_decimator)); if (va == NULL) { gst_vp8_enc->n_ts_rate_decimator = 0; } else if (va->n_values > VPX_TS_MAX_LAYERS) { g_warning ("%s: Only %d layers allowed at maximum", GST_ELEMENT_NAME (gst_vp8_enc), VPX_TS_MAX_LAYERS); } else { gint i; for (i = 0; i < va->n_values; i++) gst_vp8_enc->cfg.ts_rate_decimator[i] = g_value_get_int (g_value_array_get_nth (va, i)); gst_vp8_enc->n_ts_rate_decimator = va->n_values; } global = TRUE; break; } case PROP_TS_PERIODICITY: gst_vp8_enc->cfg.ts_periodicity = g_value_get_int (value); global = TRUE; break; case PROP_TS_LAYER_ID:{ GValueArray *va = g_value_get_boxed (value); memset (&gst_vp8_enc->cfg.ts_layer_id, 0, sizeof (gst_vp8_enc->cfg.ts_layer_id)); if (va && va->n_values > VPX_TS_MAX_PERIODICITY) { g_warning ("%s: Only %d sized layer sequences allowed at maximum", GST_ELEMENT_NAME (gst_vp8_enc), VPX_TS_MAX_PERIODICITY); } else if (va) { gint i; for (i = 0; i < va->n_values; i++) gst_vp8_enc->cfg.ts_layer_id[i] = g_value_get_int (g_value_array_get_nth (va, i)); gst_vp8_enc->n_ts_layer_id = va->n_values; } else { gst_vp8_enc->n_ts_layer_id = 0; } global = TRUE; break; } case PROP_ERROR_RESILIENT: gst_vp8_enc->cfg.g_error_resilient = g_value_get_flags (value); global = TRUE; break; case PROP_LAG_IN_FRAMES: gst_vp8_enc->cfg.g_lag_in_frames = g_value_get_int (value); global = TRUE; break; case PROP_THREADS: gst_vp8_enc->cfg.g_threads = g_value_get_int (value); global = TRUE; break; case PROP_DEADLINE: gst_vp8_enc->deadline = g_value_get_int64 (value); break; case PROP_H_SCALING_MODE: gst_vp8_enc->h_scaling_mode = g_value_get_enum (value); if (gst_vp8_enc->inited) { vpx_scaling_mode_t sm; sm.h_scaling_mode = gst_vp8_enc->h_scaling_mode; sm.v_scaling_mode = gst_vp8_enc->v_scaling_mode; status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_SCALEMODE, &sm); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_SCALEMODE: %s", gst_vpx_error_name (status)); } } break; case PROP_V_SCALING_MODE: gst_vp8_enc->v_scaling_mode = g_value_get_enum (value); if (gst_vp8_enc->inited) { vpx_scaling_mode_t sm; sm.h_scaling_mode = gst_vp8_enc->h_scaling_mode; sm.v_scaling_mode = gst_vp8_enc->v_scaling_mode; status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_SCALEMODE, &sm); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_SCALEMODE: %s", gst_vpx_error_name (status)); } } break; case PROP_CPU_USED: gst_vp8_enc->cpu_used = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_CPUUSED, gst_vp8_enc->cpu_used); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_CPUUSED: %s", gst_vpx_error_name (status)); } } break; case PROP_ENABLE_AUTO_ALT_REF: gst_vp8_enc->enable_auto_alt_ref = g_value_get_boolean (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_ENABLEAUTOALTREF, (gst_vp8_enc->enable_auto_alt_ref ? 1 : 0)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_ENABLEAUTOALTREF: %s", gst_vpx_error_name (status)); } } break; case PROP_NOISE_SENSITIVITY: gst_vp8_enc->noise_sensitivity = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_NOISE_SENSITIVITY, gst_vp8_enc->noise_sensitivity); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_NOISE_SENSITIVITY: %s", gst_vpx_error_name (status)); } } break; case PROP_SHARPNESS: gst_vp8_enc->sharpness = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_SHARPNESS, gst_vp8_enc->sharpness); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_SHARPNESS: %s", gst_vpx_error_name (status)); } } break; case PROP_STATIC_THRESHOLD: gst_vp8_enc->static_threshold = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_STATIC_THRESHOLD, gst_vp8_enc->static_threshold); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_STATIC_THRESHOLD: %s", gst_vpx_error_name (status)); } } break; case PROP_TOKEN_PARTITIONS: gst_vp8_enc->token_partitions = g_value_get_enum (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_TOKEN_PARTITIONS, gst_vp8_enc->token_partitions); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_TOKEN_PARTIONS: %s", gst_vpx_error_name (status)); } } break; case PROP_ARNR_MAXFRAMES: gst_vp8_enc->arnr_maxframes = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_ARNR_MAXFRAMES, gst_vp8_enc->arnr_maxframes); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_ARNR_MAXFRAMES: %s", gst_vpx_error_name (status)); } } break; case PROP_ARNR_STRENGTH: gst_vp8_enc->arnr_strength = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_ARNR_STRENGTH, gst_vp8_enc->arnr_strength); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_ARNR_STRENGTH: %s", gst_vpx_error_name (status)); } } break; case PROP_ARNR_TYPE: gst_vp8_enc->arnr_type = g_value_get_int (value); g_warning ("arnr-type is a no-op since control has been deprecated " "in libvpx"); break; case PROP_TUNING: gst_vp8_enc->tuning = g_value_get_enum (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_TUNING, gst_vp8_enc->tuning); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_TUNING: %s", gst_vpx_error_name (status)); } } break; case PROP_CQ_LEVEL: gst_vp8_enc->cq_level = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_CQ_LEVEL, gst_vp8_enc->cq_level); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_CQ_LEVEL: %s", gst_vpx_error_name (status)); } } break; case PROP_MAX_INTRA_BITRATE_PCT: gst_vp8_enc->max_intra_bitrate_pct = g_value_get_int (value); if (gst_vp8_enc->inited) { status = vpx_codec_control (&gst_vp8_enc->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, gst_vp8_enc->max_intra_bitrate_pct); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (gst_vp8_enc, "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT: %s", gst_vpx_error_name (status)); } } break; case PROP_TIMEBASE: gst_vp8_enc->timebase_n = gst_value_get_fraction_numerator (value); gst_vp8_enc->timebase_d = gst_value_get_fraction_denominator (value); break; default: break; } if (global &&gst_vp8_enc->inited) { status = vpx_codec_enc_config_set (&gst_vp8_enc->encoder, &gst_vp8_enc->cfg); if (status != VPX_CODEC_OK) { g_mutex_unlock (&gst_vp8_enc->encoder_lock); GST_ELEMENT_ERROR (gst_vp8_enc, LIBRARY, INIT, ("Failed to set encoder configuration"), ("%s", gst_vpx_error_name (status))); } else { g_mutex_unlock (&gst_vp8_enc->encoder_lock); } } else { g_mutex_unlock (&gst_vp8_enc->encoder_lock); } } static void gst_vp8_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstVP8Enc *gst_vp8_enc; g_return_if_fail (GST_IS_VP8_ENC (object)); gst_vp8_enc = GST_VP8_ENC (object); g_mutex_lock (&gst_vp8_enc->encoder_lock); switch (prop_id) { case PROP_RC_END_USAGE: g_value_set_enum (value, gst_vp8_enc->cfg.rc_end_usage); break; case PROP_RC_TARGET_BITRATE: g_value_set_int (value, gst_vp8_enc->cfg.rc_target_bitrate * 1000); break; case PROP_RC_MIN_QUANTIZER: g_value_set_int (value, gst_vp8_enc->cfg.rc_min_quantizer); break; case PROP_RC_MAX_QUANTIZER: g_value_set_int (value, gst_vp8_enc->cfg.rc_max_quantizer); break; case PROP_RC_DROPFRAME_THRESH: g_value_set_int (value, gst_vp8_enc->cfg.rc_dropframe_thresh); break; case PROP_RC_RESIZE_ALLOWED: g_value_set_boolean (value, gst_vp8_enc->cfg.rc_resize_allowed); break; case PROP_RC_RESIZE_UP_THRESH: g_value_set_int (value, gst_vp8_enc->cfg.rc_resize_up_thresh); break; case PROP_RC_RESIZE_DOWN_THRESH: g_value_set_int (value, gst_vp8_enc->cfg.rc_resize_down_thresh); break; case PROP_RC_UNDERSHOOT_PCT: g_value_set_int (value, gst_vp8_enc->cfg.rc_undershoot_pct); break; case PROP_RC_OVERSHOOT_PCT: g_value_set_int (value, gst_vp8_enc->cfg.rc_overshoot_pct); break; case PROP_RC_BUF_SZ: g_value_set_int (value, gst_vp8_enc->cfg.rc_buf_sz); break; case PROP_RC_BUF_INITIAL_SZ: g_value_set_int (value, gst_vp8_enc->cfg.rc_buf_initial_sz); break; case PROP_RC_BUF_OPTIMAL_SZ: g_value_set_int (value, gst_vp8_enc->cfg.rc_buf_optimal_sz); break; case PROP_RC_2PASS_VBR_BIAS_PCT: g_value_set_int (value, gst_vp8_enc->cfg.rc_2pass_vbr_bias_pct); break; case PROP_RC_2PASS_VBR_MINSECTION_PCT: g_value_set_int (value, gst_vp8_enc->cfg.rc_2pass_vbr_minsection_pct); break; case PROP_RC_2PASS_VBR_MAXSECTION_PCT: g_value_set_int (value, gst_vp8_enc->cfg.rc_2pass_vbr_maxsection_pct); break; case PROP_KF_MODE: g_value_set_enum (value, gst_vp8_enc->cfg.kf_mode); break; case PROP_KF_MAX_DIST: g_value_set_int (value, gst_vp8_enc->cfg.kf_max_dist); break; case PROP_MULTIPASS_MODE: g_value_set_enum (value, gst_vp8_enc->cfg.g_pass); break; case PROP_MULTIPASS_CACHE_FILE: g_value_set_string (value, gst_vp8_enc->multipass_cache_file); break; case PROP_TS_NUMBER_LAYERS: g_value_set_int (value, gst_vp8_enc->cfg.ts_number_layers); break; case PROP_TS_TARGET_BITRATE:{ GValueArray *va; if (gst_vp8_enc->n_ts_target_bitrate == 0) { g_value_set_boxed (value, NULL); } else { gint i; va = g_value_array_new (gst_vp8_enc->n_ts_target_bitrate); for (i = 0; i < gst_vp8_enc->n_ts_target_bitrate; i++) { GValue v = { 0, }; g_value_init (&v, G_TYPE_INT); g_value_set_int (&v, gst_vp8_enc->cfg.ts_target_bitrate[i]); g_value_array_append (va, &v); g_value_unset (&v); } g_value_set_boxed (value, va); g_value_array_free (va); } break; } case PROP_TS_RATE_DECIMATOR:{ GValueArray *va; if (gst_vp8_enc->n_ts_rate_decimator == 0) { g_value_set_boxed (value, NULL); } else { gint i; va = g_value_array_new (gst_vp8_enc->n_ts_rate_decimator); for (i = 0; i < gst_vp8_enc->n_ts_rate_decimator; i++) { GValue v = { 0, }; g_value_init (&v, G_TYPE_INT); g_value_set_int (&v, gst_vp8_enc->cfg.ts_rate_decimator[i]); g_value_array_append (va, &v); g_value_unset (&v); } g_value_set_boxed (value, va); g_value_array_free (va); } break; } case PROP_TS_PERIODICITY: g_value_set_int (value, gst_vp8_enc->cfg.ts_periodicity); break; case PROP_TS_LAYER_ID:{ GValueArray *va; if (gst_vp8_enc->n_ts_layer_id == 0) { g_value_set_boxed (value, NULL); } else { gint i; va = g_value_array_new (gst_vp8_enc->n_ts_layer_id); for (i = 0; i < gst_vp8_enc->n_ts_layer_id; i++) { GValue v = { 0, }; g_value_init (&v, G_TYPE_INT); g_value_set_int (&v, gst_vp8_enc->cfg.ts_layer_id[i]); g_value_array_append (va, &v); g_value_unset (&v); } g_value_set_boxed (value, va); g_value_array_free (va); } break; } case PROP_ERROR_RESILIENT: g_value_set_flags (value, gst_vp8_enc->cfg.g_error_resilient); break; case PROP_LAG_IN_FRAMES: g_value_set_int (value, gst_vp8_enc->cfg.g_lag_in_frames); break; case PROP_THREADS: g_value_set_int (value, gst_vp8_enc->cfg.g_threads); break; case PROP_DEADLINE: g_value_set_int64 (value, gst_vp8_enc->deadline); break; case PROP_H_SCALING_MODE: g_value_set_enum (value, gst_vp8_enc->h_scaling_mode); break; case PROP_V_SCALING_MODE: g_value_set_enum (value, gst_vp8_enc->v_scaling_mode); break; case PROP_CPU_USED: g_value_set_int (value, gst_vp8_enc->cpu_used); break; case PROP_ENABLE_AUTO_ALT_REF: g_value_set_boolean (value, gst_vp8_enc->enable_auto_alt_ref); break; case PROP_NOISE_SENSITIVITY: g_value_set_int (value, gst_vp8_enc->noise_sensitivity); break; case PROP_SHARPNESS: g_value_set_int (value, gst_vp8_enc->sharpness); break; case PROP_STATIC_THRESHOLD: g_value_set_int (value, gst_vp8_enc->static_threshold); break; case PROP_TOKEN_PARTITIONS: g_value_set_enum (value, gst_vp8_enc->token_partitions); break; case PROP_ARNR_MAXFRAMES: g_value_set_int (value, gst_vp8_enc->arnr_maxframes); break; case PROP_ARNR_STRENGTH: g_value_set_int (value, gst_vp8_enc->arnr_strength); break; case PROP_ARNR_TYPE: g_value_set_int (value, gst_vp8_enc->arnr_type); break; case PROP_TUNING: g_value_set_enum (value, gst_vp8_enc->tuning); break; case PROP_CQ_LEVEL: g_value_set_int (value, gst_vp8_enc->cq_level); break; case PROP_MAX_INTRA_BITRATE_PCT: g_value_set_int (value, gst_vp8_enc->max_intra_bitrate_pct); break; case PROP_TIMEBASE: gst_value_set_fraction (value, gst_vp8_enc->timebase_n, gst_vp8_enc->timebase_d); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } g_mutex_unlock (&gst_vp8_enc->encoder_lock); } static gboolean gst_vp8_enc_start (GstVideoEncoder * video_encoder) { GstVP8Enc *encoder = GST_VP8_ENC (video_encoder); GST_DEBUG_OBJECT (video_encoder, "start"); if (!encoder->have_default_config) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to get default encoder configuration"), (NULL)); return FALSE; } return TRUE; } static void gst_vp8_enc_destroy_encoder (GstVP8Enc * encoder) { g_mutex_lock (&encoder->encoder_lock); if (encoder->inited) { vpx_codec_destroy (&encoder->encoder); encoder->inited = FALSE; } if (encoder->first_pass_cache_content) { g_byte_array_free (encoder->first_pass_cache_content, TRUE); encoder->first_pass_cache_content = NULL; } if (encoder->cfg.rc_twopass_stats_in.buf) { g_free (encoder->cfg.rc_twopass_stats_in.buf); encoder->cfg.rc_twopass_stats_in.buf = NULL; encoder->cfg.rc_twopass_stats_in.sz = 0; } g_mutex_unlock (&encoder->encoder_lock); } static gboolean gst_vp8_enc_stop (GstVideoEncoder * video_encoder) { GstVP8Enc *encoder; GST_DEBUG_OBJECT (video_encoder, "stop"); encoder = GST_VP8_ENC (video_encoder); gst_vp8_enc_destroy_encoder (encoder); gst_tag_setter_reset_tags (GST_TAG_SETTER (encoder)); return TRUE; } static gint gst_vp8_enc_get_downstream_profile (GstVP8Enc * encoder) { GstCaps *allowed; GstStructure *s; gint profile = DEFAULT_PROFILE; allowed = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); if (allowed) { allowed = gst_caps_truncate (allowed); s = gst_caps_get_structure (allowed, 0); if (gst_structure_has_field (s, "profile")) { const GValue *v = gst_structure_get_value (s, "profile"); const gchar *profile_str = NULL; if (GST_VALUE_HOLDS_LIST (v) && gst_value_list_get_size (v) > 0) { profile_str = g_value_get_string (gst_value_list_get_value (v, 0)); } else if (G_VALUE_HOLDS_STRING (v)) { profile_str = g_value_get_string (v); } if (profile_str) { gchar *endptr = NULL; profile = g_ascii_strtoull (profile_str, &endptr, 10); if (*endptr != '\0' || profile < 0 || profile > 3) { GST_ERROR_OBJECT (encoder, "Invalid profile '%s'", profile_str); profile = DEFAULT_PROFILE; } } } gst_caps_unref (allowed); } GST_DEBUG_OBJECT (encoder, "Using profile %d", profile); return profile; } static gboolean gst_vp8_enc_set_format (GstVideoEncoder * video_encoder, GstVideoCodecState * state) { GstVP8Enc *encoder; vpx_codec_err_t status; vpx_image_t *image; guint8 *data = NULL; GstCaps *caps; gboolean ret = TRUE; GstVideoInfo *info = &state->info; GstVideoCodecState *output_state; gchar *profile_str; GstClockTime latency; encoder = GST_VP8_ENC (video_encoder); GST_DEBUG_OBJECT (video_encoder, "set_format"); if (encoder->inited) { gst_vp8_enc_drain (video_encoder); g_mutex_lock (&encoder->encoder_lock); vpx_codec_destroy (&encoder->encoder); encoder->inited = FALSE; } else { g_mutex_lock (&encoder->encoder_lock); } encoder->cfg.g_profile = gst_vp8_enc_get_downstream_profile (encoder); /* Scale default bitrate to our size */ if (!encoder->rc_target_bitrate_set) encoder->cfg.rc_target_bitrate = gst_util_uint64_scale (DEFAULT_RC_TARGET_BITRATE, GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info), 320 * 240 * 1000); encoder->cfg.g_w = GST_VIDEO_INFO_WIDTH (info); encoder->cfg.g_h = GST_VIDEO_INFO_HEIGHT (info); if (encoder->timebase_n != 0 && encoder->timebase_d != 0) { GST_DEBUG_OBJECT (video_encoder, "Using timebase configuration"); encoder->cfg.g_timebase.num = encoder->timebase_n; encoder->cfg.g_timebase.den = encoder->timebase_d; } else { /* Zero framerate and max-framerate but still need to setup the timebase to avoid * a divide by zero error. Presuming the lowest common denominator will be RTP - * VP8 payload draft states clock rate of 90000 which should work for anyone where * FPS < 90000 (shouldn't be too many cases where it's higher) though wouldn't be optimal. RTP specification * http://tools.ietf.org/html/draft-ietf-payload-vp8-01 section 6.3.1 */ encoder->cfg.g_timebase.num = 1; encoder->cfg.g_timebase.den = 90000; } if (encoder->cfg.g_pass == VPX_RC_FIRST_PASS) { if (encoder->first_pass_cache_content == NULL) { encoder->first_pass_cache_content = g_byte_array_sized_new (4096); } } else if (encoder->cfg.g_pass == VPX_RC_LAST_PASS) { GError *err = NULL; if (!encoder->multipass_cache_file) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("No multipass cache file provided"), (NULL)); g_mutex_unlock (&encoder->encoder_lock); return FALSE; } if (encoder->cfg.rc_twopass_stats_in.buf != NULL) { g_free (encoder->cfg.rc_twopass_stats_in.buf); encoder->cfg.rc_twopass_stats_in.buf = NULL; encoder->cfg.rc_twopass_stats_in.sz = 0; } if (!g_file_get_contents (encoder->multipass_cache_file, (gchar **) & encoder->cfg.rc_twopass_stats_in.buf, &encoder->cfg.rc_twopass_stats_in.sz, &err)) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("Failed to read multipass cache file provided"), ("%s", err->message)); g_error_free (err); g_mutex_unlock (&encoder->encoder_lock); return FALSE; } } status = vpx_codec_enc_init (&encoder->encoder, &vpx_codec_vp8_cx_algo, &encoder->cfg, 0); if (status != VPX_CODEC_OK) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to initialize encoder"), ("%s", gst_vpx_error_name (status))); g_mutex_unlock (&encoder->encoder_lock); return FALSE; } { vpx_scaling_mode_t sm; sm.h_scaling_mode = encoder->h_scaling_mode; sm.v_scaling_mode = encoder->v_scaling_mode; status = vpx_codec_control (&encoder->encoder, VP8E_SET_SCALEMODE, &sm); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_SCALEMODE: %s", gst_vpx_error_name (status)); } } status = vpx_codec_control (&encoder->encoder, VP8E_SET_CPUUSED, encoder->cpu_used); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CPUUSED: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_ENABLEAUTOALTREF, (encoder->enable_auto_alt_ref ? 1 : 0)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_ENABLEAUTOALTREF: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_NOISE_SENSITIVITY, encoder->noise_sensitivity); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_NOISE_SENSITIVITY: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_SHARPNESS, encoder->sharpness); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_SHARPNESS: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_STATIC_THRESHOLD, encoder->static_threshold); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_STATIC_THRESHOLD: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_TOKEN_PARTITIONS, encoder->token_partitions); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_TOKEN_PARTIONS: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_MAXFRAMES, encoder->arnr_maxframes); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_ARNR_MAXFRAMES: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_STRENGTH, encoder->arnr_strength); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_ARNR_STRENGTH: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_TUNING, encoder->tuning); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_TUNING: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_CQ_LEVEL, encoder->cq_level); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CQ_LEVEL: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, encoder->max_intra_bitrate_pct); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT: %s", gst_vpx_error_name (status)); } if (GST_VIDEO_INFO_FPS_D (info) == 0 || GST_VIDEO_INFO_FPS_N (info) == 0) { /* FIXME: Assume 25fps for unknown framerates. Better than reporting * that we introduce no latency while we actually do */ latency = gst_util_uint64_scale (encoder->cfg.g_lag_in_frames, 1 * GST_SECOND, 25); } else { latency = gst_util_uint64_scale (encoder->cfg.g_lag_in_frames, GST_VIDEO_INFO_FPS_D (info) * GST_SECOND, GST_VIDEO_INFO_FPS_N (info)); } gst_video_encoder_set_latency (video_encoder, latency, latency); encoder->inited = TRUE; /* Store input state */ if (encoder->input_state) gst_video_codec_state_unref (encoder->input_state); encoder->input_state = gst_video_codec_state_ref (state); /* prepare cached image buffer setup */ image = &encoder->image; memset (image, 0, sizeof (*image)); image->fmt = VPX_IMG_FMT_I420; image->bps = 12; image->x_chroma_shift = image->y_chroma_shift = 1; image->w = image->d_w = GST_VIDEO_INFO_WIDTH (info); image->h = image->d_h = GST_VIDEO_INFO_HEIGHT (info); image->stride[VPX_PLANE_Y] = GST_VIDEO_INFO_COMP_STRIDE (info, 0); image->stride[VPX_PLANE_U] = GST_VIDEO_INFO_COMP_STRIDE (info, 1); image->stride[VPX_PLANE_V] = GST_VIDEO_INFO_COMP_STRIDE (info, 2); profile_str = g_strdup_printf ("%d", encoder->cfg.g_profile); caps = gst_caps_new_simple ("video/x-vp8", "profile", G_TYPE_STRING, profile_str, NULL); g_free (profile_str); { GstStructure *s; GstBuffer *stream_hdr, *vorbiscomment; const GstTagList *iface_tags; GValue array = { 0, }; GValue value = { 0, }; GstMapInfo map; s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); gst_buffer_map (stream_hdr, &map, GST_MAP_WRITE); data = map.data; GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, GST_VIDEO_INFO_WIDTH (info)); GST_WRITE_UINT16_BE (data + 10, GST_VIDEO_INFO_HEIGHT (info)); GST_WRITE_UINT24_BE (data + 12, GST_VIDEO_INFO_PAR_N (info)); GST_WRITE_UINT24_BE (data + 15, GST_VIDEO_INFO_PAR_D (info)); GST_WRITE_UINT32_BE (data + 18, GST_VIDEO_INFO_FPS_N (info)); GST_WRITE_UINT32_BE (data + 22, GST_VIDEO_INFO_FPS_D (info)); gst_buffer_unmap (stream_hdr, &map); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_HEADER); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (iface_tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); } g_mutex_unlock (&encoder->encoder_lock); output_state = gst_video_encoder_set_output_state (video_encoder, caps, state); gst_video_codec_state_unref (output_state); gst_video_encoder_negotiate (GST_VIDEO_ENCODER (encoder)); return ret; } static GstFlowReturn gst_vp8_enc_process (GstVP8Enc * encoder) { vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; GstVideoEncoder *video_encoder; GstVP8EncUserData *user_data; GstVideoCodecFrame *frame; GstFlowReturn ret = GST_FLOW_OK; video_encoder = GST_VIDEO_ENCODER (encoder); g_mutex_lock (&encoder->encoder_lock); pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); while (pkt != NULL) { GstBuffer *buffer; gboolean invisible; GST_DEBUG_OBJECT (encoder, "packet %u type %d", (guint) pkt->data.frame.sz, pkt->kind); if (pkt->kind == VPX_CODEC_STATS_PKT && encoder->cfg.g_pass == VPX_RC_FIRST_PASS) { GST_LOG_OBJECT (encoder, "handling STATS packet"); g_byte_array_append (encoder->first_pass_cache_content, pkt->data.twopass_stats.buf, pkt->data.twopass_stats.sz); frame = gst_video_encoder_get_oldest_frame (video_encoder); if (frame != NULL) { buffer = gst_buffer_new (); GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_LIVE); frame->output_buffer = buffer; g_mutex_unlock (&encoder->encoder_lock); ret = gst_video_encoder_finish_frame (video_encoder, frame); g_mutex_lock (&encoder->encoder_lock); } pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); continue; } else if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) { GST_LOG_OBJECT (encoder, "non frame pkt: %d", pkt->kind); pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); continue; } invisible = (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) != 0; frame = gst_video_encoder_get_oldest_frame (video_encoder); g_assert (frame != NULL); if ((pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0) GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); else GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame); user_data = gst_video_codec_frame_get_user_data (frame); /* FIXME : It would be nice to avoid the memory copy ... */ buffer = gst_buffer_new_wrapped (g_memdup (pkt->data.frame.buf, pkt->data.frame.sz), pkt->data.frame.sz); if (user_data->image) g_slice_free (vpx_image_t, user_data->image); user_data->image = NULL; if (invisible) { user_data->invisible = g_list_append (user_data->invisible, buffer); gst_video_codec_frame_unref (frame); } else { frame->output_buffer = buffer; g_mutex_unlock (&encoder->encoder_lock); ret = gst_video_encoder_finish_frame (video_encoder, frame); g_mutex_lock (&encoder->encoder_lock); } pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); } g_mutex_unlock (&encoder->encoder_lock); return ret; } /* This function should be called holding then stream lock*/ static GstFlowReturn gst_vp8_enc_drain (GstVideoEncoder * video_encoder) { GstVP8Enc *encoder; int flags = 0; vpx_codec_err_t status; gint64 deadline; vpx_codec_pts_t pts; encoder = GST_VP8_ENC (video_encoder); g_mutex_lock (&encoder->encoder_lock); deadline = encoder->deadline; pts = gst_util_uint64_scale (encoder->last_pts, encoder->cfg.g_timebase.den, encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND); status = vpx_codec_encode (&encoder->encoder, NULL, pts, 0, flags, deadline); g_mutex_unlock (&encoder->encoder_lock); if (status != 0) { GST_ERROR_OBJECT (encoder, "encode returned %d %s", status, gst_vpx_error_name (status)); return GST_FLOW_ERROR; } /* dispatch remaining frames */ gst_vp8_enc_process (encoder); g_mutex_lock (&encoder->encoder_lock); if (encoder->cfg.g_pass == VPX_RC_FIRST_PASS && encoder->multipass_cache_file) { GError *err = NULL; if (!g_file_set_contents (encoder->multipass_cache_file, (const gchar *) encoder->first_pass_cache_content->data, encoder->first_pass_cache_content->len, &err)) { GST_ELEMENT_ERROR (encoder, RESOURCE, WRITE, (NULL), ("Failed to write multipass cache file: %s", err->message)); g_error_free (err); } } g_mutex_unlock (&encoder->encoder_lock); return GST_FLOW_OK; } static gboolean gst_vp8_enc_flush (GstVideoEncoder * video_encoder) { GstVP8Enc *encoder; GST_DEBUG_OBJECT (video_encoder, "flush"); encoder = GST_VP8_ENC (video_encoder); gst_vp8_enc_destroy_encoder (encoder); if (encoder->input_state) { gst_video_codec_state_ref (encoder->input_state); gst_vp8_enc_set_format (video_encoder, encoder->input_state); gst_video_codec_state_unref (encoder->input_state); } return TRUE; } static GstFlowReturn gst_vp8_enc_finish (GstVideoEncoder * video_encoder) { GstVP8Enc *encoder; GstFlowReturn ret; GST_DEBUG_OBJECT (video_encoder, "finish"); encoder = GST_VP8_ENC (video_encoder); if (encoder->inited) { ret = gst_vp8_enc_drain (video_encoder); } else { ret = GST_FLOW_OK; } return ret; } static vpx_image_t * gst_vp8_enc_buffer_to_image (GstVP8Enc * enc, GstVideoFrame * frame) { vpx_image_t *image = g_slice_new (vpx_image_t); memcpy (image, &enc->image, sizeof (*image)); image->planes[VPX_PLANE_Y] = GST_VIDEO_FRAME_COMP_DATA (frame, 0); image->planes[VPX_PLANE_U] = GST_VIDEO_FRAME_COMP_DATA (frame, 1); image->planes[VPX_PLANE_V] = GST_VIDEO_FRAME_COMP_DATA (frame, 2); image->stride[VPX_PLANE_Y] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); image->stride[VPX_PLANE_U] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); image->stride[VPX_PLANE_V] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); return image; } static GstFlowReturn gst_vp8_enc_handle_frame (GstVideoEncoder * video_encoder, GstVideoCodecFrame * frame) { GstVP8Enc *encoder; vpx_codec_err_t status; int flags = 0; vpx_image_t *image; GstVP8EncUserData *user_data; GstVideoFrame vframe; vpx_codec_pts_t pts; unsigned long duration; GST_DEBUG_OBJECT (video_encoder, "handle_frame"); encoder = GST_VP8_ENC (video_encoder); GST_DEBUG_OBJECT (video_encoder, "size %d %d", GST_VIDEO_INFO_WIDTH (&encoder->input_state->info), GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info)); gst_video_frame_map (&vframe, &encoder->input_state->info, frame->input_buffer, GST_MAP_READ); image = gst_vp8_enc_buffer_to_image (encoder, &vframe); user_data = g_slice_new0 (GstVP8EncUserData); user_data->image = image; gst_video_codec_frame_set_user_data (frame, user_data, (GDestroyNotify) gst_vp8_enc_user_data_free); if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { flags |= VPX_EFLAG_FORCE_KF; } g_mutex_lock (&encoder->encoder_lock); pts = gst_util_uint64_scale (frame->pts, encoder->cfg.g_timebase.den, encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND); encoder->last_pts = frame->pts; if (frame->duration != GST_CLOCK_TIME_NONE) { duration = gst_util_uint64_scale (frame->duration, encoder->cfg.g_timebase.den, encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND); encoder->last_pts += frame->duration; } else { duration = 1; } status = vpx_codec_encode (&encoder->encoder, image, pts, duration, flags, encoder->deadline); g_mutex_unlock (&encoder->encoder_lock); gst_video_frame_unmap (&vframe); if (status != 0) { GST_ELEMENT_ERROR (encoder, LIBRARY, ENCODE, ("Failed to encode frame"), ("%s", gst_vpx_error_name (status))); gst_video_codec_frame_set_user_data (frame, NULL, NULL); return FALSE; } gst_video_codec_frame_unref (frame); return gst_vp8_enc_process (encoder); } static guint64 _to_granulepos (guint64 frame_end_number, guint inv_count, guint keyframe_dist) { guint64 granulepos; guint inv; inv = (inv_count == 0) ? 0x3 : inv_count - 1; granulepos = (frame_end_number << 32) | (inv << 30) | (keyframe_dist << 3); return granulepos; } static GstFlowReturn gst_vp8_enc_pre_push (GstVideoEncoder * video_encoder, GstVideoCodecFrame * frame) { GstVP8Enc *encoder; GstBuffer *buf; GstFlowReturn ret = GST_FLOW_OK; GstVP8EncUserData *user_data = gst_video_codec_frame_get_user_data (frame); GList *l; gint inv_count; GstVideoInfo *info; GST_DEBUG_OBJECT (video_encoder, "pre_push"); encoder = GST_VP8_ENC (video_encoder); info = &encoder->input_state->info; g_assert (user_data != NULL); for (inv_count = 0, l = user_data->invisible; l; inv_count++, l = l->next) { buf = l->data; l->data = NULL; /* FIXME : All of this should have already been handled by base classes, no ? */ if (l == user_data->invisible && GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) { GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); encoder->keyframe_distance = 0; } else { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); encoder->keyframe_distance++; } GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DECODE_ONLY); GST_BUFFER_TIMESTAMP (buf) = GST_BUFFER_TIMESTAMP (frame->output_buffer); GST_BUFFER_DURATION (buf) = 0; if (GST_VIDEO_INFO_FPS_D (info) == 0 || GST_VIDEO_INFO_FPS_N (info) == 0) { GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; } else { GST_BUFFER_OFFSET_END (buf) = _to_granulepos (frame->presentation_frame_number + 1, inv_count, encoder->keyframe_distance); GST_BUFFER_OFFSET (buf) = gst_util_uint64_scale (frame->presentation_frame_number + 1, GST_SECOND * GST_VIDEO_INFO_FPS_D (info), GST_VIDEO_INFO_FPS_N (info)); } ret = gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (video_encoder), buf); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (encoder, "flow error %d", ret); goto done; } } buf = frame->output_buffer; /* FIXME : All of this should have already been handled by base classes, no ? */ if (!user_data->invisible && GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) { GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); encoder->keyframe_distance = 0; } else { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); encoder->keyframe_distance++; } if (GST_VIDEO_INFO_FPS_D (info) == 0 || GST_VIDEO_INFO_FPS_N (info) == 0) { GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; } else { GST_BUFFER_OFFSET_END (buf) = _to_granulepos (frame->presentation_frame_number + 1, 0, encoder->keyframe_distance); GST_BUFFER_OFFSET (buf) = gst_util_uint64_scale (frame->presentation_frame_number + 1, GST_SECOND * GST_VIDEO_INFO_FPS_D (info), GST_VIDEO_INFO_FPS_N (info)); } GST_LOG_OBJECT (video_encoder, "src ts: %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); done: return ret; } static gboolean gst_vp8_enc_sink_event (GstVideoEncoder * benc, GstEvent * event) { GstVP8Enc *enc = GST_VP8_ENC (benc); /* FIXME : Move this to base encoder class */ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { GstTagList *list; GstTagSetter *setter = GST_TAG_SETTER (enc); const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter); gst_event_parse_tag (event, &list); gst_tag_setter_merge_tags (setter, list, mode); } /* just peeked, baseclass handles the rest */ return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (benc, event); } static gboolean gst_vp8_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query) { gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder, query); } #endif /* HAVE_VP8_ENCODER */