+static void vp8_ts_parse_int_array(int *dest, char *value, size_t value_len, int max_entries)
+{
+ int dest_idx = 0;
+ char *saveptr = NULL;
+ char *token = av_strtok(value, ",", &saveptr);
+
+ while (token && dest_idx < max_entries) {
+ dest[dest_idx++] = strtoul(token, NULL, 10);
+ token = av_strtok(NULL, ",", &saveptr);
+ }
+}
+
+#if CONFIG_LIBVPX_VP9_ENCODER && defined(VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT)
+static void vp8_ts_parse_int64_array(int64_t *dest, char *value, size_t value_len, int max_entries)
+{
+ int dest_idx = 0;
+ char *saveptr = NULL;
+ char *token = av_strtok(value, ",", &saveptr);
+
+ while (token && dest_idx < max_entries) {
+ dest[dest_idx++] = strtoull(token, NULL, 10);
+ token = av_strtok(NULL, ",", &saveptr);
+ }
+}
+#endif
+
+static void set_temporal_layer_pattern(int layering_mode, vpx_codec_enc_cfg_t *cfg,
+ int *layer_flags, int *flag_periodicity)
+{
+ switch (layering_mode) {
+ case 2: {
+ /**
+ * 2-layers, 2-frame period.
+ */
+ static const int ids[2] = { 0, 1 };
+ cfg->ts_periodicity = 2;
+ *flag_periodicity = 2;
+ cfg->ts_number_layers = 2;
+ cfg->ts_rate_decimator[0] = 2;
+ cfg->ts_rate_decimator[1] = 1;
+ memcpy(cfg->ts_layer_id, ids, sizeof(ids));
+
+ layer_flags[0] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
+ layer_flags[1] =
+ VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF |
+ VP8_EFLAG_NO_UPD_LAST |
+ VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF;
+ break;
+ }
+ case 3: {
+ /**
+ * 3-layers structure with one reference frame.
+ * This works same as temporal_layering_mode 3.
+ *
+ * 3-layers, 4-frame period.
+ */
+ static const int ids[4] = { 0, 2, 1, 2 };
+ cfg->ts_periodicity = 4;
+ *flag_periodicity = 4;
+ cfg->ts_number_layers = 3;
+ cfg->ts_rate_decimator[0] = 4;
+ cfg->ts_rate_decimator[1] = 2;
+ cfg->ts_rate_decimator[2] = 1;
+ memcpy(cfg->ts_layer_id, ids, sizeof(ids));
+
+ /**
+ * 0=L, 1=GF, 2=ARF,
+ * Intra-layer prediction disabled.
+ */
+ layer_flags[0] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
+ layer_flags[1] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
+ VP8_EFLAG_NO_UPD_ARF;
+ layer_flags[2] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
+ layer_flags[3] =
+ VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
+ VP8_EFLAG_NO_UPD_ARF;
+ break;
+ }
+ case 4: {
+ /**
+ * 3-layers structure.
+ * added dependency between the two TL2 frames (on top of case 3).
+ * 3-layers, 4-frame period.
+ */
+ static const int ids[4] = { 0, 2, 1, 2 };
+ cfg->ts_periodicity = 4;
+ *flag_periodicity = 4;
+ cfg->ts_number_layers = 3;
+ cfg->ts_rate_decimator[0] = 4;
+ cfg->ts_rate_decimator[1] = 2;
+ cfg->ts_rate_decimator[2] = 1;
+ memcpy(cfg->ts_layer_id, ids, sizeof(ids));
+
+ /**
+ * 0=L, 1=GF, 2=ARF, Intra-layer prediction disabled.
+ */
+ layer_flags[0] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
+ layer_flags[1] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
+ layer_flags[2] =
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
+ VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
+ layer_flags[3] =
+ VP8_EFLAG_NO_REF_LAST |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
+ VP8_EFLAG_NO_UPD_ARF;
+ break;
+ }
+ default:
+ /**
+ * do not change the layer_flags or the flag_periodicity in this case;
+ * it might be that the code is using external flags to be used.
+ */
+ break;
+
+ }
+}
+
+static int vpx_ts_param_parse(VPxContext *ctx, struct vpx_codec_enc_cfg *enccfg,
+ char *key, char *value, enum AVCodecID codec_id)
+{
+ size_t value_len = strlen(value);
+ int ts_layering_mode = 0;
+
+ if (!value_len)
+ return -1;
+
+ if (!strcmp(key, "ts_number_layers"))
+ enccfg->ts_number_layers = strtoul(value, &value, 10);
+ else if (!strcmp(key, "ts_target_bitrate")) {
+ if (codec_id == AV_CODEC_ID_VP8)
+ vp8_ts_parse_int_array(enccfg->ts_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS);
+#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER
+ if (codec_id == AV_CODEC_ID_VP9)
+ vp8_ts_parse_int_array(enccfg->layer_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS);
+#endif
+ } else if (!strcmp(key, "ts_rate_decimator")) {
+ vp8_ts_parse_int_array(enccfg->ts_rate_decimator, value, value_len, VPX_TS_MAX_LAYERS);
+ } else if (!strcmp(key, "ts_periodicity")) {
+ enccfg->ts_periodicity = strtoul(value, &value, 10);
+ } else if (!strcmp(key, "ts_layer_id")) {
+ vp8_ts_parse_int_array(enccfg->ts_layer_id, value, value_len, VPX_TS_MAX_PERIODICITY);
+ } else if (!strcmp(key, "ts_layering_mode")) {
+ /* option for pre-defined temporal structures in function set_temporal_layer_pattern. */
+ ts_layering_mode = strtoul(value, &value, 4);
+ }
+
+#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER
+ enccfg->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS; // only bypass mode is supported for now.
+ enccfg->ss_number_layers = 1; // TODO: add spatial scalability support.
+#endif
+ if (ts_layering_mode) {
+ // make sure the ts_layering_mode comes at the end of the ts_parameter string to ensure that
+ // correct configuration is done.
+ ctx->ts_layer_flags = av_malloc_array(VPX_TS_MAX_PERIODICITY, sizeof(*ctx->ts_layer_flags));
+ set_temporal_layer_pattern(ts_layering_mode, enccfg, ctx->ts_layer_flags, &enccfg->ts_periodicity);
+ }
+
+ return 0;
+}
+
+#if CONFIG_LIBVPX_VP9_ENCODER && defined(VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT)
+static int vpx_ref_frame_config_set_value(vpx_svc_ref_frame_config_t *ref_frame_config,
+ int ss_number_layers, char *key, char *value)
+{
+ size_t value_len = strlen(value);
+
+ if (!value_len)
+ return AVERROR(EINVAL);
+
+ if (!strcmp(key, "rfc_update_buffer_slot")) {
+ vp8_ts_parse_int_array(ref_frame_config->update_buffer_slot, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_update_last")) {
+ vp8_ts_parse_int_array(ref_frame_config->update_last, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_update_golden")) {
+ vp8_ts_parse_int_array(ref_frame_config->update_golden, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_update_alt_ref")) {
+ vp8_ts_parse_int_array(ref_frame_config->update_alt_ref, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_lst_fb_idx")) {
+ vp8_ts_parse_int_array(ref_frame_config->lst_fb_idx, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_gld_fb_idx")) {
+ vp8_ts_parse_int_array(ref_frame_config->gld_fb_idx, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_alt_fb_idx")) {
+ vp8_ts_parse_int_array(ref_frame_config->alt_fb_idx, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_reference_last")) {
+ vp8_ts_parse_int_array(ref_frame_config->reference_last, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_reference_golden")) {
+ vp8_ts_parse_int_array(ref_frame_config->reference_golden, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_reference_alt_ref")) {
+ vp8_ts_parse_int_array(ref_frame_config->reference_alt_ref, value, value_len, ss_number_layers);
+ } else if (!strcmp(key, "rfc_reference_duration")) {
+ vp8_ts_parse_int64_array(ref_frame_config->duration, value, value_len, ss_number_layers);
+ }
+
+ return 0;
+}
+
+static int vpx_parse_ref_frame_config_element(vpx_svc_ref_frame_config_t *ref_frame_config,
+ int ss_number_layers, const char **buf)
+{
+ const char key_val_sep[] = "=";
+ const char pairs_sep[] = ":";
+ char *key = av_get_token(buf, key_val_sep);
+ char *val = NULL;
+ int ret;
+
+ if (key && *key && strspn(*buf, key_val_sep)) {
+ (*buf)++;
+ val = av_get_token(buf, pairs_sep);
+ }
+
+ if (key && *key && val && *val)
+ ret = vpx_ref_frame_config_set_value(ref_frame_config, ss_number_layers, key, val);
+ else
+ ret = AVERROR(EINVAL);
+
+ av_freep(&key);
+ av_freep(&val);
+
+ return ret;
+}
+
+static int vpx_parse_ref_frame_config(vpx_svc_ref_frame_config_t *ref_frame_config,
+ int ss_number_layers, const char *str)
+{
+ int ret = 0;
+
+ while (*str) {
+ ret =
+ vpx_parse_ref_frame_config_element(ref_frame_config, ss_number_layers, &str);
+ if (ret < 0)
+ return ret;
+
+ if (*str)
+ str++;
+ }
+
+ return ret;
+}
+#endif
+