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));