"type": "gint",
"writable": true
},
+ "temporal-scalability-layer-flags": {
+ "blurb": "Sequence defining coding layer flags",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": false,
+ "controllable": false,
+ "mutable": "null",
+ "readable": true,
+ "type": "GstValueArray",
+ "writable": true
+ },
"temporal-scalability-layer-id": {
"blurb": "Sequence defining coding layer membership",
"conditionally-available": false,
"type": "GValueArray",
"writable": true
},
+ "temporal-scalability-layer-sync-flags": {
+ "blurb": "Sequence defining coding layer sync flags",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": false,
+ "controllable": false,
+ "mutable": "null",
+ "readable": true,
+ "type": "GstValueArray",
+ "writable": true
+ },
"temporal-scalability-number-layers": {
"blurb": "Number of coding layers to use",
"conditionally-available": false,
{
vpx_image_t *image;
GList *invisible;
+ guint layer_id;
+ guint8 tl0picidx;
+ gboolean layer_sync;
} GstVP8EncUserData;
static void
void *user_data, GstBuffer * buffer);
static void gst_vp8_enc_set_frame_user_data (GstVPXEnc * enc,
GstVideoCodecFrame * frame, vpx_image_t * image);
+static void gst_vp8_enc_apply_frame_temporal_settings (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, guint layer_id, guint8 tl0picidx,
+ gboolean layer_sync);
+static void gst_vp8_enc_get_frame_temporal_settings (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, guint * layer_id, guint8 * tl0picidx,
+ gboolean * layer_sync);
+static void gst_vp8_enc_preflight_buffer (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, GstBuffer * buffer,
+ gboolean layer_sync, guint layer_id, guint8 tl0picidx);
static GstFlowReturn gst_vp8_enc_pre_push (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame);
vpx_encoder_class->handle_invisible_frame_buffer =
gst_vp8_enc_handle_invisible_frame_buffer;
vpx_encoder_class->set_frame_user_data = gst_vp8_enc_set_frame_user_data;
+ vpx_encoder_class->apply_frame_temporal_settings =
+ gst_vp8_enc_apply_frame_temporal_settings;
+ vpx_encoder_class->get_frame_temporal_settings =
+ gst_vp8_enc_get_frame_temporal_settings;
+ vpx_encoder_class->preflight_buffer = gst_vp8_enc_preflight_buffer;
GST_DEBUG_CATEGORY_INIT (gst_vp8enc_debug, "vp8enc", 0, "VP8 Encoder");
}
return;
}
+static void
+gst_vp8_enc_apply_frame_temporal_settings (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, guint layer_id, guint8 tl0picidx,
+ gboolean layer_sync)
+{
+ GstVP8EncUserData *user_data;
+
+ user_data = gst_video_codec_frame_get_user_data (frame);
+
+ if (!user_data) {
+ GST_ERROR_OBJECT (enc, "Have no frame user data");
+ return;
+ }
+
+ vpx_codec_control (&enc->encoder, VP8E_SET_TEMPORAL_LAYER_ID, layer_id);
+ user_data->layer_id = layer_id;
+ user_data->tl0picidx = tl0picidx;
+ user_data->layer_sync = layer_sync;
+
+ return;
+}
+
+static void
+gst_vp8_enc_get_frame_temporal_settings (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, guint * layer_id, guint8 * tl0picidx,
+ gboolean * layer_sync)
+{
+ GstVP8EncUserData *user_data;
+
+ user_data = gst_video_codec_frame_get_user_data (frame);
+
+ if (!user_data) {
+ GST_ERROR_OBJECT (enc, "Have no frame user data");
+ *layer_id = 0;
+ *tl0picidx = 0;
+ *layer_sync = FALSE;
+ return;
+ }
+
+ *layer_id = user_data->layer_id;
+ *tl0picidx = user_data->tl0picidx;
+ *layer_sync = user_data->layer_sync;
+
+ return;
+}
+
+static void
+gst_vp8_enc_preflight_buffer (GstVPXEnc * enc,
+ GstVideoCodecFrame * frame, GstBuffer * buffer,
+ gboolean layer_sync, guint layer_id, guint8 tl0picidx)
+{
+ GstCustomMeta *meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
+ GstStructure *s = gst_custom_meta_get_structure (meta);
+
+ gst_structure_set (s,
+ "use-temporal-scaling", G_TYPE_BOOLEAN, (enc->cfg.ts_periodicity != 0),
+ "layer-sync", G_TYPE_BOOLEAN, layer_sync,
+ "layer-id", G_TYPE_UINT, layer_id,
+ "tl0picidx", G_TYPE_UINT, tl0picidx, NULL);
+}
+
static guint64
_to_granulepos (guint64 frame_end_number, guint inv_count, guint keyframe_dist)
{
#define DEFAULT_TS_RATE_DECIMATOR NULL
#define DEFAULT_TS_PERIODICITY 0
#define DEFAULT_TS_LAYER_ID NULL
+#define DEFAULT_TS_LAYER_FLAGS NULL
+#define DEFAULT_TS_LAYER_SYNC_FLAGS NULL
#define DEFAULT_ERROR_RESILIENT 0
#define DEFAULT_LAG_IN_FRAMES 0
PROP_TS_RATE_DECIMATOR,
PROP_TS_PERIODICITY,
PROP_TS_LAYER_ID,
+ PROP_TS_LAYER_FLAGS,
+ PROP_TS_LAYER_SYNC_FLAGS,
PROP_MULTIPASS_MODE,
PROP_MULTIPASS_CACHE_FILE,
PROP_ERROR_RESILIENT,
return id;
}
+#define GST_VPX_ENC_TS_LAYER_FLAGS_TYPE (gst_vpx_enc_ts_layer_flags_get_type())
+static GType
+gst_vpx_enc_ts_layer_flags_get_type (void)
+{
+ static const GFlagsValue values[] = {
+ {VP8_EFLAG_NO_REF_LAST, "Don't reference the last frame", "no-ref-last"},
+ {VP8_EFLAG_NO_REF_GF, "Don't reference the golden frame", "no-ref-golden"},
+ {VP8_EFLAG_NO_REF_ARF, "Don't reference the alternate reference frame",
+ "no-ref-alt"},
+ {VP8_EFLAG_NO_UPD_LAST, "Don't update the last frame", "no-upd-last"},
+ {VP8_EFLAG_NO_UPD_GF, "Don't update the golden frame", "no-upd-golden"},
+ {VP8_EFLAG_NO_UPD_ARF, "Don't update the alternate reference frame",
+ "no-upd-alt"},
+ {VP8_EFLAG_NO_UPD_ENTROPY, "Disable entropy update", "no-upd-entropy"},
+ {0, NULL, NULL}
+ };
+ static volatile GType id = 0;
+
+ if (g_once_init_enter ((gsize *) & id)) {
+ GType _id;
+
+ _id = g_flags_register_static ("GstVPXEncTsLayerFlags", 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);
video_encoder, GstEvent * event);
static gboolean gst_vpx_enc_propose_allocation (GstVideoEncoder * encoder,
GstQuery * query);
+static gboolean gst_vpx_enc_transform_meta (GstVideoEncoder * encoder,
+ GstVideoCodecFrame * frame, GstMeta * meta);
#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);
- );
+ G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL););
static void
gst_vpx_enc_class_init (GstVPXEncClass * klass)
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;
+ video_encoder_class->transform_meta = gst_vpx_enc_transform_meta;
g_object_class_install_property (gobject_class, PROP_RC_END_USAGE,
g_param_spec_enum ("end-usage", "Rate control mode",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_DOC_SHOW_DEFAULT));
+ /**
+ * GstVPXEnc:temporal-scalability-layer-flags:
+ *
+ * Sequence defining coding layer flags
+ *
+ * Since: 1.20
+ */
+ g_object_class_install_property (gobject_class, PROP_TS_LAYER_FLAGS,
+ gst_param_spec_array ("temporal-scalability-layer-flags",
+ "Coding layer flags", "Sequence defining coding layer flags",
+ g_param_spec_flags ("flags", "Flags", "Flags",
+ GST_VPX_ENC_TS_LAYER_FLAGS_TYPE, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstVPXEnc:temporal-scalability-layer-sync-flags:
+ *
+ * Sequence defining coding layer sync flags
+ *
+ * Since: 1.20
+ */
+ g_object_class_install_property (gobject_class, PROP_TS_LAYER_SYNC_FLAGS,
+ gst_param_spec_array ("temporal-scalability-layer-sync-flags",
+ "Coding layer sync flags",
+ "Sequence defining coding layer sync flags",
+ g_param_spec_boolean ("flags", "Flags", "Flags", FALSE,
+ 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",
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->n_ts_layer_flags = 0;
+ gst_vpx_enc->ts_layer_flags = NULL;
+ gst_vpx_enc->n_ts_layer_sync_flags = 0;
+ gst_vpx_enc->ts_layer_sync_flags = NULL;
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->timebase_n = DEFAULT_TIMEBASE_N;
gst_vpx_enc->timebase_d = DEFAULT_TIMEBASE_D;
gst_vpx_enc->bits_per_pixel = DEFAULT_BITS_PER_PIXEL;
+ gst_vpx_enc->tl0picidx = 0;
+ gst_vpx_enc->prev_was_keyframe = FALSE;
gst_vpx_enc->cfg.g_profile = DEFAULT_PROFILE;
g_return_if_fail (GST_IS_VPX_ENC (object));
gst_vpx_enc = GST_VPX_ENC (object);
+ g_free (gst_vpx_enc->ts_layer_flags);
+ g_free (gst_vpx_enc->ts_layer_sync_flags);
+
g_free (gst_vpx_enc->multipass_cache_prefix);
g_free (gst_vpx_enc->multipass_cache_file);
gst_vpx_enc->multipass_cache_idx = 0;
global = TRUE;
break;
}
+ case PROP_TS_LAYER_FLAGS:{
+ gint l = gst_value_array_get_size (value);
+
+ g_free (gst_vpx_enc->ts_layer_flags);
+ gst_vpx_enc->n_ts_layer_flags = 0;
+
+ if (l > 0) {
+ gint i;
+
+ gst_vpx_enc->ts_layer_flags = g_new (gint, l);
+
+ for (i = 0; i < l; i++)
+ gst_vpx_enc->ts_layer_flags[i] =
+ g_value_get_flags (gst_value_array_get_value (value, i));
+ gst_vpx_enc->n_ts_layer_flags = l;
+ } else {
+ gst_vpx_enc->ts_layer_flags = NULL;
+ }
+ break;
+ }
+ case PROP_TS_LAYER_SYNC_FLAGS:{
+ gint l = gst_value_array_get_size (value);
+
+ g_free (gst_vpx_enc->ts_layer_sync_flags);
+ gst_vpx_enc->n_ts_layer_sync_flags = 0;
+
+ if (l > 0) {
+ gint i;
+
+ gst_vpx_enc->ts_layer_sync_flags = g_new (gboolean, l);
+ for (i = 0; i < l; i++)
+ gst_vpx_enc->ts_layer_sync_flags[i] =
+ g_value_get_boolean (gst_value_array_get_value (value, i));
+ gst_vpx_enc->n_ts_layer_sync_flags = l;
+ } else {
+ gst_vpx_enc->ts_layer_sync_flags = NULL;
+ }
+ break;
+ }
case PROP_ERROR_RESILIENT:
gst_vpx_enc->cfg.g_error_resilient = g_value_get_flags (value);
global = TRUE;
}
break;
}
+ case PROP_TS_LAYER_FLAGS:{
+ gint i;
+
+ for (i = 0; i < gst_vpx_enc->n_ts_layer_flags; i++) {
+ GValue v = { 0, };
+
+ g_value_init (&v, GST_VPX_ENC_TS_LAYER_FLAGS_TYPE);
+ g_value_set_flags (&v, gst_vpx_enc->ts_layer_flags[i]);
+ gst_value_array_append_value (value, &v);
+ g_value_unset (&v);
+ }
+ break;
+ }
+ case PROP_TS_LAYER_SYNC_FLAGS:{
+ gint i;
+
+ for (i = 0; i < gst_vpx_enc->n_ts_layer_sync_flags; i++) {
+ GValue v = { 0, };
+
+ g_value_init (&v, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&v, gst_vpx_enc->ts_layer_sync_flags[i]);
+ gst_value_array_append_value (value, &v);
+ g_value_unset (&v);
+ }
+ break;
+ }
case PROP_ERROR_RESILIENT:
g_value_set_flags (value, gst_vpx_enc->cfg.g_error_resilient);
break;
GstFlowReturn ret = GST_FLOW_OK;
GstVPXEncClass *vpx_enc_class;
vpx_codec_pts_t pts;
+ guint layer_id = 0;
+ guint8 tl0picidx = 0;
+ gboolean layer_sync = FALSE;
video_encoder = GST_VIDEO_ENCODER (encoder);
vpx_enc_class = GST_VPX_ENC_GET_CLASS (encoder);
} 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 =
pkt->data.frame.sz), pkt->data.frame.sz);
user_data = vpx_enc_class->process_frame_user_data (encoder, frame);
+ if (vpx_enc_class->get_frame_temporal_settings &&
+ encoder->cfg.ts_periodicity != 0) {
+ vpx_enc_class->get_frame_temporal_settings (encoder, frame,
+ &layer_id, &tl0picidx, &layer_sync);
+ }
+
+ if (layer_id != 0 && encoder->prev_was_keyframe) {
+ /* Non-base layer frame immediately after a keyframe is a layer sync */
+ layer_sync = TRUE;
+ }
+
+ if ((pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0) {
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
+ /* Key frames always live on layer 0 */
+ layer_id = 0;
+ layer_sync = TRUE;
+ encoder->prev_was_keyframe = TRUE;
+ } else {
+ GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
+ encoder->prev_was_keyframe = FALSE;
+ }
+
+ if ((pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0)
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DROPPABLE);
+ else
+ GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DROPPABLE);
+
+ if (layer_id == 0) {
+ /* Allocate a new tl0picidx if this is layer 0 */
+ tl0picidx = ++encoder->tl0picidx;
+ }
+
+ if (vpx_enc_class->preflight_buffer) {
+ vpx_enc_class->preflight_buffer (encoder, frame, buffer,
+ layer_sync, layer_id, tl0picidx);
+ }
if (invisible) {
ret =
duration = 1;
}
+ if (encoder->n_ts_layer_flags != 0) {
+ /* If we need a keyframe, then the pattern is irrelevant */
+ if ((flags & VPX_EFLAG_FORCE_KF) == 0) {
+ flags |=
+ encoder->ts_layer_flags[frame->system_frame_number %
+ encoder->n_ts_layer_flags];
+ }
+ }
+
+ if (vpx_enc_class->apply_frame_temporal_settings &&
+ encoder->cfg.ts_periodicity != 0 &&
+ encoder->n_ts_layer_id >= encoder->cfg.ts_periodicity) {
+ vpx_enc_class->apply_frame_temporal_settings (encoder, frame,
+ encoder->cfg.ts_layer_id[frame->system_frame_number %
+ encoder->cfg.ts_periodicity], encoder->tl0picidx,
+ encoder->ts_layer_sync_flags[frame->system_frame_number %
+ encoder->n_ts_layer_sync_flags]);
+ }
+
status = vpx_codec_encode (&encoder->encoder, image,
pts, duration, flags, encoder->deadline);
query);
}
+static gboolean
+gst_vpx_enc_transform_meta (GstVideoEncoder * encoder,
+ GstVideoCodecFrame * frame, GstMeta * meta)
+{
+ const GstMetaInfo *info = meta->info;
+ gboolean ret = FALSE;
+
+ /* Do not copy GstVP8Meta from input to output buffer */
+ if (gst_meta_info_is_custom (info)
+ && gst_custom_meta_has_name ((GstCustomMeta *) meta, "GstVP8Meta"))
+ goto done;
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
#endif /* HAVE_VP8_ENCODER || HAVE_VP9_ENCODER */
gint n_ts_target_bitrate;
gint n_ts_rate_decimator;
gint n_ts_layer_id;
+ gint n_ts_layer_flags;
+ gint *ts_layer_flags;
+ gint n_ts_layer_sync_flags;
+ gboolean *ts_layer_sync_flags;
/* Global two-pass options */
gchar *multipass_cache_file;
gchar *multipass_cache_prefix;
/* state */
gboolean inited;
+ guint8 tl0picidx;
+ gboolean prev_was_keyframe;
vpx_image_t image;
/*process user data*/
void* (*process_frame_user_data) (GstVPXEnc *enc, GstVideoCodecFrame* frame);
/*set frame user data*/
- void (*set_frame_user_data) (GstVPXEnc *enc, GstVideoCodecFrame* frame, vpx_image_t *image);
+ void (*set_frame_user_data) (GstVPXEnc *enc, GstVideoCodecFrame* frame,
+ vpx_image_t *image);
/*Handle invisible frame*/
- GstFlowReturn (*handle_invisible_frame_buffer) (GstVPXEnc *enc, void* user_data, GstBuffer* buffer);
+ GstFlowReturn (*handle_invisible_frame_buffer) (GstVPXEnc *enc,
+ void* user_data, GstBuffer* buffer);
+ /*apply temporal settings -- called with encoder lock*/
+ void (*apply_frame_temporal_settings) (GstVPXEnc *enc,
+ GstVideoCodecFrame* frame, guint layer_id, guint8 tl0picidx,
+ gboolean layer_sync);
+ /*get temporal settings*/
+ void (*get_frame_temporal_settings) (GstVPXEnc *enc,
+ GstVideoCodecFrame *frame, guint * layer_id, guint8 *tl0picidx,
+ gboolean *layer_sync);
+ /* preflight buffer */
+ void (*preflight_buffer) (GstVPXEnc *enc,
+ GstVideoCodecFrame *frame, GstBuffer *buffer,
+ gboolean layer_sync, guint layer_id, guint8 tl0picidx);
};
GType gst_vpx_enc_get_type (void);
static gboolean
plugin_init (GstPlugin * plugin)
{
+ static const gchar *tags[] = { NULL };
+
#ifdef HAVE_VP8_DECODER
gst_element_register (plugin, "vp8dec", GST_RANK_PRIMARY,
gst_vp8_dec_get_type ());
gst_vp9_enc_get_type ());
#endif
+ if (!gst_meta_register_custom ("GstVP8Meta", tags, NULL, NULL, NULL))
+ return FALSE;
+
return TRUE;
}
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
+
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
#include <gst/check/gstharness.h>
#include <gst/check/gstcheck.h>
#include <gst/video/video.h>
GST_END_TEST;
+#define verify_meta(buffer, usets, ybit, tid, tl0picindex) \
+ G_STMT_START { \
+ gboolean use_temporal_scaling, layer_sync; \
+ guint temporal_layer_id, tl0picidx; \
+ GstCustomMeta *meta = gst_buffer_get_custom_meta (buffer, \
+ "GstVP8Meta"); \
+ GstStructure *s; \
+ fail_unless (meta != NULL); \
+ s = gst_custom_meta_get_structure (meta); \
+ fail_unless (gst_structure_get (s, \
+ "use-temporal-scaling", G_TYPE_BOOLEAN, &use_temporal_scaling, \
+ "layer-sync", G_TYPE_BOOLEAN, &layer_sync, \
+ "layer-id", G_TYPE_UINT, &temporal_layer_id, \
+ "tl0picidx", G_TYPE_UINT, &tl0picidx, NULL)); \
+ fail_unless_equals_int (usets, use_temporal_scaling); \
+ fail_unless_equals_int (ybit, layer_sync); \
+ fail_unless_equals_int (tid, temporal_layer_id); \
+ fail_unless_equals_int (tl0picindex, tl0picidx); \
+ } G_STMT_END
+
+static void
+configure_vp8ts (GstHarness * h)
+{
+ gint i;
+ GValue layer_sync_flags = G_VALUE_INIT;
+ GValueArray *decimators = g_value_array_new (3);
+ GValueArray *layer_ids = g_value_array_new (4);
+ GValueArray *bitrates = g_value_array_new (3);
+ GValue ival = { 0, }, bval = {
+ 0,};
+
+ gst_value_array_init (&layer_sync_flags, 8);
+
+ g_value_init (&ival, G_TYPE_INT);
+ for (i = 0; i < 3; i++) {
+ /* 7.5, 15, 30fps */
+ static const gint d[] = { 4, 2, 1 };
+ g_value_set_int (&ival, d[i]);
+ g_value_array_append (decimators, &ival);
+ }
+
+ for (i = 0; i < 4; i++) {
+ static const gint d[] = { 0, 2, 1, 2 };
+ g_value_set_int (&ival, d[i]);
+ g_value_array_append (layer_ids, &ival);
+ }
+
+ for (i = 0; i < 3; i++) {
+ /* Split 512kbps 40%, 20%, 40% */
+ static const gint d[] = { 204800, 307200, 512000 };
+ g_value_set_int (&ival, d[i]);
+ g_value_array_append (bitrates, &ival);
+ }
+
+ gst_util_set_object_arg (G_OBJECT (h->element),
+ "temporal-scalability-layer-flags",
+ /* layer 0 */
+ "<no-ref-golden+no-upd-golden+no-upd-alt,"
+ /* layer 2 (sync) */
+ "no-ref-golden+no-upd-last+no-upd-golden+no-upd-alt+no-upd-entropy,"
+ /* layer 1 (sync) */
+ "no-ref-golden+no-upd-last+no-upd-alt,"
+ /* layer 2 */
+ "no-upd-last+no-upd-golden+no-upd-alt+no-upd-entropy,"
+ /* layer 0 */
+ "no-ref-golden+no-upd-golden+no-upd-alt,"
+ /* layer 2 */
+ "no-upd-last+no-upd-golden+no-upd-alt+no-upd-entropy,"
+ /* layer 1 */
+ "no-upd-last+no-upd-alt,"
+ /* layer 2 */
+ "no-upd-last+no-upd-golden+no-upd-alt+no-upd-entropy>");
+
+ g_value_init (&bval, G_TYPE_BOOLEAN);
+ for (i = 0; i < 8; i++) {
+ /* Reflect pattern above */
+ static const gboolean d[] = {
+ FALSE,
+ TRUE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE
+ };
+ g_value_set_boolean (&bval, d[i]);
+ gst_value_array_append_value (&layer_sync_flags, &bval);
+ }
+
+ g_object_set_property (G_OBJECT (h->element),
+ "temporal-scalability-layer-sync-flags", &layer_sync_flags);
+
+ g_object_set (h->element,
+ "temporal-scalability-number-layers", decimators->n_values,
+ "temporal-scalability-periodicity", layer_ids->n_values,
+ "temporal-scalability-rate-decimator", decimators,
+ "temporal-scalability-layer-id", layer_ids,
+ "temporal-scalability-target-bitrate", bitrates,
+ "error-resilient", 1, NULL);
+
+ g_value_array_free (decimators);
+ g_value_array_free (layer_ids);
+ g_value_array_free (bitrates);
+ g_value_unset (&layer_sync_flags);
+}
+
+GST_START_TEST (test_encode_temporally_scaled)
+{
+ gint i;
+ struct
+ {
+ gboolean ybit;
+ gint tid;
+ gint tl0picidx;
+ gboolean droppable;
+ } expected[] = {
+ {
+ TRUE, 0, 1, FALSE}, /* This is an intra */
+ {
+ TRUE, 2, 1, TRUE}, {
+ TRUE, 1, 1, FALSE}, {
+ FALSE, 2, 1, TRUE}, {
+ FALSE, 0, 2, FALSE}, {
+ FALSE, 2, 2, TRUE}, {
+ FALSE, 1, 2, FALSE}, {
+ FALSE, 2, 2, TRUE}, {
+ FALSE, 0, 3, FALSE}, {
+ TRUE, 2, 3, TRUE}, {
+ TRUE, 1, 3, FALSE}, {
+ FALSE, 2, 3, TRUE}, {
+ FALSE, 0, 4, FALSE}, {
+ FALSE, 2, 4, TRUE}, {
+ FALSE, 1, 4, FALSE}, {
+ FALSE, 2, 4, TRUE},};
+ GstHarness *h = gst_harness_new ("vp8enc");
+ gst_harness_set_src_caps (h, gst_caps_new_i420 (320, 240));
+ configure_vp8ts (h);
+
+ for (i = 0; i < 16; i++) {
+ GstBuffer *in, *out;
+
+ in = gst_harness_create_video_buffer_full (h, 0x42,
+ 320, 240, gst_util_uint64_scale (i, GST_SECOND, 30),
+ gst_util_uint64_scale (1, GST_SECOND, 30));
+ gst_harness_push (h, in);
+
+ out = gst_harness_pull (h);
+ /* Ensure first frame is encoded as an intra */
+ if (i == 0)
+ fail_if (GST_BUFFER_FLAG_IS_SET (out, GST_BUFFER_FLAG_DELTA_UNIT));
+ else
+ fail_unless (GST_BUFFER_FLAG_IS_SET (out, GST_BUFFER_FLAG_DELTA_UNIT));
+ fail_unless_equals_int (expected[i].droppable,
+ GST_BUFFER_FLAG_IS_SET (out, GST_BUFFER_FLAG_DROPPABLE));
+ verify_meta (out, TRUE, expected[i].ybit, expected[i].tid,
+ expected[i].tl0picidx);
+ gst_buffer_unref (out);
+ }
+
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_encode_fresh_meta)
+{
+ gint i;
+ GstBuffer *buffer;
+ GstHarness *h = gst_harness_new ("vp8enc");
+ GstCustomMeta *meta;
+ GstStructure *s;
+ gst_harness_set_src_caps (h, gst_caps_new_i420_full (320, 240, 25, 1, 1, 1));
+
+ buffer = gst_harness_create_video_buffer_full (h, 0x0,
+ 320, 240, gst_util_uint64_scale (0, GST_SECOND, 25),
+ gst_util_uint64_scale (1, GST_SECOND, 25));
+
+ /* Attach bogus meta to input buffer */
+ meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
+ s = gst_custom_meta_get_structure (meta);
+ gst_structure_set (s,
+ "use-temporal-scaling", G_TYPE_BOOLEAN, FALSE,
+ "layer-sync", G_TYPE_BOOLEAN, FALSE,
+ "layer-id", G_TYPE_UINT, 0, "tl0picidx", G_TYPE_UINT, 0, NULL);
+
+ for (i = 0; i < 2; i++) {
+ GstBuffer *out;
+
+ fail_unless_equals_int (GST_FLOW_OK,
+ gst_harness_push (h, gst_buffer_ref (buffer)));
+
+ out = gst_harness_pull (h);
+ /* Ensure that output buffer has fresh meta */
+ verify_meta (out, FALSE, (i == 0), 0, i + 1);
+ gst_buffer_unref (out);
+ }
+
+ gst_buffer_unref (buffer);
+
+ gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
static Suite *
vp8enc_suite (void)
{
tcase_add_test (tc_chain, test_encode_lag_in_frames);
tcase_add_test (tc_chain, test_encode_simple_when_bitrate_set_to_zero);
tcase_add_test (tc_chain, test_autobitrate_changes_with_caps);
+ tcase_add_test (tc_chain, test_encode_temporally_scaled);
+ tcase_add_test (tc_chain, test_encode_fresh_meta);
return s;
}