gstreamer/ext/vpx/gstvpxenc.c
George Kiagiadakis 36fc2a747a vpxenc: discard frames that have been dropped by libvpx
This fixes a memory leak. When dropframe-threshold has been set,
libvpx may output less frames than the input ones, which causes
some GstVideoCodecFrames to queue up in GstVideoEncoder's internal
frame queue with no chance of ever being all released. And because
the frames keep references to the input buffers, the input buffer
pool keeps allocating new buffers and memory usage grows very fast.
For example the following pipeline's memory usage grows at a rate
of about 1GB per minute!

videotestsrc ! capsfilter caps=video/x-raw,width=1920,height=1080,framerate=30/1,format=I420 ! \
  vp8enc target-bitrate=1000000 end-usage=cbr dropframe-threshold=95 ! fakesink

https://bugzilla.gnome.org/show_bug.cgi?id=783086
2017-08-11 14:32:37 +03:00

1968 lines
67 KiB
C

/* VPX
* Copyright (C) 2006 David Schleef <ds@schleef.org>
* Copyright (C) 2010 Entropy Wave Inc
* Copyright (C) 2010-2012 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* 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.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_VP8_ENCODER) || defined(HAVE_VP9_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 <gst/tag/tag.h>
#include <gst/video/video.h>
#include <string.h>
#include "gstvp8utils.h"
#include "gstvpxenc.h"
GST_DEBUG_CATEGORY_STATIC (gst_vpxenc_debug);
#define GST_CAT_DEFAULT gst_vpxenc_debug
/* From vp8/vp8_cx_iface.c and vp9/vp9_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_VPX_ENC_END_USAGE_TYPE (gst_vpx_enc_end_usage_get_type())
static GType
gst_vpx_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 ("GstVPXEncEndUsage", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_MULTIPASS_MODE_TYPE (gst_vpx_enc_multipass_mode_get_type())
static GType
gst_vpx_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 ("GstVPXEncMultipassMode", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_KF_MODE_TYPE (gst_vpx_enc_kf_mode_get_type())
static GType
gst_vpx_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 ("GstVPXEncKfMode", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_TUNING_TYPE (gst_vpx_enc_tuning_get_type())
static GType
gst_vpx_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 ("GstVPXEncTuning", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_SCALING_MODE_TYPE (gst_vpx_enc_scaling_mode_get_type())
static GType
gst_vpx_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 ("GstVPXEncScalingMode", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_TOKEN_PARTITIONS_TYPE (gst_vpx_enc_token_partitions_get_type())
static GType
gst_vpx_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 ("GstVPXEncTokenPartitions", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define GST_VPX_ENC_ER_FLAGS_TYPE (gst_vpx_enc_er_flags_get_type())
static GType
gst_vpx_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 ("GstVPXEncErFlags", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
static void gst_vpx_enc_finalize (GObject * object);
static void gst_vpx_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_vpx_enc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_vpx_enc_start (GstVideoEncoder * encoder);
static gboolean gst_vpx_enc_stop (GstVideoEncoder * encoder);
static gboolean gst_vpx_enc_set_format (GstVideoEncoder *
video_encoder, GstVideoCodecState * state);
static GstFlowReturn gst_vpx_enc_finish (GstVideoEncoder * video_encoder);
static gboolean gst_vpx_enc_flush (GstVideoEncoder * video_encoder);
static GstFlowReturn gst_vpx_enc_drain (GstVideoEncoder * video_encoder);
static GstFlowReturn gst_vpx_enc_handle_frame (GstVideoEncoder *
video_encoder, GstVideoCodecFrame * frame);
static gboolean gst_vpx_enc_sink_event (GstVideoEncoder *
video_encoder, GstEvent * event);
static gboolean gst_vpx_enc_propose_allocation (GstVideoEncoder * encoder,
GstQuery * query);
#define parent_class gst_vpx_enc_parent_class
G_DEFINE_TYPE_WITH_CODE (GstVPXEnc, gst_vpx_enc, GST_TYPE_VIDEO_ENCODER,
G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL);
G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL););
static void
gst_vpx_enc_class_init (GstVPXEncClass * klass)
{
GObjectClass *gobject_class;
GstVideoEncoderClass *video_encoder_class;
gobject_class = G_OBJECT_CLASS (klass);
video_encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
gobject_class->set_property = gst_vpx_enc_set_property;
gobject_class->get_property = gst_vpx_enc_get_property;
gobject_class->finalize = gst_vpx_enc_finalize;
video_encoder_class->start = gst_vpx_enc_start;
video_encoder_class->stop = gst_vpx_enc_stop;
video_encoder_class->handle_frame = gst_vpx_enc_handle_frame;
video_encoder_class->set_format = gst_vpx_enc_set_format;
video_encoder_class->flush = gst_vpx_enc_flush;
video_encoder_class->finish = gst_vpx_enc_finish;
video_encoder_class->sink_event = gst_vpx_enc_sink_event;
video_encoder_class->propose_allocation = gst_vpx_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_VPX_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_VPX_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_VPX_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. "
"If stream caps reinited, multiple files will be created: "
"file, file.1, file.2, ... and so on.",
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_VPX_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_VPX_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_VPX_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_VPX_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_VPX_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_vpxenc_debug, "vpxenc", 0, "VPX Encoder");
}
static void
gst_vpx_enc_init (GstVPXEnc * gst_vpx_enc)
{
GST_DEBUG_OBJECT (gst_vpx_enc, "init");
GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (gst_vpx_enc));
gst_vpx_enc->cfg.rc_end_usage = DEFAULT_RC_END_USAGE;
gst_vpx_enc->cfg.rc_target_bitrate = DEFAULT_RC_TARGET_BITRATE / 1000;
gst_vpx_enc->rc_target_bitrate_set = FALSE;
gst_vpx_enc->cfg.rc_min_quantizer = DEFAULT_RC_MIN_QUANTIZER;
gst_vpx_enc->cfg.rc_max_quantizer = DEFAULT_RC_MAX_QUANTIZER;
gst_vpx_enc->cfg.rc_dropframe_thresh = DEFAULT_RC_DROPFRAME_THRESH;
gst_vpx_enc->cfg.rc_resize_allowed = DEFAULT_RC_RESIZE_ALLOWED;
gst_vpx_enc->cfg.rc_resize_up_thresh = DEFAULT_RC_RESIZE_UP_THRESH;
gst_vpx_enc->cfg.rc_resize_down_thresh = DEFAULT_RC_RESIZE_DOWN_THRESH;
gst_vpx_enc->cfg.rc_undershoot_pct = DEFAULT_RC_UNDERSHOOT_PCT;
gst_vpx_enc->cfg.rc_overshoot_pct = DEFAULT_RC_OVERSHOOT_PCT;
gst_vpx_enc->cfg.rc_buf_sz = DEFAULT_RC_BUF_SZ;
gst_vpx_enc->cfg.rc_buf_initial_sz = DEFAULT_RC_BUF_INITIAL_SZ;
gst_vpx_enc->cfg.rc_buf_optimal_sz = DEFAULT_RC_BUF_OPTIMAL_SZ;
gst_vpx_enc->cfg.rc_2pass_vbr_bias_pct = DEFAULT_RC_2PASS_VBR_BIAS_PCT;
gst_vpx_enc->cfg.rc_2pass_vbr_minsection_pct =
DEFAULT_RC_2PASS_VBR_MINSECTION_PCT;
gst_vpx_enc->cfg.rc_2pass_vbr_maxsection_pct =
DEFAULT_RC_2PASS_VBR_MAXSECTION_PCT;
gst_vpx_enc->cfg.kf_mode = DEFAULT_KF_MODE;
gst_vpx_enc->cfg.kf_max_dist = DEFAULT_KF_MAX_DIST;
gst_vpx_enc->cfg.g_pass = DEFAULT_MULTIPASS_MODE;
gst_vpx_enc->multipass_cache_prefix = g_strdup (DEFAULT_MULTIPASS_CACHE_FILE);
gst_vpx_enc->multipass_cache_file = NULL;
gst_vpx_enc->multipass_cache_idx = 0;
gst_vpx_enc->cfg.ts_number_layers = DEFAULT_TS_NUMBER_LAYERS;
gst_vpx_enc->n_ts_target_bitrate = 0;
gst_vpx_enc->n_ts_rate_decimator = 0;
gst_vpx_enc->cfg.ts_periodicity = DEFAULT_TS_PERIODICITY;
gst_vpx_enc->n_ts_layer_id = 0;
gst_vpx_enc->cfg.g_error_resilient = DEFAULT_ERROR_RESILIENT;
gst_vpx_enc->cfg.g_lag_in_frames = DEFAULT_LAG_IN_FRAMES;
gst_vpx_enc->cfg.g_threads = DEFAULT_THREADS;
gst_vpx_enc->deadline = DEFAULT_DEADLINE;
gst_vpx_enc->h_scaling_mode = DEFAULT_H_SCALING_MODE;
gst_vpx_enc->v_scaling_mode = DEFAULT_V_SCALING_MODE;
gst_vpx_enc->cpu_used = DEFAULT_CPU_USED;
gst_vpx_enc->enable_auto_alt_ref = DEFAULT_ENABLE_AUTO_ALT_REF;
gst_vpx_enc->noise_sensitivity = DEFAULT_NOISE_SENSITIVITY;
gst_vpx_enc->sharpness = DEFAULT_SHARPNESS;
gst_vpx_enc->static_threshold = DEFAULT_STATIC_THRESHOLD;
gst_vpx_enc->token_partitions = DEFAULT_TOKEN_PARTITIONS;
gst_vpx_enc->arnr_maxframes = DEFAULT_ARNR_MAXFRAMES;
gst_vpx_enc->arnr_strength = DEFAULT_ARNR_STRENGTH;
gst_vpx_enc->arnr_type = DEFAULT_ARNR_TYPE;
gst_vpx_enc->tuning = DEFAULT_TUNING;
gst_vpx_enc->cq_level = DEFAULT_CQ_LEVEL;
gst_vpx_enc->max_intra_bitrate_pct = DEFAULT_MAX_INTRA_BITRATE_PCT;
gst_vpx_enc->timebase_n = DEFAULT_TIMEBASE_N;
gst_vpx_enc->timebase_d = DEFAULT_TIMEBASE_D;
gst_vpx_enc->cfg.g_profile = DEFAULT_PROFILE;
g_mutex_init (&gst_vpx_enc->encoder_lock);
}
static void
gst_vpx_enc_finalize (GObject * object)
{
GstVPXEnc *gst_vpx_enc;
GST_DEBUG_OBJECT (object, "finalize");
g_return_if_fail (GST_IS_VPX_ENC (object));
gst_vpx_enc = GST_VPX_ENC (object);
g_free (gst_vpx_enc->multipass_cache_prefix);
g_free (gst_vpx_enc->multipass_cache_file);
gst_vpx_enc->multipass_cache_idx = 0;
if (gst_vpx_enc->input_state)
gst_video_codec_state_unref (gst_vpx_enc->input_state);
g_mutex_clear (&gst_vpx_enc->encoder_lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_vpx_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVPXEnc *gst_vpx_enc;
gboolean global = FALSE;
vpx_codec_err_t status;
g_return_if_fail (GST_IS_VPX_ENC (object));
gst_vpx_enc = GST_VPX_ENC (object);
GST_DEBUG_OBJECT (object, "gst_vpx_enc_set_property");
g_mutex_lock (&gst_vpx_enc->encoder_lock);
switch (prop_id) {
case PROP_RC_END_USAGE:
gst_vpx_enc->cfg.rc_end_usage = g_value_get_enum (value);
global = TRUE;
break;
case PROP_RC_TARGET_BITRATE:
gst_vpx_enc->cfg.rc_target_bitrate = g_value_get_int (value) / 1000;
gst_vpx_enc->rc_target_bitrate_set = TRUE;
global = TRUE;
break;
case PROP_RC_MIN_QUANTIZER:
gst_vpx_enc->cfg.rc_min_quantizer = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_MAX_QUANTIZER:
gst_vpx_enc->cfg.rc_max_quantizer = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_DROPFRAME_THRESH:
gst_vpx_enc->cfg.rc_dropframe_thresh = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_RESIZE_ALLOWED:
gst_vpx_enc->cfg.rc_resize_allowed = g_value_get_boolean (value);
global = TRUE;
break;
case PROP_RC_RESIZE_UP_THRESH:
gst_vpx_enc->cfg.rc_resize_up_thresh = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_RESIZE_DOWN_THRESH:
gst_vpx_enc->cfg.rc_resize_down_thresh = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_UNDERSHOOT_PCT:
gst_vpx_enc->cfg.rc_undershoot_pct = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_OVERSHOOT_PCT:
gst_vpx_enc->cfg.rc_overshoot_pct = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_BUF_SZ:
gst_vpx_enc->cfg.rc_buf_sz = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_BUF_INITIAL_SZ:
gst_vpx_enc->cfg.rc_buf_initial_sz = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_BUF_OPTIMAL_SZ:
gst_vpx_enc->cfg.rc_buf_optimal_sz = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_2PASS_VBR_BIAS_PCT:
gst_vpx_enc->cfg.rc_2pass_vbr_bias_pct = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_2PASS_VBR_MINSECTION_PCT:
gst_vpx_enc->cfg.rc_2pass_vbr_minsection_pct = g_value_get_int (value);
global = TRUE;
break;
case PROP_RC_2PASS_VBR_MAXSECTION_PCT:
gst_vpx_enc->cfg.rc_2pass_vbr_maxsection_pct = g_value_get_int (value);
global = TRUE;
break;
case PROP_KF_MODE:
gst_vpx_enc->cfg.kf_mode = g_value_get_enum (value);
global = TRUE;
break;
case PROP_KF_MAX_DIST:
gst_vpx_enc->cfg.kf_max_dist = g_value_get_int (value);
global = TRUE;
break;
case PROP_MULTIPASS_MODE:
gst_vpx_enc->cfg.g_pass = g_value_get_enum (value);
global = TRUE;
break;
case PROP_MULTIPASS_CACHE_FILE:
if (gst_vpx_enc->multipass_cache_prefix)
g_free (gst_vpx_enc->multipass_cache_prefix);
gst_vpx_enc->multipass_cache_prefix = g_value_dup_string (value);
break;
case PROP_TS_NUMBER_LAYERS:
gst_vpx_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_vpx_enc->cfg.ts_target_bitrate, 0,
sizeof (gst_vpx_enc->cfg.ts_target_bitrate));
if (va == NULL) {
gst_vpx_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_vpx_enc), VPX_TS_MAX_LAYERS);
} else {
gint i;
for (i = 0; i < va->n_values; i++)
gst_vpx_enc->cfg.ts_target_bitrate[i] =
g_value_get_int (g_value_array_get_nth (va, i));
gst_vpx_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_vpx_enc->cfg.ts_rate_decimator, 0,
sizeof (gst_vpx_enc->cfg.ts_rate_decimator));
if (va == NULL) {
gst_vpx_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_vpx_enc), VPX_TS_MAX_LAYERS);
} else {
gint i;
for (i = 0; i < va->n_values; i++)
gst_vpx_enc->cfg.ts_rate_decimator[i] =
g_value_get_int (g_value_array_get_nth (va, i));
gst_vpx_enc->n_ts_rate_decimator = va->n_values;
}
global = TRUE;
break;
}
case PROP_TS_PERIODICITY:
gst_vpx_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_vpx_enc->cfg.ts_layer_id, 0,
sizeof (gst_vpx_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_vpx_enc), VPX_TS_MAX_PERIODICITY);
} else if (va) {
gint i;
for (i = 0; i < va->n_values; i++)
gst_vpx_enc->cfg.ts_layer_id[i] =
g_value_get_int (g_value_array_get_nth (va, i));
gst_vpx_enc->n_ts_layer_id = va->n_values;
} else {
gst_vpx_enc->n_ts_layer_id = 0;
}
global = TRUE;
break;
}
case PROP_ERROR_RESILIENT:
gst_vpx_enc->cfg.g_error_resilient = g_value_get_flags (value);
global = TRUE;
break;
case PROP_LAG_IN_FRAMES:
gst_vpx_enc->cfg.g_lag_in_frames = g_value_get_int (value);
global = TRUE;
break;
case PROP_THREADS:
gst_vpx_enc->cfg.g_threads = g_value_get_int (value);
global = TRUE;
break;
case PROP_DEADLINE:
gst_vpx_enc->deadline = g_value_get_int64 (value);
break;
case PROP_H_SCALING_MODE:
gst_vpx_enc->h_scaling_mode = g_value_get_enum (value);
if (gst_vpx_enc->inited) {
vpx_scaling_mode_t sm;
sm.h_scaling_mode = gst_vpx_enc->h_scaling_mode;
sm.v_scaling_mode = gst_vpx_enc->v_scaling_mode;
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SCALEMODE, &sm);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_SCALEMODE: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_V_SCALING_MODE:
gst_vpx_enc->v_scaling_mode = g_value_get_enum (value);
if (gst_vpx_enc->inited) {
vpx_scaling_mode_t sm;
sm.h_scaling_mode = gst_vpx_enc->h_scaling_mode;
sm.v_scaling_mode = gst_vpx_enc->v_scaling_mode;
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SCALEMODE, &sm);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_SCALEMODE: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_CPU_USED:
gst_vpx_enc->cpu_used = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_CPUUSED,
gst_vpx_enc->cpu_used);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc, "Failed to set VP8E_SET_CPUUSED: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_ENABLE_AUTO_ALT_REF:
gst_vpx_enc->enable_auto_alt_ref = g_value_get_boolean (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ENABLEAUTOALTREF,
(gst_vpx_enc->enable_auto_alt_ref ? 1 : 0));
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_ENABLEAUTOALTREF: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_NOISE_SENSITIVITY:
gst_vpx_enc->noise_sensitivity = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder,
VP8E_SET_NOISE_SENSITIVITY, gst_vpx_enc->noise_sensitivity);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_NOISE_SENSITIVITY: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_SHARPNESS:
gst_vpx_enc->sharpness = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_SHARPNESS,
gst_vpx_enc->sharpness);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_SHARPNESS: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_STATIC_THRESHOLD:
gst_vpx_enc->static_threshold = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_STATIC_THRESHOLD,
gst_vpx_enc->static_threshold);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_STATIC_THRESHOLD: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_TOKEN_PARTITIONS:
gst_vpx_enc->token_partitions = g_value_get_enum (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_TOKEN_PARTITIONS,
gst_vpx_enc->token_partitions);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_TOKEN_PARTIONS: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_ARNR_MAXFRAMES:
gst_vpx_enc->arnr_maxframes = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ARNR_MAXFRAMES,
gst_vpx_enc->arnr_maxframes);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_ARNR_MAXFRAMES: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_ARNR_STRENGTH:
gst_vpx_enc->arnr_strength = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_ARNR_STRENGTH,
gst_vpx_enc->arnr_strength);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_ARNR_STRENGTH: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_ARNR_TYPE:
gst_vpx_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_vpx_enc->tuning = g_value_get_enum (value);
if (gst_vpx_enc->inited) {
status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_TUNING,
gst_vpx_enc->tuning);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_TUNING: %s", gst_vpx_error_name (status));
}
}
break;
case PROP_CQ_LEVEL:
gst_vpx_enc->cq_level = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status = vpx_codec_control (&gst_vpx_enc->encoder, VP8E_SET_CQ_LEVEL,
gst_vpx_enc->cq_level);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_CQ_LEVEL: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_MAX_INTRA_BITRATE_PCT:
gst_vpx_enc->max_intra_bitrate_pct = g_value_get_int (value);
if (gst_vpx_enc->inited) {
status =
vpx_codec_control (&gst_vpx_enc->encoder,
VP8E_SET_MAX_INTRA_BITRATE_PCT, gst_vpx_enc->max_intra_bitrate_pct);
if (status != VPX_CODEC_OK) {
GST_WARNING_OBJECT (gst_vpx_enc,
"Failed to set VP8E_SET_MAX_INTRA_BITRATE_PCT: %s",
gst_vpx_error_name (status));
}
}
break;
case PROP_TIMEBASE:
gst_vpx_enc->timebase_n = gst_value_get_fraction_numerator (value);
gst_vpx_enc->timebase_d = gst_value_get_fraction_denominator (value);
break;
default:
break;
}
if (global &&gst_vpx_enc->inited) {
status =
vpx_codec_enc_config_set (&gst_vpx_enc->encoder, &gst_vpx_enc->cfg);
if (status != VPX_CODEC_OK) {
g_mutex_unlock (&gst_vpx_enc->encoder_lock);
GST_ELEMENT_ERROR (gst_vpx_enc, LIBRARY, INIT,
("Failed to set encoder configuration"), ("%s",
gst_vpx_error_name (status)));
} else {
g_mutex_unlock (&gst_vpx_enc->encoder_lock);
}
} else {
g_mutex_unlock (&gst_vpx_enc->encoder_lock);
}
}
static void
gst_vpx_enc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstVPXEnc *gst_vpx_enc;
g_return_if_fail (GST_IS_VPX_ENC (object));
gst_vpx_enc = GST_VPX_ENC (object);
g_mutex_lock (&gst_vpx_enc->encoder_lock);
switch (prop_id) {
case PROP_RC_END_USAGE:
g_value_set_enum (value, gst_vpx_enc->cfg.rc_end_usage);
break;
case PROP_RC_TARGET_BITRATE:
g_value_set_int (value, gst_vpx_enc->cfg.rc_target_bitrate * 1000);
break;
case PROP_RC_MIN_QUANTIZER:
g_value_set_int (value, gst_vpx_enc->cfg.rc_min_quantizer);
break;
case PROP_RC_MAX_QUANTIZER:
g_value_set_int (value, gst_vpx_enc->cfg.rc_max_quantizer);
break;
case PROP_RC_DROPFRAME_THRESH:
g_value_set_int (value, gst_vpx_enc->cfg.rc_dropframe_thresh);
break;
case PROP_RC_RESIZE_ALLOWED:
g_value_set_boolean (value, gst_vpx_enc->cfg.rc_resize_allowed);
break;
case PROP_RC_RESIZE_UP_THRESH:
g_value_set_int (value, gst_vpx_enc->cfg.rc_resize_up_thresh);
break;
case PROP_RC_RESIZE_DOWN_THRESH:
g_value_set_int (value, gst_vpx_enc->cfg.rc_resize_down_thresh);
break;
case PROP_RC_UNDERSHOOT_PCT:
g_value_set_int (value, gst_vpx_enc->cfg.rc_undershoot_pct);
break;
case PROP_RC_OVERSHOOT_PCT:
g_value_set_int (value, gst_vpx_enc->cfg.rc_overshoot_pct);
break;
case PROP_RC_BUF_SZ:
g_value_set_int (value, gst_vpx_enc->cfg.rc_buf_sz);
break;
case PROP_RC_BUF_INITIAL_SZ:
g_value_set_int (value, gst_vpx_enc->cfg.rc_buf_initial_sz);
break;
case PROP_RC_BUF_OPTIMAL_SZ:
g_value_set_int (value, gst_vpx_enc->cfg.rc_buf_optimal_sz);
break;
case PROP_RC_2PASS_VBR_BIAS_PCT:
g_value_set_int (value, gst_vpx_enc->cfg.rc_2pass_vbr_bias_pct);
break;
case PROP_RC_2PASS_VBR_MINSECTION_PCT:
g_value_set_int (value, gst_vpx_enc->cfg.rc_2pass_vbr_minsection_pct);
break;
case PROP_RC_2PASS_VBR_MAXSECTION_PCT:
g_value_set_int (value, gst_vpx_enc->cfg.rc_2pass_vbr_maxsection_pct);
break;
case PROP_KF_MODE:
g_value_set_enum (value, gst_vpx_enc->cfg.kf_mode);
break;
case PROP_KF_MAX_DIST:
g_value_set_int (value, gst_vpx_enc->cfg.kf_max_dist);
break;
case PROP_MULTIPASS_MODE:
g_value_set_enum (value, gst_vpx_enc->cfg.g_pass);
break;
case PROP_MULTIPASS_CACHE_FILE:
g_value_set_string (value, gst_vpx_enc->multipass_cache_prefix);
break;
case PROP_TS_NUMBER_LAYERS:
g_value_set_int (value, gst_vpx_enc->cfg.ts_number_layers);
break;
case PROP_TS_TARGET_BITRATE:{
GValueArray *va;
if (gst_vpx_enc->n_ts_target_bitrate == 0) {
g_value_set_boxed (value, NULL);
} else {
gint i;
va = g_value_array_new (gst_vpx_enc->n_ts_target_bitrate);
for (i = 0; i < gst_vpx_enc->n_ts_target_bitrate; i++) {
GValue v = { 0, };
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, gst_vpx_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_vpx_enc->n_ts_rate_decimator == 0) {
g_value_set_boxed (value, NULL);
} else {
gint i;
va = g_value_array_new (gst_vpx_enc->n_ts_rate_decimator);
for (i = 0; i < gst_vpx_enc->n_ts_rate_decimator; i++) {
GValue v = { 0, };
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, gst_vpx_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_vpx_enc->cfg.ts_periodicity);
break;
case PROP_TS_LAYER_ID:{
GValueArray *va;
if (gst_vpx_enc->n_ts_layer_id == 0) {
g_value_set_boxed (value, NULL);
} else {
gint i;
va = g_value_array_new (gst_vpx_enc->n_ts_layer_id);
for (i = 0; i < gst_vpx_enc->n_ts_layer_id; i++) {
GValue v = { 0, };
g_value_init (&v, G_TYPE_INT);
g_value_set_int (&v, gst_vpx_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_vpx_enc->cfg.g_error_resilient);
break;
case PROP_LAG_IN_FRAMES:
g_value_set_int (value, gst_vpx_enc->cfg.g_lag_in_frames);
break;
case PROP_THREADS:
g_value_set_int (value, gst_vpx_enc->cfg.g_threads);
break;
case PROP_DEADLINE:
g_value_set_int64 (value, gst_vpx_enc->deadline);
break;
case PROP_H_SCALING_MODE:
g_value_set_enum (value, gst_vpx_enc->h_scaling_mode);
break;
case PROP_V_SCALING_MODE:
g_value_set_enum (value, gst_vpx_enc->v_scaling_mode);
break;
case PROP_CPU_USED:
g_value_set_int (value, gst_vpx_enc->cpu_used);
break;
case PROP_ENABLE_AUTO_ALT_REF:
g_value_set_boolean (value, gst_vpx_enc->enable_auto_alt_ref);
break;
case PROP_NOISE_SENSITIVITY:
g_value_set_int (value, gst_vpx_enc->noise_sensitivity);
break;
case PROP_SHARPNESS:
g_value_set_int (value, gst_vpx_enc->sharpness);
break;
case PROP_STATIC_THRESHOLD:
g_value_set_int (value, gst_vpx_enc->static_threshold);
break;
case PROP_TOKEN_PARTITIONS:
g_value_set_enum (value, gst_vpx_enc->token_partitions);
break;
case PROP_ARNR_MAXFRAMES:
g_value_set_int (value, gst_vpx_enc->arnr_maxframes);
break;
case PROP_ARNR_STRENGTH:
g_value_set_int (value, gst_vpx_enc->arnr_strength);
break;
case PROP_ARNR_TYPE:
g_value_set_int (value, gst_vpx_enc->arnr_type);
break;
case PROP_TUNING:
g_value_set_enum (value, gst_vpx_enc->tuning);
break;
case PROP_CQ_LEVEL:
g_value_set_int (value, gst_vpx_enc->cq_level);
break;
case PROP_MAX_INTRA_BITRATE_PCT:
g_value_set_int (value, gst_vpx_enc->max_intra_bitrate_pct);
break;
case PROP_TIMEBASE:
gst_value_set_fraction (value, gst_vpx_enc->timebase_n,
gst_vpx_enc->timebase_d);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
g_mutex_unlock (&gst_vpx_enc->encoder_lock);
}
static gboolean
gst_vpx_enc_start (GstVideoEncoder * video_encoder)
{
GstVPXEnc *encoder = GST_VPX_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_vpx_enc_destroy_encoder (GstVPXEnc * 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_vpx_enc_stop (GstVideoEncoder * video_encoder)
{
GstVPXEnc *encoder;
GST_DEBUG_OBJECT (video_encoder, "stop");
encoder = GST_VPX_ENC (video_encoder);
gst_vpx_enc_destroy_encoder (encoder);
gst_tag_setter_reset_tags (GST_TAG_SETTER (encoder));
g_free (encoder->multipass_cache_file);
encoder->multipass_cache_file = NULL;
encoder->multipass_cache_idx = 0;
return TRUE;
}
static gint
gst_vpx_enc_get_downstream_profile (GstVPXEnc * 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_vpx_enc_set_format (GstVideoEncoder * video_encoder,
GstVideoCodecState * state)
{
GstVPXEnc *encoder;
vpx_codec_err_t status;
vpx_image_t *image;
GstCaps *caps;
gboolean ret = TRUE;
GstVideoInfo *info = &state->info;
GstVideoCodecState *output_state;
GstClockTime latency;
GstVPXEncClass *vpx_enc_class;
encoder = GST_VPX_ENC (video_encoder);
vpx_enc_class = GST_VPX_ENC_GET_CLASS (encoder);
GST_DEBUG_OBJECT (video_encoder, "set_format");
if (encoder->inited) {
gst_vpx_enc_drain (video_encoder);
g_mutex_lock (&encoder->encoder_lock);
vpx_codec_destroy (&encoder->encoder);
encoder->inited = FALSE;
encoder->multipass_cache_idx++;
} else {
g_mutex_lock (&encoder->encoder_lock);
}
encoder->cfg.g_profile = gst_vpx_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 ||
encoder->cfg.g_pass == VPX_RC_LAST_PASS) {
if (!encoder->multipass_cache_prefix) {
GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ,
("No multipass cache file provided"), (NULL));
g_mutex_unlock (&encoder->encoder_lock);
return FALSE;
}
g_free (encoder->multipass_cache_file);
if (encoder->multipass_cache_idx > 0)
encoder->multipass_cache_file = g_strdup_printf ("%s.%u",
encoder->multipass_cache_prefix, encoder->multipass_cache_idx);
else
encoder->multipass_cache_file =
g_strdup (encoder->multipass_cache_prefix);
}
if (encoder->cfg.g_pass == VPX_RC_FIRST_PASS) {
if (encoder->first_pass_cache_content != NULL)
g_byte_array_free (encoder->first_pass_cache_content, TRUE);
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->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_enc_class->get_algo (encoder),
&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;
}
if (vpx_enc_class->enable_scaling (encoder)) {
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));
vpx_enc_class->set_image_format (encoder, image);
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);
caps = vpx_enc_class->get_new_vpx_caps (encoder);
vpx_enc_class->set_stream_info (encoder, caps, info);
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_vpx_enc_process (GstVPXEnc * encoder)
{
vpx_codec_iter_t iter = NULL;
const vpx_codec_cx_pkt_t *pkt;
GstVideoEncoder *video_encoder;
void *user_data;
GstVideoCodecFrame *frame;
GstFlowReturn ret = GST_FLOW_OK;
GstVPXEncClass *vpx_enc_class;
vpx_codec_pts_t pts;
video_encoder = GST_VIDEO_ENCODER (encoder);
vpx_enc_class = GST_VPX_ENC_GET_CLASS (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;
/* discard older frames that were dropped by libvpx */
frame = NULL;
do {
if (frame)
gst_video_encoder_finish_frame (video_encoder, frame);
frame = gst_video_encoder_get_oldest_frame (video_encoder);
pts =
gst_util_uint64_scale (frame->pts,
encoder->cfg.g_timebase.den,
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND);
GST_TRACE_OBJECT (encoder, "vpx pts: %" G_GINT64_FORMAT
", gst frame pts: %" G_GINT64_FORMAT, pkt->data.frame.pts, pts);
} while (pkt->data.frame.pts > pts);
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);
/* 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);
user_data = vpx_enc_class->process_frame_user_data (encoder, frame);
if (invisible) {
ret =
vpx_enc_class->handle_invisible_frame_buffer (encoder, user_data,
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_vpx_enc_drain (GstVideoEncoder * video_encoder)
{
GstVPXEnc *encoder;
int flags = 0;
vpx_codec_err_t status;
gint64 deadline;
vpx_codec_pts_t pts;
encoder = GST_VPX_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_vpx_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_vpx_enc_flush (GstVideoEncoder * video_encoder)
{
GstVPXEnc *encoder;
GST_DEBUG_OBJECT (video_encoder, "flush");
encoder = GST_VPX_ENC (video_encoder);
gst_vpx_enc_destroy_encoder (encoder);
if (encoder->input_state) {
gst_video_codec_state_ref (encoder->input_state);
gst_vpx_enc_set_format (video_encoder, encoder->input_state);
gst_video_codec_state_unref (encoder->input_state);
}
return TRUE;
}
static GstFlowReturn
gst_vpx_enc_finish (GstVideoEncoder * video_encoder)
{
GstVPXEnc *encoder;
GstFlowReturn ret;
GST_DEBUG_OBJECT (video_encoder, "finish");
encoder = GST_VPX_ENC (video_encoder);
if (encoder->inited) {
ret = gst_vpx_enc_drain (video_encoder);
} else {
ret = GST_FLOW_OK;
}
return ret;
}
static vpx_image_t *
gst_vpx_enc_buffer_to_image (GstVPXEnc * 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_vpx_enc_handle_frame (GstVideoEncoder * video_encoder,
GstVideoCodecFrame * frame)
{
GstVPXEnc *encoder;
vpx_codec_err_t status;
int flags = 0;
vpx_image_t *image;
GstVideoFrame vframe;
vpx_codec_pts_t pts;
unsigned long duration;
GstVPXEncClass *vpx_enc_class;
GST_DEBUG_OBJECT (video_encoder, "handle_frame");
encoder = GST_VPX_ENC (video_encoder);
vpx_enc_class = GST_VPX_ENC_GET_CLASS (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_vpx_enc_buffer_to_image (encoder, &vframe);
vpx_enc_class->set_frame_user_data (encoder, frame, image);
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);
if (duration > 0) {
encoder->last_pts += frame->duration;
} else {
/* We force the path ignoring the duration if we end up with a zero
* value for duration after scaling (e.g. duration value too small) */
GST_WARNING_OBJECT (encoder,
"Ignoring too small frame duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (frame->duration));
duration = 1;
}
} 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);
gst_video_codec_frame_unref (frame);
return GST_FLOW_ERROR;
}
gst_video_codec_frame_unref (frame);
return gst_vpx_enc_process (encoder);
}
static gboolean
gst_vpx_enc_sink_event (GstVideoEncoder * benc, GstEvent * event)
{
GstVPXEnc *enc = GST_VPX_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_vpx_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 || HAVE_VP9_ENCODER */