Source code

Revision control

Copy as Markdown

Other Tools

/*
* Copyright (c) 2010, Google, Inc.
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* VP8/9 encoder support via libvpx
*/
#include "config_components.h"
#define VPX_DISABLE_CTRL_TYPECHECKS 1
#define VPX_CODEC_DISABLE_COMPAT 1
#include <vpx/vpx_encoder.h>
#include <vpx/vp8cx.h>
#include "avcodec.h"
#include "codec_internal.h"
#include "encode.h"
#include "libavutil/avassert.h"
#include "libavutil/mem.h"
#include "libvpx.h"
#include "packet_internal.h"
#include "profiles.h"
#include "libavutil/avstring.h"
#include "libavutil/base64.h"
#include "libavutil/common.h"
#include "libavutil/cpu.h"
#include "libavutil/fifo.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#define IS_VP9(avctx) (CONFIG_LIBVPX_VP9_ENCODER && avctx->codec_id == AV_CODEC_ID_VP9)
#define IS_VP8(avctx) (CONFIG_LIBVPX_VP8_ENCODER && avctx->codec_id == AV_CODEC_ID_VP8)
/**
* Portion of struct vpx_codec_cx_pkt from vpx_encoder.h.
* One encoded frame returned from the library.
*/
struct FrameListData {
void *buf; /**< compressed data buffer */
size_t sz; /**< length of compressed data */
int64_t pts; /**< time stamp to show frame
(in timebase units) */
uint32_t flags; /**< flags for this frame */
uint64_t sse[4];
int have_sse; /**< true if we have pending sse[] */
struct FrameListData *next;
};
typedef struct FrameData {
int64_t pts;
int64_t duration;
void *frame_opaque;
AVBufferRef *frame_opaque_ref;
AVBufferRef *hdr10_plus;
} FrameData;
typedef struct VPxEncoderContext {
AVClass *class;
struct vpx_codec_ctx encoder;
struct vpx_image rawimg;
struct vpx_codec_ctx encoder_alpha;
struct vpx_image rawimg_alpha;
uint8_t is_alpha;
struct vpx_fixed_buf twopass_stats;
unsigned twopass_stats_size;
int deadline; //i.e., RT/GOOD/BEST
uint64_t sse[4];
int have_sse; /**< true if we have pending sse[] */
struct FrameListData *coded_frame_list;
struct FrameListData *alpha_coded_frame_list;
int cpu_used;
int sharpness;
/**
* VP8 specific flags, see VP8F_* below.
*/
int flags;
#define VP8F_ERROR_RESILIENT 0x00000001 ///< Enable measures appropriate for streaming over lossy links
#define VP8F_AUTO_ALT_REF 0x00000002 ///< Enable automatic alternate reference frame generation
int auto_alt_ref;
int arnr_max_frames;
int arnr_strength;
int arnr_type;
int tune;
int lag_in_frames;
int error_resilient;
int crf;
int static_thresh;
int max_intra_rate;
int rc_undershoot_pct;
int rc_overshoot_pct;
AVDictionary *vpx_ts_parameters;
int *ts_layer_flags;
int current_temporal_idx;
// VP8-only
int screen_content_mode;
// VP9-only
int lossless;
int tile_columns;
int tile_rows;
int frame_parallel;
int aq_mode;
int drop_threshold;
int noise_sensitivity;
int vpx_cs;
float level;
int row_mt;
int tune_content;
int corpus_complexity;
int tpl_model;
int min_gf_interval;
// This FIFO is used to propagate various properties from frames to packets.
AVFifo *fifo;
/**
* If the driver does not support ROI then warn the first time we
* encounter a frame with ROI side data.
*/
int roi_warned;
#if CONFIG_LIBVPX_VP9_ENCODER && defined(VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT)
vpx_svc_ref_frame_config_t ref_frame_config;
#endif
} VPxContext;
/** String mappings for enum vp8e_enc_control_id */
static const char *const ctlidstr[] = {
[VP8E_SET_CPUUSED] = "VP8E_SET_CPUUSED",
[VP8E_SET_ENABLEAUTOALTREF] = "VP8E_SET_ENABLEAUTOALTREF",
[VP8E_SET_NOISE_SENSITIVITY] = "VP8E_SET_NOISE_SENSITIVITY",
[VP8E_SET_STATIC_THRESHOLD] = "VP8E_SET_STATIC_THRESHOLD",
[VP8E_SET_TOKEN_PARTITIONS] = "VP8E_SET_TOKEN_PARTITIONS",
[VP8E_SET_ARNR_MAXFRAMES] = "VP8E_SET_ARNR_MAXFRAMES",
[VP8E_SET_ARNR_STRENGTH] = "VP8E_SET_ARNR_STRENGTH",
[VP8E_SET_ARNR_TYPE] = "VP8E_SET_ARNR_TYPE",
[VP8E_SET_TUNING] = "VP8E_SET_TUNING",
[VP8E_SET_CQ_LEVEL] = "VP8E_SET_CQ_LEVEL",
[VP8E_SET_MAX_INTRA_BITRATE_PCT] = "VP8E_SET_MAX_INTRA_BITRATE_PCT",
[VP8E_SET_SHARPNESS] = "VP8E_SET_SHARPNESS",
[VP8E_SET_TEMPORAL_LAYER_ID] = "VP8E_SET_TEMPORAL_LAYER_ID",
[VP8E_SET_SCREEN_CONTENT_MODE] = "VP8E_SET_SCREEN_CONTENT_MODE",
#if CONFIG_LIBVPX_VP9_ENCODER
[VP9E_SET_LOSSLESS] = "VP9E_SET_LOSSLESS",
[VP9E_SET_TILE_COLUMNS] = "VP9E_SET_TILE_COLUMNS",
[VP9E_SET_TILE_ROWS] = "VP9E_SET_TILE_ROWS",
[VP9E_SET_FRAME_PARALLEL_DECODING] = "VP9E_SET_FRAME_PARALLEL_DECODING",
[VP9E_SET_AQ_MODE] = "VP9E_SET_AQ_MODE",
[VP9E_SET_COLOR_SPACE] = "VP9E_SET_COLOR_SPACE",
[VP9E_SET_SVC_LAYER_ID] = "VP9E_SET_SVC_LAYER_ID",
#if VPX_ENCODER_ABI_VERSION >= 12
[VP9E_SET_SVC_PARAMETERS] = "VP9E_SET_SVC_PARAMETERS",
[VP9E_SET_SVC_REF_FRAME_CONFIG] = "VP9E_SET_SVC_REF_FRAME_CONFIG",
#endif
[VP9E_SET_SVC] = "VP9E_SET_SVC",
#if VPX_ENCODER_ABI_VERSION >= 11
[VP9E_SET_COLOR_RANGE] = "VP9E_SET_COLOR_RANGE",
#endif
#if VPX_ENCODER_ABI_VERSION >= 12
[VP9E_SET_TARGET_LEVEL] = "VP9E_SET_TARGET_LEVEL",
[VP9E_GET_LEVEL] = "VP9E_GET_LEVEL",
#endif
#ifdef VPX_CTRL_VP9E_SET_ROW_MT
[VP9E_SET_ROW_MT] = "VP9E_SET_ROW_MT",
#endif
#ifdef VPX_CTRL_VP9E_SET_TUNE_CONTENT
[VP9E_SET_TUNE_CONTENT] = "VP9E_SET_TUNE_CONTENT",
#endif
#ifdef VPX_CTRL_VP9E_SET_TPL
[VP9E_SET_TPL] = "VP9E_SET_TPL",
#endif
#ifdef VPX_CTRL_VP9E_SET_MIN_GF_INTERVAL
[VP9E_SET_MIN_GF_INTERVAL] = "VP9E_SET_MIN_GF_INTERVAL",
#endif
#endif
};
static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc)
{
VPxContext *ctx = avctx->priv_data;
const char *error = vpx_codec_error(&ctx->encoder);
const char *detail = vpx_codec_error_detail(&ctx->encoder);
av_log(avctx, AV_LOG_ERROR, "%s: %s\n", desc, error);
if (detail)
av_log(avctx, AV_LOG_ERROR, " Additional information: %s\n", detail);
}
static av_cold void dump_enc_cfg(AVCodecContext *avctx,
const struct vpx_codec_enc_cfg *cfg,
int level)
{
int width = -30;
int i;
av_log(avctx, level, "vpx_codec_enc_cfg\n");
av_log(avctx, level, "generic settings\n"
" %*s%u\n %*s%u\n %*s%u\n %*s%u\n %*s%u\n"
#if CONFIG_LIBVPX_VP9_ENCODER
" %*s%u\n %*s%u\n"
#endif
" %*s{%u/%u}\n %*s%u\n %*s%d\n %*s%u\n",
width, "g_usage:", cfg->g_usage,
width, "g_threads:", cfg->g_threads,
width, "g_profile:", cfg->g_profile,
width, "g_w:", cfg->g_w,
width, "g_h:", cfg->g_h,
#if CONFIG_LIBVPX_VP9_ENCODER
width, "g_bit_depth:", cfg->g_bit_depth,
width, "g_input_bit_depth:", cfg->g_input_bit_depth,
#endif
width, "g_timebase:", cfg->g_timebase.num, cfg->g_timebase.den,
width, "g_error_resilient:", cfg->g_error_resilient,
width, "g_pass:", cfg->g_pass,
width, "g_lag_in_frames:", cfg->g_lag_in_frames);
av_log(avctx, level, "rate control settings\n"
" %*s%u\n %*s%u\n %*s%u\n %*s%u\n"
" %*s%d\n %*s%p(%"SIZE_SPECIFIER")\n %*s%u\n",
width, "rc_dropframe_thresh:", cfg->rc_dropframe_thresh,
width, "rc_resize_allowed:", cfg->rc_resize_allowed,
width, "rc_resize_up_thresh:", cfg->rc_resize_up_thresh,
width, "rc_resize_down_thresh:", cfg->rc_resize_down_thresh,
width, "rc_end_usage:", cfg->rc_end_usage,
width, "rc_twopass_stats_in:", cfg->rc_twopass_stats_in.buf, cfg->rc_twopass_stats_in.sz,
width, "rc_target_bitrate:", cfg->rc_target_bitrate);
av_log(avctx, level, "quantizer settings\n"
" %*s%u\n %*s%u\n",
width, "rc_min_quantizer:", cfg->rc_min_quantizer,
width, "rc_max_quantizer:", cfg->rc_max_quantizer);
av_log(avctx, level, "bitrate tolerance\n"
" %*s%u\n %*s%u\n",
width, "rc_undershoot_pct:", cfg->rc_undershoot_pct,
width, "rc_overshoot_pct:", cfg->rc_overshoot_pct);
av_log(avctx, level, "temporal layering settings\n"
" %*s%u\n", width, "ts_number_layers:", cfg->ts_number_layers);
if (avctx->codec_id == AV_CODEC_ID_VP8) {
av_log(avctx, level,
"\n %*s", width, "ts_target_bitrate:");
for (i = 0; i < VPX_TS_MAX_LAYERS; i++)
av_log(avctx, level,
"%u ", cfg->ts_target_bitrate[i]);
}
#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER
if (avctx->codec_id == AV_CODEC_ID_VP9) {
av_log(avctx, level,
"\n %*s", width, "layer_target_bitrate:");
for (i = 0; i < VPX_TS_MAX_LAYERS; i++)
av_log(avctx, level,
"%u ", cfg->layer_target_bitrate[i]);
}
#endif
av_log(avctx, level, "\n");
av_log(avctx, level,
"\n %*s", width, "ts_rate_decimator:");
for (i = 0; i < VPX_TS_MAX_LAYERS; i++)
av_log(avctx, level, "%u ", cfg->ts_rate_decimator[i]);
av_log(avctx, level, "\n");
av_log(avctx, level,
"\n %*s%u\n", width, "ts_periodicity:", cfg->ts_periodicity);
av_log(avctx, level,
"\n %*s", width, "ts_layer_id:");
for (i = 0; i < VPX_TS_MAX_PERIODICITY; i++)
av_log(avctx, level, "%u ", cfg->ts_layer_id[i]);
av_log(avctx, level, "\n");
av_log(avctx, level, "decoder buffer model\n"
" %*s%u\n %*s%u\n %*s%u\n",
width, "rc_buf_sz:", cfg->rc_buf_sz,
width, "rc_buf_initial_sz:", cfg->rc_buf_initial_sz,
width, "rc_buf_optimal_sz:", cfg->rc_buf_optimal_sz);
av_log(avctx, level, "2 pass rate control settings\n"
" %*s%u\n %*s%u\n %*s%u\n",
width, "rc_2pass_vbr_bias_pct:", cfg->rc_2pass_vbr_bias_pct,
width, "rc_2pass_vbr_minsection_pct:", cfg->rc_2pass_vbr_minsection_pct,
width, "rc_2pass_vbr_maxsection_pct:", cfg->rc_2pass_vbr_maxsection_pct);
#if VPX_ENCODER_ABI_VERSION >= 14
av_log(avctx, level, " %*s%u\n",
width, "rc_2pass_vbr_corpus_complexity:", cfg->rc_2pass_vbr_corpus_complexity);
#endif
av_log(avctx, level, "keyframing settings\n"
" %*s%d\n %*s%u\n %*s%u\n",
width, "kf_mode:", cfg->kf_mode,
width, "kf_min_dist:", cfg->kf_min_dist,
width, "kf_max_dist:", cfg->kf_max_dist);
av_log(avctx, level, "\n");
}
static void coded_frame_add(void *list, struct FrameListData *cx_frame)
{
struct FrameListData **p = list;
while (*p)
p = &(*p)->next;
*p = cx_frame;
cx_frame->next = NULL;
}
static av_cold void free_coded_frame(struct FrameListData *cx_frame)
{
av_freep(&cx_frame->buf);
av_freep(&cx_frame);
}
static av_cold void free_frame_list(struct FrameListData *list)
{
struct FrameListData *p = list;
while (p) {
list = list->next;
free_coded_frame(p);
p = list;
}
}
static void frame_data_uninit(FrameData *fd)
{
av_buffer_unref(&fd->frame_opaque_ref);
av_buffer_unref(&fd->hdr10_plus);
}
static av_cold void fifo_free(AVFifo **fifo)
{
FrameData fd;
while (av_fifo_read(*fifo, &fd, 1) >= 0)
frame_data_uninit(&fd);
av_fifo_freep2(fifo);
}
static int frame_data_submit(AVCodecContext *avctx, AVFifo *fifo,
const AVFrame *frame)
{
VPxContext *ctx = avctx->priv_data;
const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc;
FrameData fd = { .pts = frame->pts };
int ret;
if (IS_VP9(avctx) &&
// Keep HDR10+ if it has bit depth higher than 8 and
// it has PQ trc (SMPTE2084).
enccfg->g_bit_depth > 8 && avctx->color_trc == AVCOL_TRC_SMPTE2084) {
const AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS);
if (sd) {
fd.hdr10_plus = av_buffer_ref(sd->buf);
if (!fd.hdr10_plus)
return AVERROR(ENOMEM);
}
}
fd.duration = frame->duration;
fd.frame_opaque = frame->opaque;
if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE && frame->opaque_ref) {
ret = av_buffer_replace(&fd.frame_opaque_ref, frame->opaque_ref);
if (ret < 0)
goto fail;
}
ret = av_fifo_write(fifo, &fd, 1);
if (ret < 0)
goto fail;
return 0;
fail:
frame_data_uninit(&fd);
return ret;
}
static int frame_data_apply(AVCodecContext *avctx, AVFifo *fifo, AVPacket *pkt)
{
FrameData fd;
uint8_t *data;
int ret = 0;
if (av_fifo_peek(fifo, &fd, 1, 0) < 0)
return 0;
if (fd.pts != pkt->pts) {
av_log(avctx, AV_LOG_WARNING,
"Mismatching timestamps: libvpx %"PRId64" queued %"PRId64"; "
"this is a bug, please report it\n", pkt->pts, fd.pts);
goto skip;
}
pkt->duration = fd.duration;
if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {
pkt->opaque = fd.frame_opaque;
pkt->opaque_ref = fd.frame_opaque_ref;
fd.frame_opaque_ref = NULL;
}
if (fd.hdr10_plus) {
data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, fd.hdr10_plus->size);
if (!data) {
ret = AVERROR(ENOMEM);
goto skip;
}
memcpy(data, fd.hdr10_plus->data, fd.hdr10_plus->size);
}
skip:
av_fifo_drain2(fifo, 1);
frame_data_uninit(&fd);
return ret;
}
static av_cold int codecctl_int(AVCodecContext *avctx,
enum vp8e_enc_control_id id, int val)
{
VPxContext *ctx = avctx->priv_data;
char buf[80];
int width = -30;
int res;
snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]);
av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, val);
res = vpx_codec_control(&ctx->encoder, id, val);
if (res != VPX_CODEC_OK) {
snprintf(buf, sizeof(buf), "Failed to set %s codec control",
ctlidstr[id]);
log_encoder_error(avctx, buf);
return AVERROR(EINVAL);
}
if (ctx->is_alpha) {
int res_alpha = vpx_codec_control(&ctx->encoder_alpha, id, val);
if (res_alpha != VPX_CODEC_OK) {
snprintf(buf, sizeof(buf), "Failed to set %s alpha codec control",
ctlidstr[id]);
log_encoder_error(avctx, buf);
return AVERROR(EINVAL);
}
}
return 0;
}
#if VPX_ENCODER_ABI_VERSION >= 12
static av_cold int codecctl_intp(AVCodecContext *avctx,
enum vp8e_enc_control_id id, int *val)
{
VPxContext *ctx = avctx->priv_data;
char buf[80];
int width = -30;
int res;
snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]);
av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, *val);
res = vpx_codec_control(&ctx->encoder, id, val);
if (res != VPX_CODEC_OK) {
snprintf(buf, sizeof(buf), "Failed to set %s codec control",
ctlidstr[id]);
log_encoder_error(avctx, buf);
return AVERROR(EINVAL);
}
if (ctx->is_alpha) {
int res_alpha = vpx_codec_control(&ctx->encoder_alpha, id, val);
if (res_alpha != VPX_CODEC_OK) {
snprintf(buf, sizeof(buf), "Failed to set %s alpha codec control",
ctlidstr[id]);
log_encoder_error(avctx, buf);
return AVERROR(EINVAL);
}
}
return 0;
}
#endif
static av_cold int vpx_free(AVCodecContext *avctx)
{
VPxContext *ctx = avctx->priv_data;
#if VPX_ENCODER_ABI_VERSION >= 12
if (avctx->codec_id == AV_CODEC_ID_VP9 && ctx->level >= 0 &&
!(avctx->flags & AV_CODEC_FLAG_PASS1)) {
int level_out = 0;
if (!codecctl_intp(avctx, VP9E_GET_LEVEL, &level_out))
av_log(avctx, AV_LOG_INFO, "Encoded level %.1f\n", level_out * 0.1);
}
#endif
av_freep(&ctx->ts_layer_flags);
vpx_codec_destroy(&ctx->encoder);
if (ctx->is_alpha) {
vpx_codec_destroy(&ctx->encoder_alpha);
av_freep(&ctx->rawimg_alpha.planes[VPX_PLANE_U]);
av_freep(&ctx->rawimg_alpha.planes[VPX_PLANE_V]);
}
av_freep(&ctx->twopass_stats.buf);
av_freep(&avctx->stats_out);
free_frame_list(ctx->coded_frame_list);
free_frame_list(ctx->alpha_coded_frame_list);
if (ctx->fifo)
fifo_free(&ctx->fifo);
return 0;
}
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, 10);
}
#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
#if CONFIG_LIBVPX_VP9_ENCODER
static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps,
struct vpx_codec_enc_cfg *enccfg, vpx_codec_flags_t *flags,
vpx_img_fmt_t *img_fmt)
{
VPxContext *ctx = avctx->priv_data;
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
enccfg->g_bit_depth = enccfg->g_input_bit_depth = desc->comp[0].depth;
switch (avctx->pix_fmt) {
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUVA420P:
enccfg->g_profile = 0;
*img_fmt = VPX_IMG_FMT_I420;
return 0;
case AV_PIX_FMT_YUV422P:
enccfg->g_profile = 1;
*img_fmt = VPX_IMG_FMT_I422;
return 0;
case AV_PIX_FMT_YUV440P:
enccfg->g_profile = 1;
*img_fmt = VPX_IMG_FMT_I440;
return 0;
case AV_PIX_FMT_GBRP:
ctx->vpx_cs = VPX_CS_SRGB;
case AV_PIX_FMT_YUV444P:
enccfg->g_profile = 1;
*img_fmt = VPX_IMG_FMT_I444;
return 0;
case AV_PIX_FMT_YUV420P10:
case AV_PIX_FMT_YUV420P12:
if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) {
enccfg->g_profile = 2;
*img_fmt = VPX_IMG_FMT_I42016;
*flags |= VPX_CODEC_USE_HIGHBITDEPTH;
return 0;
}
break;
case AV_PIX_FMT_YUV422P10:
case AV_PIX_FMT_YUV422P12:
if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) {
enccfg->g_profile = 3;
*img_fmt = VPX_IMG_FMT_I42216;
*flags |= VPX_CODEC_USE_HIGHBITDEPTH;
return 0;
}
break;
case AV_PIX_FMT_YUV440P10:
case AV_PIX_FMT_YUV440P12:
if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) {
enccfg->g_profile = 3;
*img_fmt = VPX_IMG_FMT_I44016;
*flags |= VPX_CODEC_USE_HIGHBITDEPTH;
return 0;
}
break;
case AV_PIX_FMT_GBRP10:
case AV_PIX_FMT_GBRP12:
ctx->vpx_cs = VPX_CS_SRGB;
case AV_PIX_FMT_YUV444P10:
case AV_PIX_FMT_YUV444P12:
if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) {
enccfg->g_profile = 3;
*img_fmt = VPX_IMG_FMT_I44416;
*flags |= VPX_CODEC_USE_HIGHBITDEPTH;
return 0;
}
break;
default:
break;
}
av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format.\n");
return AVERROR_INVALIDDATA;
}
static void set_colorspace(AVCodecContext *avctx)
{
enum vpx_color_space vpx_cs;
VPxContext *ctx = avctx->priv_data;
if (ctx->vpx_cs) {
vpx_cs = ctx->vpx_cs;
} else {
switch (avctx->colorspace) {
case AVCOL_SPC_RGB: vpx_cs = VPX_CS_SRGB; break;
case AVCOL_SPC_BT709: vpx_cs = VPX_CS_BT_709; break;
case AVCOL_SPC_UNSPECIFIED: vpx_cs = VPX_CS_UNKNOWN; break;
case AVCOL_SPC_RESERVED: vpx_cs = VPX_CS_RESERVED; break;
case AVCOL_SPC_BT470BG: vpx_cs = VPX_CS_BT_601; break;
case AVCOL_SPC_SMPTE170M: vpx_cs = VPX_CS_SMPTE_170; break;
case AVCOL_SPC_SMPTE240M: vpx_cs = VPX_CS_SMPTE_240; break;
case AVCOL_SPC_BT2020_NCL: vpx_cs = VPX_CS_BT_2020; break;
default:
av_log(avctx, AV_LOG_WARNING, "Unsupported colorspace (%d)\n",
avctx->colorspace);
return;
}
}
codecctl_int(avctx, VP9E_SET_COLOR_SPACE, vpx_cs);
}
#if VPX_ENCODER_ABI_VERSION >= 11
static void set_color_range(AVCodecContext *avctx)
{
enum vpx_color_range vpx_cr;
switch (avctx->color_range) {
case AVCOL_RANGE_UNSPECIFIED:
case AVCOL_RANGE_MPEG: vpx_cr = VPX_CR_STUDIO_RANGE; break;
case AVCOL_RANGE_JPEG: vpx_cr = VPX_CR_FULL_RANGE; break;
default:
av_log(avctx, AV_LOG_WARNING, "Unsupported color range (%d)\n",
avctx->color_range);
return;
}
codecctl_int(avctx, VP9E_SET_COLOR_RANGE, vpx_cr);