add scalability experiment
cmake: -DCONFIG_SCALABILITY=1
Change-Id: Ifa908f809bcf904bdf0ed87b351e1ef3accc2b3f
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 743dd0c..168cbf7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -330,6 +330,12 @@
"${AOM_ROOT}/examples/simple_decoder.c"
$<TARGET_OBJECTS:aom_common_app_util>
$<TARGET_OBJECTS:aom_decoder_app_util>)
+ if (CONFIG_SCALABILITY)
+ add_executable(scalable_decoder
+ "${AOM_ROOT}/examples/scalable_decoder.c"
+ $<TARGET_OBJECTS:aom_common_app_util>
+ $<TARGET_OBJECTS:aom_decoder_app_util>)
+ endif ()
if (CONFIG_ANALYZER)
add_executable(analyzer
@@ -370,6 +376,11 @@
set(AOM_DECODER_EXAMPLE_TARGETS ${AOM_DECODER_EXAMPLE_TARGETS}
aomdec decode_to_md5 decode_with_drops simple_decoder)
+ if (CONFIG_SCALABILITY)
+ set(AOM_DECODER_EXAMPLE_TARGETS ${AOM_DECODER_EXAMPLE_TARGETS}
+ scalable_decoder)
+ endif ()
+
# Add decoder examples to the app targets list.
set(AOM_APP_TARGETS ${AOM_APP_TARGETS} ${AOM_DECODER_EXAMPLE_TARGETS})
endif ()
@@ -397,10 +408,22 @@
"${AOM_ROOT}/examples/twopass_encoder.c"
$<TARGET_OBJECTS:aom_common_app_util>
$<TARGET_OBJECTS:aom_encoder_app_util>)
+ if (CONFIG_SCALABILITY)
+ add_executable(scalable_encoder
+ "${AOM_ROOT}/examples/scalable_encoder.c"
+ $<TARGET_OBJECTS:aom_common_app_util>
+ $<TARGET_OBJECTS:aom_encoder_app_util>)
+ endif ()
# Maintain a list of encoder example targets.
set(AOM_ENCODER_EXAMPLE_TARGETS
aomenc lossless_encoder set_maps simple_encoder twopass_encoder)
+
+ if (CONFIG_SCALABILITY)
+ set(AOM_ENCODER_EXAMPLE_TARGETS ${AOM_ENCODER_EXAMPLE_TARGETS}
+ scalable_encoder)
+ endif ()
+
endif ()
if (ENABLE_TOOLS)
diff --git a/aom/aom_codec.h b/aom/aom_codec.h
index 976c2b0..6800bb2 100644
--- a/aom/aom_codec.h
+++ b/aom/aom_codec.h
@@ -496,6 +496,7 @@
OBU_METADATA_TYPE_PRIVATE_DATA = 0,
OBU_METADATA_TYPE_HDR_CLL = 1,
OBU_METADATA_TYPE_HDR_MDCV = 2,
+ OBU_METADATA_TYPE_SCALABILITY = 3,
} OBU_METADATA_TYPE;
/*!@} - end defgroup codec*/
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h
index 9525283..d92e43a 100644
--- a/aom/aom_decoder.h
+++ b/aom/aom_decoder.h
@@ -83,9 +83,10 @@
* stream.
*/
typedef struct aom_codec_stream_info {
- unsigned int w; /**< Width (or 0 for unknown/default) */
- unsigned int h; /**< Height (or 0 for unknown/default) */
- unsigned int is_kf; /**< Current frame is a keyframe */
+ unsigned int w; /**< Width (or 0 for unknown/default) */
+ unsigned int h; /**< Height (or 0 for unknown/default) */
+ unsigned int is_kf; /**< Current frame is a keyframe */
+ unsigned int enhancement_layers_cnt; /**< Enhancement layers */
} aom_codec_stream_info_t;
/* REQUIRED FUNCTIONS
diff --git a/aom/aom_image.h b/aom/aom_image.h
index f94582e..3636ae1 100644
--- a/aom/aom_image.h
+++ b/aom/aom_image.h
@@ -213,6 +213,9 @@
int bps; /**< bits per sample (for packed formats) */
+ int temporal_id; /**< Temporal layer Id of image */
+ int enhancement_id; /**< Spatial layer Id of image */
+
/*!\brief The following member may be set by the application to associate
* data with this image.
*/
diff --git a/aom/aomcx.h b/aom/aomcx.h
index d48b796..1cedd58 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -143,6 +143,10 @@
*/
AOME_SET_SCALEMODE = 11,
+ /*!\brief Codec control function to set encoder enhancement layer id.
+ */
+ AOME_SET_ENHANCEMENT_LAYER_ID = 12,
+
/*!\brief Codec control function to set encoder internal speed settings.
*
* Changes in this value influences, among others, the encoder's selection
@@ -215,6 +219,10 @@
*/
AOME_SET_MAX_INTRA_BITRATE_PCT,
+ /*!\brief Codec control function to set number of spatial layers.
+ */
+ AOME_SET_NUMBER_SPATIAL_LAYERS,
+
/*!\brief Codec control function to set max data rate for Inter frames.
*
* This value controls additional clamping on the maximum size of an
@@ -798,6 +806,9 @@
AOM_CTRL_USE_TYPE(AOME_SET_SCALEMODE, aom_scaling_mode_t *)
#define AOM_CTRL_AOME_SET_SCALEMODE
+AOM_CTRL_USE_TYPE(AOME_SET_ENHANCEMENT_LAYER_ID, int)
+#define AOM_CTRL_AOME_SET_ENHANCEMENT_LAYER_ID
+
AOM_CTRL_USE_TYPE(AOME_SET_CPUUSED, int)
#define AOM_CTRL_AOME_SET_CPUUSED
AOM_CTRL_USE_TYPE(AOME_SET_DEVSF, int)
@@ -847,6 +858,9 @@
AOM_CTRL_USE_TYPE(AOME_SET_MAX_INTER_BITRATE_PCT, unsigned int)
#define AOM_CTRL_AOME_SET_MAX_INTER_BITRATE_PCT
+AOM_CTRL_USE_TYPE(AOME_SET_NUMBER_SPATIAL_LAYERS, int)
+#define AOME_CTRL_AOME_SET_NUMBER_SPATIAL_LAYERS
+
AOM_CTRL_USE_TYPE(AV1E_SET_GF_CBR_BOOST_PCT, unsigned int)
#define AOM_CTRL_AV1E_SET_GF_CBR_BOOST_PCT
diff --git a/aomdec.c b/aomdec.c
index 097d6fe..5f9181f 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -255,8 +255,12 @@
#if CONFIG_OBU_NO_IVF
case FILE_TYPE_OBU:
return obu_read_temporal_unit(input->aom_input_ctx->file, buf,
+#if CONFIG_SCALABILITY
+ bytes_in_buffer, buffer_size, 0);
+#else
bytes_in_buffer, buffer_size);
#endif
+#endif
default: return 1;
}
}
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 43e5361..024926f 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -29,6 +29,10 @@
#define MAG_SIZE (4)
#define MAX_INDEX_SIZE (256)
+#if CONFIG_SCALABILITY
+#define MAX_NUM_ENHANCEMENT_LAYERS 128
+#endif
+
struct av1_extracfg {
int cpu_used; // available cpu percentage in 1/16
int dev_sf;
@@ -1422,28 +1426,34 @@
pkt.data.frame.partition_id = -1;
#if CONFIG_OBU
- // move data PRE_OBU_SIZE_BYTES + 1 bytes and insert OBU_TD preceded by
- // 4 byte size
- uint32_t obu_size = 1;
- if (ctx->pending_cx_data) {
- const size_t index_sz = PRE_OBU_SIZE_BYTES + 1;
- memmove(ctx->pending_cx_data + index_sz, ctx->pending_cx_data,
- ctx->pending_cx_data_sz);
- }
- obu_size = write_obu_header(
- OBU_TEMPORAL_DELIMITER, 0,
- (uint8_t *)(ctx->pending_cx_data + PRE_OBU_SIZE_BYTES));
- obu_size += write_temporal_delimiter_obu();
+ int write_temporal_delimiter = 1;
+#if CONFIG_SCALABILITY
+ // only write OBU_TD if base layer
+ write_temporal_delimiter = !cpi->common.enhancement_layer_id;
+#endif // CONFIG_SCALABILITY
+ if (write_temporal_delimiter) {
+ // move data PRE_OBU_SIZE_BYTES + 1 bytes and insert OBU_TD preceded by
+ // optional 4 byte size
+ uint32_t obu_size = 1;
+ if (ctx->pending_cx_data) {
+ const size_t index_sz = PRE_OBU_SIZE_BYTES + 1;
+ memmove(ctx->pending_cx_data + index_sz, ctx->pending_cx_data,
+ ctx->pending_cx_data_sz);
+ }
+ obu_size = write_obu_header(
+ OBU_TEMPORAL_DELIMITER, 0,
+ (uint8_t *)(ctx->pending_cx_data + PRE_OBU_SIZE_BYTES));
+ obu_size += write_temporal_delimiter_obu();
#if CONFIG_OBU_SIZING
- // OBUs are preceded by an unsigned leb128 coded unsigned integer padded
- // to PRE_OBU_SIZE_BYTES bytes.
- if (write_uleb_obu_size(obu_size, ctx->pending_cx_data) != AOM_CODEC_OK)
- return AOM_CODEC_ERROR;
+ // OBUs are preceded by an unsigned leb128 coded unsigned integer padded
+ // to PRE_OBU_SIZE_BYTES bytes.
+ if (write_uleb_obu_size(obu_size, ctx->pending_cx_data) != AOM_CODEC_OK)
+ return AOM_CODEC_ERROR;
#else
- mem_put_le32(ctx->pending_cx_data, obu_size);
+ mem_put_le32(ctx->pending_cx_data, obu_size);
#endif // CONFIG_OBU_SIZING
-
- pkt.data.frame.sz += (obu_size + PRE_OBU_SIZE_BYTES);
+ pkt.data.frame.sz += (obu_size + PRE_OBU_SIZE_BYTES);
+ }
#endif // CONFIG_OBU
pkt.data.frame.pts = ticks_to_timebase_units(timebase, dst_time_stamp);
@@ -1610,6 +1620,36 @@
}
}
+static aom_codec_err_t ctrl_set_enhancement_layer_id(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+#if CONFIG_SCALABILITY
+ const int enhancement_layer_id = va_arg(args, int);
+ if (enhancement_layer_id > MAX_NUM_ENHANCEMENT_LAYERS)
+ return AOM_CODEC_INVALID_PARAM;
+ ctx->cpi->common.enhancement_layer_id = enhancement_layer_id;
+ return AOM_CODEC_OK;
+#else
+ (void)ctx;
+ (void)args;
+ return AOM_CODEC_UNSUP_FEATURE;
+#endif
+}
+
+static aom_codec_err_t ctrl_set_number_spatial_layers(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+#if CONFIG_SCALABILITY
+ const int number_spatial_layers = va_arg(args, int);
+ if (number_spatial_layers > MAX_NUM_ENHANCEMENT_LAYERS)
+ return AOM_CODEC_INVALID_PARAM;
+ ctx->cpi->common.enhancement_layers_cnt = number_spatial_layers - 1;
+ return AOM_CODEC_OK;
+#else
+ (void)ctx;
+ (void)args;
+ return AOM_CODEC_UNSUP_FEATURE;
+#endif
+}
+
static aom_codec_err_t ctrl_set_tune_content(aom_codec_alg_priv_t *ctx,
va_list args) {
struct av1_extracfg extra_cfg = ctx->extra_cfg;
@@ -1709,6 +1749,7 @@
{ AOME_SET_ROI_MAP, ctrl_set_roi_map },
{ AOME_SET_ACTIVEMAP, ctrl_set_active_map },
{ AOME_SET_SCALEMODE, ctrl_set_scale_mode },
+ { AOME_SET_ENHANCEMENT_LAYER_ID, ctrl_set_enhancement_layer_id },
{ AOME_SET_CPUUSED, ctrl_set_cpuused },
{ AOME_SET_DEVSF, ctrl_set_devsf },
{ AOME_SET_ENABLEAUTOALTREF, ctrl_set_enable_auto_alt_ref },
@@ -1733,6 +1774,7 @@
{ AOME_SET_TUNING, ctrl_set_tuning },
{ AOME_SET_CQ_LEVEL, ctrl_set_cq_level },
{ AOME_SET_MAX_INTRA_BITRATE_PCT, ctrl_set_rc_max_intra_bitrate_pct },
+ { AOME_SET_NUMBER_SPATIAL_LAYERS, ctrl_set_number_spatial_layers },
{ AV1E_SET_MAX_INTER_BITRATE_PCT, ctrl_set_rc_max_inter_bitrate_pct },
{ AV1E_SET_GF_CBR_BOOST_PCT, ctrl_set_rc_gf_cbr_boost_pct },
{ AV1E_SET_LOSSLESS, ctrl_set_lossless },
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 8b2d254..6ede519 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -20,6 +20,9 @@
#include "aom/aom_decoder.h"
#include "aom_dsp/bitreader_buffer.h"
#include "aom_dsp/aom_dsp_common.h"
+#if CONFIG_OBU
+#include "aom_ports/mem_ops.h"
+#endif
#include "aom_util/aom_thread.h"
#include "av1/common/alloccommon.h"
@@ -327,10 +330,33 @@
{
#if CONFIG_OBU
- // Proper fix needed
si->is_kf = 1;
intra_only_flag = 1;
si->h = 1;
+
+ struct aom_read_bit_buffer rb = { data + PRE_OBU_SIZE_BYTES, data + data_sz,
+ 0, NULL, NULL };
+ mem_get_le32(data);
+ aom_rb_read_literal(&rb, 8); // obu_header
+ av1_read_profile(&rb); // profile
+ aom_rb_read_literal(&rb, 4); // level
+#if CONFIG_SCALABILITY
+ int i;
+ si->enhancement_layers_cnt = aom_rb_read_literal(&rb, 2);
+ for (i = 1; i <= (int)si->enhancement_layers_cnt; i++) {
+ aom_rb_read_literal(&rb, 4); // level for each enhancement layer
+ }
+#endif // CONFIG_SCALABILITY
+
+#if CONFIG_FRAME_SIZE
+ int num_bits_width = aom_rb_read_literal(&rb, 4) + 1;
+ int num_bits_height = aom_rb_read_literal(&rb, 4) + 1;
+ int max_frame_width = aom_rb_read_literal(&rb, num_bits_width) + 1;
+ int max_frame_height = aom_rb_read_literal(&rb, num_bits_height) + 1;
+ si->w = max_frame_width;
+ si->h = max_frame_height;
+#endif // CONFIG_FRAME_SIZE
+
#else
int show_frame;
int error_resilient;
@@ -989,6 +1015,10 @@
ctx->img.fb_priv = frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv;
img = &ctx->img;
+#if CONFIG_SCALABILITY
+ img->temporal_id = cm->temporal_layer_id;
+ img->enhancement_id = cm->enhancement_layer_id;
+#endif
#if CONFIG_FILM_GRAIN
return add_grain_if_needed(
img, ctx->image_with_grain,
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index fd5c6cb..eb79e5a 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -608,6 +608,12 @@
DqType dq_type;
#endif // CONFIG_NEW_QUANT
+#if CONFIG_SCALABILITY
+ int temporal_layer_id;
+ int enhancement_layer_id;
+ int enhancement_layers_cnt;
+#endif
+
#if TXCOEFF_TIMER
int64_t cum_txcoeff_timer;
int64_t txcoeff_timer;
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index 89e9eeb..e1ed8eb 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -21,8 +21,29 @@
#include "av1/decoder/decoder.h"
#include "av1/decoder/decodeframe.h"
+#if CONFIG_SCALABILITY
+// picture prediction structures (0-12 are predefined) in scaiability metadata
+typedef enum {
+ SCALABILITY_L1T2 = 0,
+ SCALABILITY_L1T3 = 1,
+ SCALABILITY_L2T1 = 2,
+ SCALABILITY_L2T2 = 3,
+ SCALABILITY_L2T3 = 4,
+ SCALABILITY_S2T1 = 5,
+ SCALABILITY_S2T2 = 6,
+ SCALABILITY_S2T3 = 7,
+ SCALABILITY_L2T2h = 8,
+ SCALABILITY_L2T3h = 9,
+ SCALABILITY_S2T1h = 10,
+ SCALABILITY_S2T2h = 11,
+ SCALABILITY_S2T3h = 12,
+ SCALABILITY_SS = 13
+} SCALABILITY_STRUCTURES;
+#endif
+
static OBU_TYPE read_obu_header(struct aom_read_bit_buffer *rb,
- size_t *header_size) {
+ size_t *header_size,
+ uint8_t *obu_extension_header) {
*header_size = 1;
// first bit is obu_forbidden_bit (0) according to R19
@@ -31,12 +52,17 @@
const OBU_TYPE obu_type = (OBU_TYPE)aom_rb_read_literal(rb, 4);
aom_rb_read_literal(rb, 2); // reserved
const int obu_extension_flag = aom_rb_read_bit(rb);
+ if (obu_extension_header) *obu_extension_header = 0;
if (obu_extension_flag) {
*header_size += 1;
+#if !CONFIG_SCALABILITY
aom_rb_read_literal(rb, 3); // temporal_id
aom_rb_read_literal(rb, 2);
aom_rb_read_literal(rb, 2);
aom_rb_read_literal(rb, 1); // reserved
+#else
+ *obu_extension_header = (uint8_t)aom_rb_read_literal(rb, 8);
+#endif
}
return obu_type;
@@ -48,10 +74,20 @@
struct aom_read_bit_buffer *rb) {
AV1_COMMON *const cm = &pbi->common;
uint32_t saved_bit_offset = rb->bit_offset;
+#if CONFIG_SCALABILITY
+ int i;
+#endif
cm->profile = av1_read_profile(rb);
aom_rb_read_literal(rb, 4); // level
+#if CONFIG_SCALABILITY
+ pbi->common.enhancement_layers_cnt = aom_rb_read_literal(rb, 2);
+ for (i = 1; i <= pbi->common.enhancement_layers_cnt; i++) {
+ aom_rb_read_literal(rb, 4); // level for each enhancement layer
+ }
+#endif
+
read_sequence_header(&cm->seq_params, rb);
av1_read_bitdepth_colorspace_sampling(cm, rb, pbi->allow_lowbitdepth);
@@ -145,6 +181,51 @@
mem_get_le16(data);
}
+#if CONFIG_SCALABILITY
+static void scalability_structure(struct aom_read_bit_buffer *rb) {
+ int enhancement_layers_cnt = aom_rb_read_literal(rb, 2);
+ int enhancement_layer_dimensions_present_flag = aom_rb_read_literal(rb, 1);
+ int enhancement_layer_description_present_flag = aom_rb_read_literal(rb, 1);
+ int temporal_group_description_flag = aom_rb_read_literal(rb, 1);
+ aom_rb_read_literal(rb, 3); // reserved
+
+ if (enhancement_layer_dimensions_present_flag) {
+ int i;
+ for (i = 0; i < enhancement_layers_cnt + 1; i++) {
+ aom_rb_read_literal(rb, 16);
+ aom_rb_read_literal(rb, 16);
+ }
+ }
+ if (enhancement_layer_description_present_flag) {
+ int i;
+ for (i = 0; i < enhancement_layers_cnt + 1; i++) {
+ aom_rb_read_literal(rb, 8);
+ }
+ }
+ if (temporal_group_description_flag) {
+ int i, j, temporal_group_size;
+ temporal_group_size = aom_rb_read_literal(rb, 8);
+ for (i = 0; i < temporal_group_size; i++) {
+ aom_rb_read_literal(rb, 3);
+ aom_rb_read_literal(rb, 1);
+ int temporal_group_ref_cnt = aom_rb_read_literal(rb, 2);
+ aom_rb_read_literal(rb, 2);
+ for (j = 0; j < temporal_group_ref_cnt; j++) {
+ aom_rb_read_literal(rb, 8);
+ }
+ }
+ }
+}
+
+static void read_metadata_scalability(const uint8_t *data, size_t sz) {
+ struct aom_read_bit_buffer rb = { data, data + sz, 0, NULL, NULL };
+ int scalability_mode_idc = aom_rb_read_literal(&rb, 8);
+ if (scalability_mode_idc == SCALABILITY_SS) {
+ scalability_structure(&rb);
+ }
+}
+#endif
+
static size_t read_metadata(const uint8_t *data, size_t sz) {
assert(sz >= 2);
const OBU_METADATA_TYPE metadata_type = (OBU_METADATA_TYPE)mem_get_le16(data);
@@ -155,11 +236,22 @@
read_metadata_hdr_cll(data + 2);
} else if (metadata_type == OBU_METADATA_TYPE_HDR_MDCV) {
read_metadata_hdr_mdcv(data + 2);
+#if CONFIG_SCALABILITY
+ } else if (metadata_type == OBU_METADATA_TYPE_SCALABILITY) {
+ read_metadata_scalability(data + 2, sz - 2);
+#endif
}
return sz;
}
+#if CONFIG_SCALABILITY
+static const uint32_t kObuExtTemporalIdBitsMask = 0x7;
+static const uint32_t kObuExtTemporalIdBitsShift = 5;
+static const uint32_t kObuExtEnhancementIdBitsMask = 0x3;
+static const uint32_t kObuExtEnhancementIdBitsShift = 3;
+#endif
+
void av1_decode_frame_from_obus(struct AV1Decoder *pbi, const uint8_t *data,
const uint8_t *data_end,
const uint8_t **p_data_end) {
@@ -168,6 +260,9 @@
int is_first_tg_obu_received = 1;
int frame_header_received = 0;
int frame_header_size = 0;
+#if CONFIG_SCALABILITY
+ uint8_t obu_extension_header = 0;
+#endif
if (data_end < data) {
cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
@@ -193,12 +288,28 @@
const size_t obu_size = mem_get_le32(data);
#endif // CONFIG_OBU_SIZING
- const OBU_TYPE obu_type = read_obu_header(&rb, &obu_header_size);
+#if !CONFIG_SCALABILITY
+ const OBU_TYPE obu_type = read_obu_header(&rb, &obu_header_size, NULL);
+#else
+ const OBU_TYPE obu_type =
+ read_obu_header(&rb, &obu_header_size, &obu_extension_header);
+#endif
+
data += (PRE_OBU_SIZE_BYTES + obu_header_size);
if (data_end < data) {
cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
return;
}
+
+#if CONFIG_SCALABILITY
+ cm->temporal_layer_id =
+ (obu_extension_header >> kObuExtTemporalIdBitsShift) &
+ kObuExtTemporalIdBitsMask;
+ cm->enhancement_layer_id =
+ (obu_extension_header >> kObuExtEnhancementIdBitsShift) &
+ kObuExtEnhancementIdBitsMask;
+#endif
+
switch (obu_type) {
case OBU_TEMPORAL_DELIMITER:
obu_payload_size = read_temporal_delimiter_obu();
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 6275f14..b5a4673 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -4513,7 +4513,13 @@
}
#endif // CONFIG_OBU_SIZING
-static uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst) {
+static uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst
+#if CONFIG_SCALABILITY
+ ,
+ uint8_t enhancement_layers_cnt) {
+#else
+ ) {
+#endif
AV1_COMMON *const cm = &cpi->common;
struct aom_write_bit_buffer wb = { dst, 0 };
uint32_t size = 0;
@@ -4521,6 +4527,13 @@
write_profile(cm->profile, &wb);
aom_wb_write_literal(&wb, 0, 4);
+#if CONFIG_SCALABILITY
+ aom_wb_write_literal(&wb, enhancement_layers_cnt, 2);
+ int i;
+ for (i = 1; i <= enhancement_layers_cnt; i++) {
+ aom_wb_write_literal(&wb, 0, 4);
+ }
+#endif
write_sequence_header(cpi, &wb);
@@ -4594,7 +4607,8 @@
#if CONFIG_EXT_TILE
struct aom_write_bit_buffer *saved_wb,
#endif
- int insert_frame_header_obu_flag) {
+ int insert_frame_header_obu_flag,
+ uint8_t obu_extension_header) {
AV1_COMMON *const cm = &cpi->common;
const int num_planes = av1_num_planes(cm);
aom_writer mode_bc;
@@ -4761,8 +4775,8 @@
data = dst + total_size;
// A new tile group begins at this tile. Write the obu header and
// tile group header
- curr_tg_data_size =
- write_obu_header(OBU_TILE_GROUP, 0, data + PRE_OBU_SIZE_BYTES);
+ curr_tg_data_size = write_obu_header(
+ OBU_TILE_GROUP, obu_extension_header, data + PRE_OBU_SIZE_BYTES);
if (n_log2_tiles)
curr_tg_data_size += write_tile_group_header(
data + curr_tg_data_size + PRE_OBU_SIZE_BYTES, tile_idx,
@@ -4845,13 +4859,20 @@
uint32_t data_size;
unsigned int max_tile_size;
unsigned int max_tile_col_size;
- AV1_COMMON *const cm = &cpi->common;
#if CONFIG_OBU
+ AV1_COMMON *const cm = &cpi->common;
uint32_t obu_size;
uint8_t *frame_header_location;
uint32_t frame_header_size;
+#if CONFIG_SCALABILITY
+ const uint8_t enhancement_layers_cnt = cm->enhancement_layers_cnt;
+ const uint8_t obu_extension_header =
+ cm->temporal_layer_id << 5 | cm->enhancement_layer_id << 3 | 0;
+#else
+ uint8_t obu_extension_header = 0;
+#endif // CONFIG_SCALABILITY
#endif // CONFIG_OBU
- (void)cm;
+
#if CONFIG_BITSTREAM_DEBUG
bitstream_queue_reset_write();
#endif
@@ -4863,8 +4884,13 @@
if (cm->frame_type == KEY_FRAME) {
obu_size =
write_obu_header(OBU_SEQUENCE_HEADER, 0, data + PRE_OBU_SIZE_BYTES);
- obu_size +=
- write_sequence_header_obu(cpi, data + PRE_OBU_SIZE_BYTES + obu_size);
+
+ obu_size += write_sequence_header_obu(
+#if CONFIG_SCALABILITY
+ cpi, data + PRE_OBU_SIZE_BYTES + obu_size, enhancement_layers_cnt);
+#else
+ cpi, data + PRE_OBU_SIZE_BYTES + obu_size);
+#endif // CONFIG_SCALABILITY
#if CONFIG_OBU_SIZING
if (write_uleb_obu_size(obu_size, data) != AOM_CODEC_OK)
@@ -4882,7 +4908,8 @@
// write frame header obu, preceded by 4-byte size
frame_header_location = data + PRE_OBU_SIZE_BYTES;
- obu_size = write_obu_header(OBU_FRAME_HEADER, 0, frame_header_location);
+ obu_size = write_obu_header(OBU_FRAME_HEADER, obu_extension_header,
+ frame_header_location);
frame_header_size =
write_frame_header_obu(cpi,
#if CONFIG_EXT_TILE
@@ -4905,17 +4932,17 @@
} else {
// Each tile group obu will be preceded by 4-byte size of the tile group
// obu
- data_size =
- write_tiles_in_tg_obus(cpi, data, &max_tile_size, &max_tile_col_size,
- frame_header_location - PRE_OBU_SIZE_BYTES,
- obu_size + PRE_OBU_SIZE_BYTES,
+ data_size = write_tiles_in_tg_obus(
+ cpi, data, &max_tile_size, &max_tile_col_size,
+ frame_header_location - PRE_OBU_SIZE_BYTES,
+ obu_size + PRE_OBU_SIZE_BYTES,
#if CONFIG_EXT_TILE
- &saved_wb,
+ &saved_wb,
#endif
- 1 /* cm->error_resilient_mode */);
+ 1 /* cm->error_resilient_mode */, obu_extension_header);
}
-#endif
+#endif // CONFIG_OBU
#if CONFIG_EXT_TILE && !CONFIG_OBU
uint32_t uncompressed_hdr_size;
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 11d261e..f89d11c 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -148,6 +148,7 @@
set(CONFIG_RECT_TX_EXT 1 CACHE NUMBER "AV1 experiment flag.")
set(CONFIG_RECT_TX_EXT_INTRA 1 CACHE NUMBER "AV1 experiment flag.")
set(CONFIG_REFERENCE_BUFFER 1 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_SCALABILITY 0 CACHE NUMBER "AV1 experiment flag.")
set(CONFIG_SEGMENT_GLOBALMV 1 CACHE NUMBER "AV1 experiment flag.")
set(CONFIG_SEGMENT_PRED_LAST 1 CACHE NUMBER "AV1 experiment flag.")
set(CONFIG_SHORT_FILTER 1 CACHE NUMBER "AV1 experiment flag.")
diff --git a/build/cmake/aom_experiment_deps.cmake b/build/cmake/aom_experiment_deps.cmake
index 3a35f02..dfa64f5 100644
--- a/build/cmake/aom_experiment_deps.cmake
+++ b/build/cmake/aom_experiment_deps.cmake
@@ -20,6 +20,15 @@
endif ()
endif ()
+ if (CONFIG_SCALABILITY)
+ if (NOT CONFIG_OBU)
+ change_config_and_warn(CONFIG_OBU 1 CONFIG_SCALABILITY)
+ endif ()
+ if (NOT CONFIG_OBU_NO_IVF)
+ change_config_and_warn(CONFIG_OBU_NO_IVF 1 CONFIG_SCALABILITY)
+ endif ()
+ endif ()
+
if (CONFIG_ANALYZER)
if (NOT CONFIG_INSPECTION)
change_config_and_warn(CONFIG_INSPECTION 1 CONFIG_ANALYZER)
diff --git a/docs.cmake b/docs.cmake
index 7fc7563..aa4cf16 100644
--- a/docs.cmake
+++ b/docs.cmake
@@ -89,6 +89,16 @@
"Simplified encoder loop."
"Two-pass encoder loop.")
+ if (CONFIG_SCALABILITY)
+ set(AOM_DOXYGEN_EXAMPLE_SOURCES
+ ${AOM_DOXYGEN_EXAMPLE_SOURCES}
+ "${AOM_ROOT}/examples/scalable_encoder.c")
+
+ set(AOM_DOXYGEN_EXAMPLE_DESCRIPTIONS
+ ${AOM_DOXYGEN_EXAMPLE_DESCRIPTIONS}
+ "Scalable encoder loop.")
+ endif ()
+
set(AOM_DOXYGEN_SECTIONS ${AOM_DOXYGEN_SECTIONS} "av1_encoder encoder")
set(AOM_DOXYGEN_SOURCES
diff --git a/examples/scalable_decoder.c b/examples/scalable_decoder.c
new file mode 100644
index 0000000..70d3428
--- /dev/null
+++ b/examples/scalable_decoder.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+// Scalable Decoder
+// ==============
+//
+// This is an example of a scalable decoder loop. It takes a 2-spatial-layer
+// input file
+// containing the compressed data (in OBU format), passes it through the
+// decoder, and writes the decompressed frames to disk. The base layer and
+// enhancement layers are stored as separate files, lyr0.yuv and lyr1.yuv,
+// respectively.
+//
+// Standard Includes
+// -----------------
+// For decoders, you only have to include `aom_decoder.h` and then any
+// header files for the specific codecs you use. In this case, we're using
+// av1.
+//
+// Initializing The Codec
+// ----------------------
+// The libaom decoder is initialized by the call to aom_codec_dec_init().
+// Determining the codec interface to use is handled by AvxVideoReader and the
+// functions prefixed with aom_video_reader_. Discussion of those functions is
+// beyond the scope of this example, but the main gist is to open the input file
+// and parse just enough of it to determine if it's a AVx file and which AVx
+// codec is contained within the file.
+// Note the NULL pointer passed to aom_codec_dec_init(). We do that in this
+// example because we want the algorithm to determine the stream configuration
+// (width/height) and allocate memory automatically.
+//
+// Decoding A Frame
+// ----------------
+// Once the frame has been read into memory, it is decoded using the
+// `aom_codec_decode` function. The call takes a pointer to the data
+// (`frame`) and the length of the data (`frame_size`). No application data
+// is associated with the frame in this example, so the `user_priv`
+// parameter is NULL. The `deadline` parameter is left at zero for this
+// example. This parameter is generally only used when doing adaptive post
+// processing.
+//
+// Codecs may produce a variable number of output frames for every call to
+// `aom_codec_decode`. These frames are retrieved by the
+// `aom_codec_get_frame` iterator function. The iterator variable `iter` is
+// initialized to NULL each time `aom_codec_decode` is called.
+// `aom_codec_get_frame` is called in a loop, returning a pointer to a
+// decoded image or NULL to indicate the end of list.
+//
+// Processing The Decoded Data
+// ---------------------------
+// In this example, we simply write the encoded data to disk. It is
+// important to honor the image's `stride` values.
+//
+// Cleanup
+// -------
+// The `aom_codec_destroy` call frees any memory allocated by the codec.
+//
+// Error Handling
+// --------------
+// This example does not special case any error return codes. If there was
+// an error, a descriptive message is printed and the program exits. With
+// few exceptions, aom_codec functions return an enumerated error status,
+// with the value `0` indicating success.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom/aom_decoder.h"
+
+#include "../tools_common.h"
+#include "../video_reader.h"
+#include "./aom_config.h"
+#if CONFIG_SCALABILITY
+#include "./obudec.h"
+#endif
+
+static const char *exec_name;
+
+#define MAX_LAYERS 5
+
+struct AvxDecInputContext {
+ struct AvxInputContext *aom_input_ctx;
+ struct WebmInputContext *webm_ctx;
+};
+
+void usage_exit(void) {
+ fprintf(stderr, "Usage: %s <infile>\n", exec_name);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv) {
+ int frame_cnt = 0;
+ FILE *outfile[MAX_LAYERS];
+ char filename[80];
+ aom_codec_ctx_t codec;
+ const AvxInterface *decoder = NULL;
+ FILE *inputfile = NULL;
+ uint8_t *buf = NULL;
+ size_t bytes_in_buffer = 0;
+ size_t buffer_size = 0;
+ int next_layer_id = 0;
+ struct AvxDecInputContext input = { NULL, NULL };
+ struct AvxInputContext aom_input_ctx;
+ input.aom_input_ctx = &aom_input_ctx;
+ aom_codec_stream_info_t si;
+ uint8_t tmpbuf[32];
+ unsigned int i;
+
+ exec_name = argv[0];
+
+ if (argc != 2) die("Invalid number of arguments.");
+
+ if (!(inputfile = fopen(argv[1], "rb")))
+ die("Failed to open %s for read.", argv[1]);
+ input.aom_input_ctx->file = inputfile;
+
+ decoder = get_aom_decoder_by_index(0);
+ printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface()));
+
+ if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
+ die_codec(&codec, "Failed to initialize decoder.");
+
+ if (!file_is_obu(input.aom_input_ctx))
+ die_codec(&codec, "Input is not a valid obu file");
+
+ // peak sequence header OBU to get enhancement layer count, if any
+ fread(tmpbuf, 1, 32, inputfile);
+ if (aom_codec_peek_stream_info(decoder->codec_interface(), tmpbuf, 32, &si)) {
+ die_codec(&codec, "Input is not a valid obu file");
+ }
+ fseek(inputfile, -32, SEEK_CUR);
+
+ // open output yuv files
+ for (i = 0; i <= si.enhancement_layers_cnt; i++) {
+ snprintf(filename, sizeof(filename), "out_lyr%d.yuv", i);
+ if (!(outfile[i] = fopen(filename, "wb")))
+ die("Failed to open output for writing.");
+ }
+
+ while (!obu_read_temporal_unit(inputfile, &buf, &bytes_in_buffer,
+ &buffer_size, next_layer_id)) {
+ aom_codec_iter_t iter = NULL;
+ aom_image_t *img = NULL;
+ if (aom_codec_decode(&codec, buf, (unsigned int)bytes_in_buffer, NULL))
+ die_codec(&codec, "Failed to decode frame.");
+
+ while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) {
+ if (img->enhancement_id == 0) {
+ printf("Writing base layer 0 %d\n", frame_cnt);
+ aom_img_write(img, outfile[0]);
+ next_layer_id++;
+ } else if (img->enhancement_id <= (int)si.enhancement_layers_cnt) {
+ printf("Writing enhancemnt layer %d %d\n", img->enhancement_id,
+ frame_cnt);
+ aom_img_write(img, outfile[img->enhancement_id]);
+ if (img->enhancement_id == (int)si.enhancement_layers_cnt)
+ next_layer_id = 0;
+ else
+ next_layer_id++;
+ } else {
+ die_codec(&codec, "Invalid bitstream. Layer id exceeds layer count");
+ }
+ if (img->enhancement_id == (int)si.enhancement_layers_cnt) ++frame_cnt;
+ }
+ }
+
+ printf("Processed %d frames.\n", frame_cnt);
+ if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
+
+ for (i = 0; i <= si.enhancement_layers_cnt; i++) fclose(outfile[i]);
+
+ fclose(inputfile);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/scalable_encoder.c b/examples/scalable_encoder.c
new file mode 100644
index 0000000..10b3587
--- /dev/null
+++ b/examples/scalable_encoder.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2018, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+// Scalable Encoder
+// ==============
+//
+// This is an example of a scalable encoder loop. It takes two input files in
+// YV12 format, passes it through the encoder, and writes the compressed
+// frames to disk in OBU format.
+//
+// Getting The Default Configuration
+// ---------------------------------
+// Encoders have the notion of "usage profiles." For example, an encoder
+// may want to publish default configurations for both a video
+// conferencing application and a best quality offline encoder. These
+// obviously have very different default settings. Consult the
+// documentation for your codec to see if it provides any default
+// configurations. All codecs provide a default configuration, number 0,
+// which is valid for material in the vacinity of QCIF/QVGA.
+//
+// Updating The Configuration
+// ---------------------------------
+// Almost all applications will want to update the default configuration
+// with settings specific to their usage. Here we set the width and height
+// of the video file to that specified on the command line. We also scale
+// the default bitrate based on the ratio between the default resolution
+// and the resolution specified on the command line.
+//
+// Encoding A Frame
+// ----------------
+// The frame is read as a continuous block (size = width * height * 3 / 2)
+// from the input file. If a frame was read (the input file has not hit
+// EOF) then the frame is passed to the encoder. Otherwise, a NULL
+// is passed, indicating the End-Of-Stream condition to the encoder. The
+// `frame_cnt` is reused as the presentation time stamp (PTS) and each
+// frame is shown for one frame-time in duration. The flags parameter is
+// unused in this example.
+
+// Forced Keyframes
+// ----------------
+// Keyframes can be forced by setting the AOM_EFLAG_FORCE_KF bit of the
+// flags passed to `aom_codec_control()`. In this example, we force a
+// keyframe every <keyframe-interval> frames. Note, the output stream can
+// contain additional keyframes beyond those that have been forced using the
+// AOM_EFLAG_FORCE_KF flag because of automatic keyframe placement by the
+// encoder.
+//
+// Processing The Encoded Data
+// ---------------------------
+// Each packet of type `AOM_CODEC_CX_FRAME_PKT` contains the encoded data
+// for this frame. We write a IVF frame header, followed by the raw data.
+//
+// Cleanup
+// -------
+// The `aom_codec_destroy` call frees any memory allocated by the codec.
+//
+// Error Handling
+// --------------
+// This example does not special case any error return codes. If there was
+// an error, a descriptive message is printed and the program exits. With
+// few exeptions, aom_codec functions return an enumerated error status,
+// with the value `0` indicating success.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom/aom_encoder.h"
+#include "aom/aomcx.h"
+#include "av1/common/enums.h"
+
+#include "../tools_common.h"
+#include "../video_writer.h"
+
+static const char *exec_name;
+
+void usage_exit(void) {
+ fprintf(stderr,
+ "Usage: %s <codec> <width> <height> <infile0> <infile1> "
+ "<outfile> <frames to encode>\n"
+ "See comments in scalable_encoder.c for more information.\n",
+ exec_name);
+ exit(EXIT_FAILURE);
+}
+
+static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img,
+ int frame_index, int flags, FILE *outfile) {
+ int got_pkts = 0;
+ aom_codec_iter_t iter = NULL;
+ const aom_codec_cx_pkt_t *pkt = NULL;
+ const aom_codec_err_t res =
+ aom_codec_encode(codec, img, frame_index, 1, flags);
+ if (res != AOM_CODEC_OK) die_codec(codec, "Failed to encode frame");
+
+ while ((pkt = aom_codec_get_cx_data(codec, &iter)) != NULL) {
+ got_pkts = 1;
+
+ if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
+ const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
+ if (fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile) !=
+ pkt->data.frame.sz) {
+ die_codec(codec, "Failed to write compressed frame");
+ }
+ printf(keyframe ? "K" : ".");
+ printf(" %6d\n", (int)pkt->data.frame.sz);
+ fflush(stdout);
+ }
+ }
+
+ return got_pkts;
+}
+
+int main(int argc, char **argv) {
+ FILE *infile0 = NULL;
+ FILE *infile1 = NULL;
+ aom_codec_ctx_t codec;
+ aom_codec_enc_cfg_t cfg;
+ int frame_count = 0;
+ aom_image_t raw0, raw1;
+ aom_codec_err_t res;
+ AvxVideoInfo info;
+ const AvxInterface *encoder = NULL;
+ const int fps = 30;
+ const int bitrate = 200;
+ int keyframe_interval = 0;
+ int max_frames = 0;
+ int frames_encoded = 0;
+ const char *codec_arg = NULL;
+ const char *width_arg = NULL;
+ const char *height_arg = NULL;
+ const char *infile0_arg = NULL;
+ const char *infile1_arg = NULL;
+ const char *outfile_arg = NULL;
+ // const char *keyframe_interval_arg = NULL;
+ FILE *outfile = NULL;
+
+ exec_name = argv[0];
+
+ // Clear explicitly, as simply assigning "{ 0 }" generates
+ // "missing-field-initializers" warning in some compilers.
+ memset(&info, 0, sizeof(info));
+
+ if (argc != 8) die("Invalid number of arguments");
+
+ codec_arg = argv[1];
+ width_arg = argv[2];
+ height_arg = argv[3];
+ infile0_arg = argv[4];
+ infile1_arg = argv[5];
+ outfile_arg = argv[6];
+ max_frames = (int)strtol(argv[7], NULL, 0);
+
+ encoder = get_aom_encoder_by_name(codec_arg);
+ if (!encoder) die("Unsupported codec.");
+
+ info.codec_fourcc = encoder->fourcc;
+ info.frame_width = (int)strtol(width_arg, NULL, 0);
+ info.frame_height = (int)strtol(height_arg, NULL, 0);
+ info.time_base.numerator = 1;
+ info.time_base.denominator = fps;
+
+ if (info.frame_width <= 0 || info.frame_height <= 0 ||
+ (info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
+ die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
+ }
+
+ if (!aom_img_alloc(&raw0, AOM_IMG_FMT_I420, info.frame_width,
+ info.frame_height, 1)) {
+ die("Failed to allocate image for layer 0.");
+ }
+ if (!aom_img_alloc(&raw1, AOM_IMG_FMT_I420, info.frame_width,
+ info.frame_height, 1)) {
+ die("Failed to allocate image for layer 1.");
+ }
+
+ // keyframe_interval = (int)strtol(keyframe_interval_arg, NULL, 0);
+ keyframe_interval = 100;
+ if (keyframe_interval < 0) die("Invalid keyframe interval value.");
+
+ printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
+
+ res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
+ if (res) die_codec(&codec, "Failed to get default codec config.");
+
+ cfg.g_w = info.frame_width;
+ cfg.g_h = info.frame_height;
+ cfg.g_timebase.num = info.time_base.numerator;
+ cfg.g_timebase.den = info.time_base.denominator;
+ cfg.rc_target_bitrate = bitrate;
+ cfg.g_error_resilient = 0;
+ cfg.g_lag_in_frames = 0;
+ cfg.rc_end_usage = AOM_Q;
+
+ outfile = fopen(outfile_arg, "wb");
+ if (!outfile) die("Failed to open %s for writing.", outfile_arg);
+
+ if (!(infile0 = fopen(infile0_arg, "rb")))
+ die("Failed to open %s for reading.", infile0_arg);
+ if (!(infile1 = fopen(infile1_arg, "rb")))
+ die("Failed to open %s for reading.", infile0_arg);
+
+ if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
+ die_codec(&codec, "Failed to initialize encoder");
+ if (aom_codec_control(&codec, AOME_SET_CPUUSED, 8))
+ die_codec(&codec, "Failed to set cpu to 8");
+
+ if (aom_codec_control(&codec, AOME_SET_NUMBER_SPATIAL_LAYERS, 2))
+ die_codec(&codec, "Failed to set number of spatial layers to 2");
+
+ // Encode frames.
+ while (aom_img_read(&raw0, infile0)) {
+ int flags = 0;
+
+ // configure and encode base layer
+
+ if (keyframe_interval > 0 && frames_encoded % keyframe_interval == 0)
+ flags |= AOM_EFLAG_FORCE_KF;
+ else
+ // use previous base layer (LAST) as sole reference
+ // save this frame as LAST to be used as reference by enhanmcent layer
+ // and next base layer
+ flags |= AOM_EFLAG_NO_REF_LAST2 | AOM_EFLAG_NO_REF_LAST3 |
+ AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
+ AOM_EFLAG_NO_REF_BWD | AOM_EFLAG_NO_REF_ARF2 |
+ AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
+ AOM_EFLAG_NO_UPD_ENTROPY;
+ cfg.g_w = info.frame_width;
+ cfg.g_h = info.frame_height;
+ if (aom_codec_enc_config_set(&codec, &cfg))
+ die_codec(&codec, "Failed to set enc cfg for layer 0");
+ if (aom_codec_control(&codec, AOME_SET_ENHANCEMENT_LAYER_ID, 0))
+ die_codec(&codec, "Failed to set layer id to 0");
+ if (aom_codec_control(&codec, AOME_SET_CQ_LEVEL, 62))
+ die_codec(&codec, "Failed to set cq level");
+ encode_frame(&codec, &raw0, frame_count++, flags, outfile);
+
+ // configure and encode enhancement layer
+
+ // use LAST (base layer) as sole reference
+ flags = AOM_EFLAG_NO_REF_LAST2 | AOM_EFLAG_NO_REF_LAST3 |
+ AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_REF_BWD |
+ AOM_EFLAG_NO_REF_ARF2 | AOM_EFLAG_NO_UPD_LAST |
+ AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
+ AOM_EFLAG_NO_UPD_ENTROPY;
+ cfg.g_w = info.frame_width;
+ cfg.g_h = info.frame_height;
+ aom_img_read(&raw1, infile1);
+ if (aom_codec_enc_config_set(&codec, &cfg))
+ die_codec(&codec, "Failed to set enc cfg for layer 1");
+ if (aom_codec_control(&codec, AOME_SET_ENHANCEMENT_LAYER_ID, 1))
+ die_codec(&codec, "Failed to set layer id to 1");
+ if (aom_codec_control(&codec, AOME_SET_CQ_LEVEL, 10))
+ die_codec(&codec, "Failed to set cq level");
+ encode_frame(&codec, &raw1, frame_count++, flags, outfile);
+
+ frames_encoded++;
+
+ if (max_frames > 0 && frames_encoded >= max_frames) break;
+ }
+
+ // Flush encoder.
+ while (encode_frame(&codec, NULL, -1, 0, outfile)) continue;
+
+ printf("\n");
+ fclose(infile0);
+ fclose(infile1);
+ printf("Processed %d frames.\n", frame_count / 2);
+
+ aom_img_free(&raw0);
+ aom_img_free(&raw1);
+ if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
+
+ fclose(outfile);
+
+ return EXIT_SUCCESS;
+}
diff --git a/obudec.c b/obudec.c
index 8f1ca00..f859a68 100644
--- a/obudec.c
+++ b/obudec.c
@@ -19,10 +19,17 @@
#include "av1/common/common.h"
#define OBU_HEADER_SIZE_BYTES 1
+#if CONFIG_SCALABILITY
+#define OBU_HEADER_EXTENSION_SIZE_BYTES 1
+#endif
#if CONFIG_OBU_NO_IVF
int obu_read_temporal_unit(FILE *infile, uint8_t **buffer, size_t *bytes_read,
+#if CONFIG_SCALABILITY
+ size_t *buffer_size, int last_layer_id) {
+#else
size_t *buffer_size) {
+#endif
size_t ret;
const size_t obu_length_header_size =
PRE_OBU_SIZE_BYTES + OBU_HEADER_SIZE_BYTES;
@@ -60,6 +67,26 @@
break;
}
+#if CONFIG_SCALABILITY
+ // break if obu_extension_flag is found and enhancement_id change
+ if ((data[PRE_OBU_SIZE_BYTES] & 0x1)) {
+ uint8_t obu_extension_header;
+ int total_obu_header_size =
+ (int)obu_length_header_size + OBU_HEADER_EXTENSION_SIZE_BYTES;
+ int curr_layer_id;
+ fread(&obu_extension_header, 1, OBU_HEADER_EXTENSION_SIZE_BYTES, infile);
+ curr_layer_id = (obu_extension_header >> 3) & 0x3;
+ if (curr_layer_id && (curr_layer_id > last_layer_id)) {
+ // new enhancement layer
+ *bytes_read -= obu_length_header_size;
+ fseek(infile, -total_obu_header_size, SEEK_CUR);
+ break;
+ } else {
+ fseek(infile, -OBU_HEADER_EXTENSION_SIZE_BYTES, SEEK_CUR);
+ }
+ }
+#endif
+
// otherwise, read the OBU payload into memory
#if CONFIG_OBU_SIZING
aom_uleb_decode(data, PRE_OBU_SIZE_BYTES, &obu_size);
@@ -112,6 +139,7 @@
return 0;
}
// fprintf(stderr, "Starting to parse OBU stream\n");
+
return 1;
}
diff --git a/obudec.h b/obudec.h
index 899a35d..a7c4d1a 100644
--- a/obudec.h
+++ b/obudec.h
@@ -20,7 +20,11 @@
int file_is_obu(struct AvxInputContext *input_ctx);
int obu_read_temporal_unit(FILE *infile, uint8_t **buffer, size_t *bytes_read,
+#if CONFIG_SCALABILITY
+ size_t *buffer_size, int last_layer_id);
+#else
size_t *buffer_size);
+#endif
#ifdef __cplusplus
} /* extern "C" */