Implement aom_codec_get_global_headers for AV1.
Returns a Sequence Header OBU via the aom_fixed_buf_t
returned to the caller.
BUG=aomedia:2012
Change-Id: Id299d4a694140272fcb74cf9b0bb3ac6a0de73ec
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index 6003088..ee83aa3 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -849,13 +849,19 @@
/*!\brief Get global stream headers
*
* Retrieves a stream level global header packet, if supported by the codec.
+ * Calls to this function should be deferred until all configuration information
+ * has been passed to libaom. Otherwise the global header data may be
+ * invalidated by additional configuration changes.
*
* \param[in] ctx Pointer to this instance's context
*
* \retval NULL
- * Encoder does not support global header
+ * Encoder does not support global header, or an error occurred while
+ * generating the global header.
* \retval Non-NULL
- * Pointer to buffer containing global header packet
+ * Pointer to buffer containing global header packet. The caller owns the
+ * memory associated with this buffer, and must free the 'buf' member as
+ * well as the aom_fixed_buf_t pointer.
*/
aom_fixed_buf_t *aom_codec_get_global_headers(aom_codec_ctx_t *ctx);
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 65f35bd..3bc7411 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -757,6 +757,10 @@
return res;
}
+static aom_fixed_buf_t *encoder_get_global_headers(aom_codec_alg_priv_t *ctx) {
+ return av1_get_global_headers(ctx->cpi);
+}
+
static aom_codec_err_t ctrl_get_quantizer(aom_codec_alg_priv_t *ctx,
va_list args) {
int *const arg = va_arg(args, int *);
@@ -1892,13 +1896,13 @@
},
{
// NOLINT
- 1, // 1 cfg map
- encoder_usage_cfg_map, // aom_codec_enc_cfg_map_t
- encoder_encode, // aom_codec_encode_fn_t
- encoder_get_cxdata, // aom_codec_get_cx_data_fn_t
- encoder_set_config, // aom_codec_enc_config_set_fn_t
- NULL, // aom_codec_get_global_headers_fn_t
- encoder_get_preview, // aom_codec_get_preview_frame_fn_t
- NULL // aom_codec_enc_mr_get_mem_loc_fn_t
+ 1, // 1 cfg map
+ encoder_usage_cfg_map, // aom_codec_enc_cfg_map_t
+ encoder_encode, // aom_codec_encode_fn_t
+ encoder_get_cxdata, // aom_codec_get_cx_data_fn_t
+ encoder_set_config, // aom_codec_enc_config_set_fn_t
+ encoder_get_global_headers, // aom_codec_get_global_headers_fn_t
+ encoder_get_preview, // aom_codec_get_preview_frame_fn_t
+ NULL // aom_codec_enc_mr_get_mem_loc_fn_t
}
};
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 1c9ce3f..c6a6e83 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2648,7 +2648,8 @@
aom_wb_write_bit(wb, seq_params->sb_size == BLOCK_128X128 ? 1 : 0);
}
-void write_sequence_header(AV1_COMP *cpi, struct aom_write_bit_buffer *wb) {
+static void write_sequence_header(AV1_COMP *cpi,
+ struct aom_write_bit_buffer *wb) {
AV1_COMMON *const cm = &cpi->common;
SequenceHeader *seq_params = &cm->seq_params;
@@ -3473,7 +3474,7 @@
aom_wb_write_literal(wb, seq_level_idx, LEVEL_BITS);
}
-static uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst) {
+uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst) {
AV1_COMMON *const cm = &cpi->common;
struct aom_write_bit_buffer wb = { dst, 0 };
uint32_t size = 0;
diff --git a/av1/encoder/bitstream.h b/av1/encoder/bitstream.h
index 8c12ac5..39096e2 100644
--- a/av1/encoder/bitstream.h
+++ b/av1/encoder/bitstream.h
@@ -20,8 +20,13 @@
struct aom_write_bit_buffer;
-void write_sequence_header(AV1_COMP *cpi, struct aom_write_bit_buffer *wb);
+// Writes only the OBU Sequence Header payload, and returns the size of the
+// payload written to 'dst'. This function does not write the OBU header, the
+// optional extension, or the OBU size to 'dst'.
+uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst);
+// Writes the OBU header byte, and the OBU header extension byte when
+// 'obu_extension' is non-zero. Returns number of bytes written to 'dst'.
uint32_t write_obu_header(OBU_TYPE obu_type, int obu_extension,
uint8_t *const dst);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 9b2fde2..39dda38 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -1067,9 +1067,30 @@
10; // Default value (not signaled)
}
- if (cm->seq_params.bit_depth == AOM_BITS_12) {
- cm->seq_params.subsampling_x = oxcf->chroma_subsampling_x;
- cm->seq_params.subsampling_y = oxcf->chroma_subsampling_y;
+ if (cm->seq_params.monochrome) {
+ cm->seq_params.subsampling_x = 1;
+ cm->seq_params.subsampling_y = 1;
+ } else if (cm->seq_params.color_primaries == AOM_CICP_CP_BT_709 &&
+ cm->seq_params.transfer_characteristics == AOM_CICP_TC_SRGB &&
+ cm->seq_params.matrix_coefficients == AOM_CICP_MC_IDENTITY) {
+ cm->seq_params.subsampling_x = 0;
+ cm->seq_params.subsampling_y = 0;
+ } else {
+ if (cm->seq_params.profile == 0) {
+ cm->seq_params.subsampling_x = 1;
+ cm->seq_params.subsampling_y = 1;
+ } else if (cm->seq_params.profile == 1) {
+ cm->seq_params.subsampling_x = 0;
+ cm->seq_params.subsampling_y = 0;
+ } else {
+ if (cm->seq_params.bit_depth == AOM_BITS_12) {
+ cm->seq_params.subsampling_x = oxcf->chroma_subsampling_x;
+ cm->seq_params.subsampling_y = oxcf->chroma_subsampling_y;
+ } else {
+ cm->seq_params.subsampling_x = 1;
+ cm->seq_params.subsampling_y = 0;
+ }
+ }
}
cm->width = oxcf->width;
@@ -6377,3 +6398,50 @@
const int64_t round = TICKS_PER_SEC * timebase->num / 2 - 1;
return (n * timebase->den + round) / timebase->num / TICKS_PER_SEC;
}
+
+aom_fixed_buf_t *av1_get_global_headers(AV1_COMP *cpi) {
+ if (!cpi) return NULL;
+
+ uint8_t header_buf[512] = { 0 };
+ const uint32_t sequence_header_size =
+ write_sequence_header_obu(cpi, &header_buf[0]);
+ assert(sequence_header_size <= sizeof(header_buf));
+ if (sequence_header_size == 0) return NULL;
+
+ const size_t obu_header_size = 1;
+ const size_t size_field_size = aom_uleb_size_in_bytes(sequence_header_size);
+ const size_t payload_offset = obu_header_size + size_field_size;
+
+ if (payload_offset + sequence_header_size > sizeof(header_buf)) return NULL;
+ memmove(&header_buf[payload_offset], &header_buf[0], sequence_header_size);
+
+ if (write_obu_header(OBU_SEQUENCE_HEADER, 0, &header_buf[0]) !=
+ obu_header_size) {
+ return NULL;
+ }
+
+ size_t coded_size_field_size = 0;
+ if (aom_uleb_encode(sequence_header_size, size_field_size,
+ &header_buf[obu_header_size],
+ &coded_size_field_size) != 0) {
+ return NULL;
+ }
+ assert(coded_size_field_size == size_field_size);
+
+ aom_fixed_buf_t *global_headers =
+ (aom_fixed_buf_t *)malloc(sizeof(*global_headers));
+ if (!global_headers) return NULL;
+
+ const size_t global_header_buf_size =
+ obu_header_size + size_field_size + sequence_header_size;
+
+ global_headers->buf = malloc(global_header_buf_size);
+ if (!global_headers->buf) {
+ free(global_headers);
+ return NULL;
+ }
+
+ memcpy(global_headers->buf, &header_buf[0], global_header_buf_size);
+ global_headers->sz = global_header_buf_size;
+ return global_headers;
+}
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index a47a4d7..e147722 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -942,6 +942,12 @@
(!cm->error_resilient_mode || cm->frame_type == KEY_FRAME);
}
+// Returns a Sequence Header OBU stored in an aom_fixed_buf_t, or NULL upon
+// failure. When a non-NULL aom_fixed_buf_t pointer is returned by this
+// function, the memory must be freed by the caller. Both the buf member of the
+// aom_fixed_buf_t, and the aom_fixed_buf_t pointer itself must be freed.
+aom_fixed_buf_t *av1_get_global_headers(AV1_COMP *cpi);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 6d09306..c26f572 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -54,6 +54,16 @@
EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_config_default(kCodecs[i], &cfg, 0));
EXPECT_EQ(AOM_CODEC_OK, aom_codec_enc_init(&enc, kCodecs[i], &cfg, 0));
+
+ EXPECT_EQ(NULL, aom_codec_get_global_headers(NULL));
+
+ aom_fixed_buf_t *glob_headers = aom_codec_get_global_headers(&enc);
+ EXPECT_TRUE(glob_headers->buf != NULL);
+ if (glob_headers) {
+ free(glob_headers->buf);
+ free(glob_headers);
+ }
+
EXPECT_EQ(AOM_CODEC_OK, aom_codec_encode(&enc, NULL, 0, 0, 0));
EXPECT_EQ(AOM_CODEC_OK, aom_codec_destroy(&enc));