[NORMATIVE-DECODING] option of annex-b/non-annex-b bitstreams
libaom modified to encode/decode AV1 as annex-b or non-annex-b
Encoder option --annexb=0 or 1 (default 0)
Decoder option --annexb
BUG=aomedia:1590
BUG=aomedia:1591
Change-Id: I0f092b45fbf30e689c7fe3c6540d22f497946880
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h
index 3c67fb3..3973c50 100644
--- a/aom/aom_decoder.h
+++ b/aom/aom_decoder.h
@@ -85,6 +85,7 @@
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 */
+ unsigned int is_annexb; /**< Is Bitstream in Annex-B format */
} aom_codec_stream_info_t;
/* REQUIRED FUNCTIONS
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index e93d081..d54d89f 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -663,6 +663,14 @@
*/
unsigned int monochrome;
+ /*!\brief Bitstream syntax mode
+ *
+ * This value indicates the bitstream syntax mode.
+ * A value of 0 indicates bitstream is saved as Section 5 bitstream. A value
+ * of 1 indicates the bitstream is saved in Annex-B format
+ */
+ unsigned int save_as_annexb;
+
/*!\brief Number of explicit tile widths specified
*
* This value indicates the number of tile widths specified
diff --git a/aom/aomdx.h b/aom/aomdx.h
index 2beba60..b3a5fb1 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -146,6 +146,9 @@
*/
AV1_SET_TILE_MODE,
+ /** control function to indicate whether bitstream is in Annex-B format. */
+ AV1D_SET_IS_ANNEXB,
+
/** control function to set an aom_inspect_cb callback that is invoked each
* time a frame is decoded. When compiled without --enable-inspection, this
* returns AOM_CODEC_INCAPABLE.
@@ -187,6 +190,8 @@
#define AOM_CTRL_AV1_SET_DECODE_TILE_COL
AOM_CTRL_USE_TYPE(AV1_SET_TILE_MODE, unsigned int)
#define AOM_CTRL_AV1_SET_TILE_MODE
+AOM_CTRL_USE_TYPE(AV1D_SET_IS_ANNEXB, unsigned int)
+#define AOM_CTRL_AV1D_SET_IS_ANNEXB
AOM_CTRL_USE_TYPE(AV1_SET_INSPECTION_CALLBACK, aom_inspect_init *)
#define AOM_CTRL_AV1_SET_INSPECTION_CALLBACK
/*!\endcond */
diff --git a/aomdec.c b/aomdec.c
index b765335..9fd8f0a 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -111,13 +111,17 @@
static const arg_def_t tilec = ARG_DEF(NULL, "tile-column", 1,
"Column index of tile to decode "
"(-1 for all columns)");
+static const arg_def_t isannexb =
+ ARG_DEF(NULL, "annexb", 0, "Bitstream is in Annex-B format");
static const arg_def_t *all_args[] = {
- &help, &codecarg, &use_yv12, &use_i420, &flipuvarg,
- &rawvideo, &noblitarg, &progressarg, &limitarg, &skiparg,
- &postprocarg, &summaryarg, &outputfile, &threadsarg, &verbosearg,
- &scalearg, &fb_arg, &md5arg, &framestatsarg, &continuearg,
- &outbitdeptharg, &tilem, &tiler, &tilec, NULL
+ &help, &codecarg, &use_yv12, &use_i420,
+ &flipuvarg, &rawvideo, &noblitarg, &progressarg,
+ &limitarg, &skiparg, &postprocarg, &summaryarg,
+ &outputfile, &threadsarg, &verbosearg, &scalearg,
+ &fb_arg, &md5arg, &framestatsarg, &continuearg,
+ &outbitdeptharg, &tilem, &tiler, &tilec,
+ &isannexb, NULL
};
#if CONFIG_LIBYUV
@@ -500,6 +504,7 @@
aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH, { 1 } };
unsigned int output_bit_depth = 0;
unsigned int tile_mode = 0;
+ unsigned int is_annexb = 0;
int tile_row = -1;
int tile_col = -1;
int frames_corrupted = 0;
@@ -527,7 +532,7 @@
memset(&webm_ctx, 0, sizeof(webm_ctx));
input.webm_ctx = &webm_ctx;
#endif
- struct ObuDecInputContext obu_ctx = { NULL, NULL, 0, 0 };
+ struct ObuDecInputContext obu_ctx = { NULL, NULL, 0, 0, 0 };
obu_ctx.avx_ctx = &aom_input_ctx;
input.obu_ctx = &obu_ctx;
input.aom_input_ctx = &aom_input_ctx;
@@ -604,6 +609,9 @@
output_bit_depth = arg_parse_uint(&arg);
} else if (arg_match(&arg, &tilem, argi)) {
tile_mode = arg_parse_int(&arg);
+ } else if (arg_match(&arg, &isannexb, argi)) {
+ is_annexb = 1;
+ input.obu_ctx->is_annexb = 1;
} else if (arg_match(&arg, &tiler, argi)) {
tile_row = arg_parse_int(&arg);
} else if (arg_match(&arg, &tilec, argi)) {
@@ -718,6 +726,11 @@
goto fail;
}
+ if (aom_codec_control(&decoder, AV1D_SET_IS_ANNEXB, is_annexb)) {
+ fprintf(stderr, "Failed to set is_annexb: %s\n", aom_codec_error(&decoder));
+ goto fail;
+ }
+
if (aom_codec_control(&decoder, AV1_SET_DECODE_TILE_ROW, tile_row)) {
fprintf(stderr, "Failed to set decode_tile_row: %s\n",
aom_codec_error(&decoder));
diff --git a/aomenc.c b/aomenc.c
index b601080..6afa006 100644
--- a/aomenc.c
+++ b/aomenc.c
@@ -380,6 +380,8 @@
ARG_DEF(NULL, "sframe-dist", 1, "S-Frame interval (frames)");
static const arg_def_t sframe_mode =
ARG_DEF(NULL, "sframe-mode", 1, "S-Frame insertion mode (1..2)");
+static const arg_def_t save_as_annexb =
+ ARG_DEF(NULL, "annexb", 1, "Save as Annex-B");
static const arg_def_t noise_sens =
ARG_DEF(NULL, "noise-sensitivity", 1, "Noise sensitivity (frames to blur)");
static const arg_def_t sharpness =
@@ -653,6 +655,7 @@
&inbitdeptharg,
&sframe_dist,
&sframe_mode,
+ &save_as_annexb,
NULL };
static const int av1_arg_ctrl_map[] = { AOME_SET_CPUUSED,
AOME_SET_DEVSF,
@@ -1225,6 +1228,8 @@
config->cfg.sframe_dist = arg_parse_uint(&arg);
} else if (arg_match(&arg, &sframe_mode, argi)) {
config->cfg.sframe_mode = arg_parse_uint(&arg);
+ } else if (arg_match(&arg, &save_as_annexb, argi)) {
+ config->cfg.save_as_annexb = arg_parse_uint(&arg);
} else if (arg_match(&arg, &tile_width, argi)) {
config->cfg.tile_width_count =
arg_parse_list(&arg, config->cfg.tile_widths, MAX_TILE_WIDTHS);
@@ -1538,6 +1543,10 @@
stream->config.cfg.large_scale_tile);
ctx_exit_on_error(&stream->decoder, "Failed to set decode_tile_mode");
+ aom_codec_control(&stream->decoder, AV1D_SET_IS_ANNEXB,
+ stream->config.cfg.save_as_annexb);
+ ctx_exit_on_error(&stream->decoder, "Failed to set is_annexb");
+
aom_codec_control(&stream->decoder, AV1_SET_DECODE_TILE_ROW, -1);
ctx_exit_on_error(&stream->decoder, "Failed to set decode_tile_row");
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index fd94390..f93e21c 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -655,6 +655,8 @@
oxcf->aq_mode = extra_cfg->aq_mode;
oxcf->deltaq_mode = extra_cfg->deltaq_mode;
+ oxcf->save_as_annexb = cfg->save_as_annexb;
+
oxcf->frame_periodic_boost = extra_cfg->frame_periodic_boost;
oxcf->motion_vector_unit_test = extra_cfg->motion_vector_unit_test;
return AOM_CODEC_OK;
@@ -1313,6 +1315,15 @@
obu_header_size + obu_payload_size + length_field_size;
}
+ if (ctx->oxcf.save_as_annexb) {
+ size_t curr_frame_size = pkt.data.frame.sz;
+ if (av1_convert_sect5obus_to_annexb(ctx->pending_cx_data,
+ &curr_frame_size) != AOM_CODEC_OK) {
+ return AOM_CODEC_ERROR;
+ }
+ pkt.data.frame.sz = curr_frame_size;
+ }
+
pkt.data.frame.pts = ticks_to_timebase_units(timebase, dst_time_stamp);
pkt.data.frame.flags = get_frame_pkt_flags(cpi, lib_flags);
pkt.data.frame.duration = (uint32_t)ticks_to_timebase_units(
@@ -1703,6 +1714,7 @@
1, // sframe_mode
0, // large_scale_tile
0, // monochrome
+ 0, // save_as_annexb
0, // tile_width_count
0, // tile_height_count
{ 0 }, // tile_widths
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index bcf7078..f3ae661 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -59,6 +59,7 @@
int decode_tile_row;
int decode_tile_col;
unsigned int tile_mode;
+ unsigned int is_annexb;
AVxWorker *frame_workers;
int num_frame_workers;
@@ -181,15 +182,25 @@
intra_only_flag = 1;
si->is_kf = 1;
- struct aom_read_bit_buffer rb = { data, data + data_sz, 0, NULL, NULL };
+ size_t length_field_size = 0;
+ if (si->is_annexb) {
+ length_field_size = get_obu_length_field_size(data, data_sz - 1);
+ }
+ struct aom_read_bit_buffer rb = { data + length_field_size, data + data_sz, 0,
+ NULL, NULL };
+
const uint8_t obu_header = (uint8_t)aom_rb_read_literal(&rb, 8);
OBU_TYPE obu_type;
if (get_obu_type(obu_header, &obu_type) != 0)
return AOM_CODEC_UNSUP_BITSTREAM;
- // One byte has been consumed by the OBU header.
- rb.bit_buffer += get_obu_length_field_size(data + 1, data_sz - 1) * 8;
+ if (!si->is_annexb) {
+ // One byte has been consumed by the OBU header.
+ // TODO(shan): Assuming 1-byte obu_header (need to account in case extension
+ // exists)
+ rb.bit_buffer += get_obu_length_field_size(data + 1, data_sz - 1) + 2;
+ }
// This check is disabled because existing behavior is depended upon by
// decoder tests (see decode_test_driver.cc), scalability_decoder (see
@@ -365,6 +376,7 @@
frame_worker_data->pbi->max_threads = ctx->cfg.threads;
frame_worker_data->pbi->inv_tile_order = ctx->invert_tile_order;
frame_worker_data->pbi->common.large_scale_tile = ctx->tile_mode;
+ frame_worker_data->pbi->common.is_annexb = ctx->is_annexb;
frame_worker_data->pbi->dec_tile_row = ctx->decode_tile_row;
frame_worker_data->pbi->dec_tile_col = ctx->decode_tile_col;
worker->hook = (AVxWorkerHook)frame_worker_hook;
@@ -402,6 +414,7 @@
// of the heap.
if (!ctx->si.h) {
int is_intra_only = 0;
+ ctx->si.is_annexb = ctx->is_annexb;
const aom_codec_err_t res =
decoder_peek_si_internal(*data, data_sz, &ctx->si, &is_intra_only);
if (res != AOM_CODEC_OK) return res;
@@ -425,6 +438,8 @@
frame_worker_data->pbi->dec_tile_row = ctx->decode_tile_row;
frame_worker_data->pbi->dec_tile_col = ctx->decode_tile_col;
+ frame_worker_data->pbi->common.is_annexb = ctx->is_annexb;
+
worker->had_error = 0;
winterface->execute(worker);
@@ -461,6 +476,7 @@
res = init_decoder(ctx);
if (res != AOM_CODEC_OK) return res;
}
+
// Decode in serial mode.
if (frame_count > 0) {
int i;
@@ -904,6 +920,12 @@
return AOM_CODEC_OK;
}
+static aom_codec_err_t ctrl_set_is_annexb(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+ ctx->is_annexb = va_arg(args, unsigned int);
+ return AOM_CODEC_OK;
+}
+
static aom_codec_err_t ctrl_set_inspection_callback(aom_codec_alg_priv_t *ctx,
va_list args) {
#if !CONFIG_INSPECTION
@@ -934,6 +956,7 @@
{ AV1_SET_DECODE_TILE_ROW, ctrl_set_decode_tile_row },
{ AV1_SET_DECODE_TILE_COL, ctrl_set_decode_tile_col },
{ AV1_SET_TILE_MODE, ctrl_set_tile_mode },
+ { AV1D_SET_IS_ANNEXB, ctrl_set_is_annexb },
{ AV1_SET_INSPECTION_CALLBACK, ctrl_set_inspection_callback },
// Getters
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index 2623445..b491724 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -516,6 +516,9 @@
int tpl_mvs_mem_size;
// TODO(jingning): This can be combined with sign_bias later.
int8_t ref_frame_side[REF_FRAMES];
+
+ int is_annexb;
+
int frame_refs_short_signaling;
int temporal_layer_id;
int enhancement_layer_id;
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index 4a3828b..40434bf 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -76,7 +76,7 @@
// Parses OBU header and stores values in 'header'.
static aom_codec_err_t read_obu_header(struct aom_read_bit_buffer *rb,
- ObuHeader *header) {
+ int is_annexb, ObuHeader *header) {
if (!rb || !header) return AOM_CODEC_INVALID_PARAM;
header->size = 1;
@@ -90,8 +90,9 @@
header->has_extension = aom_rb_read_bit(rb);
header->has_length_field = aom_rb_read_bit(rb);
- if (!header->has_length_field) {
- // libaom does not support streams with this bit set to 0.
+
+ if (!header->has_length_field && !is_annexb) {
+ // section 5 obu streams must have length field set.
return AOM_CODEC_UNSUP_BITSTREAM;
}
@@ -109,14 +110,15 @@
}
aom_codec_err_t aom_read_obu_header(uint8_t *buffer, size_t buffer_length,
- size_t *consumed, ObuHeader *header) {
+ size_t *consumed, ObuHeader *header,
+ int is_annexb) {
if (buffer_length < 1 || !consumed || !header) return AOM_CODEC_INVALID_PARAM;
// TODO(tomfinegan): Set the error handler here and throughout this file, and
// confirm parsing work done via aom_read_bit_buffer is successful.
struct aom_read_bit_buffer rb = { buffer, buffer + buffer_length, 0, NULL,
NULL };
- aom_codec_err_t parse_result = read_obu_header(&rb, header);
+ aom_codec_err_t parse_result = read_obu_header(&rb, is_annexb, header);
if (parse_result == AOM_CODEC_OK) *consumed = header->size;
return parse_result;
}
@@ -382,25 +384,39 @@
}
size_t length_field_size = 0;
+ size_t obu_size = 0;
+ if (cm->is_annexb) {
+ if (read_obu_size(data, bytes_available, &obu_size, &length_field_size) !=
+ AOM_CODEC_OK) {
+ cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
+ return;
+ }
+ }
if (data_end < data + length_field_size) {
cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
return;
}
av1_init_read_bit_buffer(pbi, &rb, data + length_field_size, data_end);
- const aom_codec_err_t status = read_obu_header(&rb, &obu_header);
+ const aom_codec_err_t status =
+ read_obu_header(&rb, cm->is_annexb, &obu_header);
if (status != AOM_CODEC_OK) {
cm->error.error_code = status;
return;
}
- if (read_obu_size(data + obu_header.size, bytes_available - obu_header.size,
- &payload_size, &length_field_size) != AOM_CODEC_OK) {
- cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
- return;
+ if (!cm->is_annexb) {
+ if (read_obu_size(data + obu_header.size,
+ bytes_available - obu_header.size, &payload_size,
+ &length_field_size) != AOM_CODEC_OK) {
+ cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
+ return;
+ }
+ av1_init_read_bit_buffer(
+ pbi, &rb, data + length_field_size + obu_header.size, data_end);
+ } else {
+ payload_size = obu_size - obu_header.size;
}
- av1_init_read_bit_buffer(
- pbi, &rb, data + length_field_size + obu_header.size, data_end);
data += length_field_size + obu_header.size;
if (data_end < data) {
diff --git a/av1/decoder/obu.h b/av1/decoder/obu.h
index 9fe5795..2295c69 100644
--- a/av1/decoder/obu.h
+++ b/av1/decoder/obu.h
@@ -35,7 +35,8 @@
int get_obu_type(uint8_t obu_header_byte, OBU_TYPE *obu_type);
aom_codec_err_t aom_read_obu_header(uint8_t *buffer, size_t buffer_length,
- size_t *consumed, ObuHeader *header);
+ size_t *consumed, ObuHeader *header,
+ int is_annexb);
void av1_decode_frame_from_obus(struct AV1Decoder *pbi, const uint8_t *data,
const uint8_t *data_end,
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index ac1ae9b..7849ae3 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -5935,6 +5935,63 @@
int av1_get_quantizer(AV1_COMP *cpi) { return cpi->common.base_qindex; }
+int av1_convert_sect5obus_to_annexb(uint8_t *buffer, size_t *frame_size) {
+ size_t output_size = 0;
+ size_t total_bytes_read = 0;
+ size_t remaining_size = *frame_size;
+ uint8_t *buff_ptr = buffer;
+
+ // go through each OBUs
+ while (total_bytes_read < *frame_size) {
+ uint8_t saved_obu_header[2];
+ uint64_t obu_payload_size;
+ size_t length_of_payload_size;
+ size_t length_of_obu_size;
+ uint32_t obu_header_size = (buff_ptr[0] >> 2) & 0x1 ? 2 : 1;
+ size_t obu_bytes_read = obu_header_size; // bytes read for current obu
+
+ // save the obu header (1 or 2 bytes)
+ memmove(saved_obu_header, buff_ptr, obu_header_size);
+ // clear the obu_has_size_field
+ saved_obu_header[0] = saved_obu_header[0] & (~0x2);
+
+ // get the payload_size and length of payload_size
+ if (aom_uleb_decode(buff_ptr + obu_header_size, remaining_size,
+ &obu_payload_size, &length_of_payload_size) != 0) {
+ return AOM_CODEC_ERROR;
+ }
+ obu_bytes_read += length_of_payload_size;
+
+ // calculate the length of size of the obu header plus payload
+ length_of_obu_size =
+ aom_uleb_size_in_bytes((uint64_t)(obu_header_size + obu_payload_size));
+
+ // move the rest of data to new location
+ memmove(buff_ptr + length_of_obu_size + obu_header_size,
+ buff_ptr + obu_bytes_read, remaining_size - obu_bytes_read);
+ obu_bytes_read += obu_payload_size;
+
+ // write the new obu size
+ const uint64_t obu_size = obu_header_size + obu_payload_size;
+ size_t coded_obu_size;
+ if (aom_uleb_encode(obu_size, sizeof(obu_size), buff_ptr,
+ &coded_obu_size) != 0) {
+ return AOM_CODEC_ERROR;
+ }
+
+ // write the saved (modified) obu_header following obu size
+ memmove(buff_ptr + length_of_obu_size, saved_obu_header, obu_header_size);
+
+ total_bytes_read += obu_bytes_read;
+ remaining_size -= obu_bytes_read;
+ buff_ptr += length_of_obu_size + obu_size;
+ output_size += length_of_obu_size + obu_size;
+ }
+
+ *frame_size = output_size;
+ return AOM_CODEC_OK;
+}
+
void av1_apply_encoding_flags(AV1_COMP *cpi, aom_enc_frame_flags_t flags) {
// TODO(yunqingwang): For what references to use, external encoding flags
// should be consistent with internal reference frame selection. Need to
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index df7c434..fde3298 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -288,6 +288,7 @@
int enable_warped_motion;
int allow_warped_motion;
int enable_superres;
+ unsigned int save_as_annexb;
} AV1EncoderConfig;
static INLINE int is_lossless_requested(const AV1EncoderConfig *cfg) {
@@ -619,6 +620,8 @@
int av1_get_quantizer(struct AV1_COMP *cpi);
+int av1_convert_sect5obus_to_annexb(uint8_t *buffer, size_t *input_size);
+
static INLINE int frame_is_kf_gf_arf(const AV1_COMP *cpi) {
return frame_is_intra_only(&cpi->common) || cpi->refresh_alt_ref_frame ||
(cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref);
diff --git a/examples/scalable_decoder.c b/examples/scalable_decoder.c
index 3586df6..6b41c85 100644
--- a/examples/scalable_decoder.c
+++ b/examples/scalable_decoder.c
@@ -102,7 +102,7 @@
size_t buffer_size = 0;
int next_layer_id = 0;
struct AvxInputContext aom_input_ctx;
- struct ObuDecInputContext obu_ctx = { &aom_input_ctx, NULL, 0, 0 };
+ struct ObuDecInputContext obu_ctx = { &aom_input_ctx, NULL, 0, 0, 0 };
aom_codec_stream_info_t si;
uint8_t tmpbuf[32];
unsigned int i;
diff --git a/obudec.c b/obudec.c
index 4d035c1..1ce7d24 100644
--- a/obudec.c
+++ b/obudec.c
@@ -35,6 +35,10 @@
if (!f || !value_buffer || !value_length || !value) return -1;
for (int len = 0; len < OBU_MAX_LENGTH_FIELD_SIZE; ++len) {
const size_t num_read = fread(&value_buffer[len], 1, 1, f);
+ if (num_read == 0 && feof(f)) {
+ *value_length = 0;
+ return 0;
+ }
if (num_read != 1) {
// Ran out of data before completing read of value.
return -1;
@@ -82,8 +86,8 @@
// success, and non-zero on failure. When end of file is reached, the return
// value is 0 and the 'bytes_read' value is set to 0.
static int obudec_read_obu_header(FILE *f, size_t buffer_capacity,
- uint8_t *obu_data, ObuHeader *obu_header,
- size_t *bytes_read) {
+ int is_annexb, uint8_t *obu_data,
+ ObuHeader *obu_header, size_t *bytes_read) {
if (!f || buffer_capacity < (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE) ||
!obu_data || !obu_header || !bytes_read) {
return -1;
@@ -107,8 +111,8 @@
}
size_t obu_bytes_parsed = 0;
- const aom_codec_err_t parse_result =
- aom_read_obu_header(obu_data, *bytes_read, &obu_bytes_parsed, obu_header);
+ const aom_codec_err_t parse_result = aom_read_obu_header(
+ obu_data, *bytes_read, &obu_bytes_parsed, obu_header, is_annexb);
if (parse_result != AOM_CODEC_OK || *bytes_read != obu_bytes_parsed) {
fprintf(stderr, "obudec: Error parsing OBU header.\n");
return -1;
@@ -133,7 +137,7 @@
return 0;
}
-static int obudec_read_one_obu(FILE *f, size_t buffer_capacity,
+static int obudec_read_one_obu(FILE *f, size_t buffer_capacity, int is_annexb,
uint8_t *obu_data, uint64_t *obu_length,
ObuHeader *obu_header) {
const size_t kMinimumBufferSize = OBU_DETECTION_SIZE;
@@ -142,8 +146,22 @@
return -1;
}
+ uint64_t obu_payload_length = 0;
+ size_t leb128_length = 0;
+ uint64_t obu_size = 0;
+ if (is_annexb) {
+ if (obudec_read_leb128(f, &obu_data[0], &leb128_length, &obu_size) != 0) {
+ fprintf(stderr, "obudec: Failure reading OBU size length.\n");
+ return -1;
+ } else if (leb128_length == 0) {
+ *obu_length = 0;
+ return 0;
+ }
+ }
+
size_t bytes_read = 0;
- if (obudec_read_obu_header(f, buffer_capacity, obu_data, obu_header,
+ if (obudec_read_obu_header(f, buffer_capacity, is_annexb,
+ obu_data + leb128_length, obu_header,
&bytes_read) != 0) {
return -1;
} else if (bytes_read == 0) {
@@ -151,13 +169,18 @@
return 0;
}
- uint64_t obu_payload_length = 0;
- size_t leb128_length = 0;
- if (obudec_read_leb128(f, &obu_data[bytes_read], &leb128_length,
- &obu_payload_length) != 0) {
- fprintf(stderr, "obudec: Failure reading OBU payload length.\n");
- return -1;
+ if (!is_annexb) {
+ if (obudec_read_leb128(f, &obu_data[bytes_read], &leb128_length,
+ &obu_payload_length) != 0) {
+ fprintf(stderr, "obudec: Failure reading OBU payload length.\n");
+ return -1;
+ }
}
+
+ if (is_annexb) {
+ obu_payload_length = obu_size - bytes_read;
+ }
+
bytes_read += leb128_length;
if (UINT64_MAX - bytes_read < obu_payload_length) return -1;
@@ -181,14 +204,14 @@
struct AvxInputContext *avx_ctx = obu_ctx->avx_ctx;
uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 };
-
+ const int is_annexb = obu_ctx->is_annexb;
FILE *f = avx_ctx->file;
uint64_t obu_length = 0;
ObuHeader obu_header;
memset(&obu_header, 0, sizeof(obu_header));
- if (obudec_read_one_obu(f, OBU_DETECTION_SIZE, &detect_buf[0], &obu_length,
- &obu_header) != 0) {
+ if (obudec_read_one_obu(f, OBU_DETECTION_SIZE, is_annexb, &detect_buf[0],
+ &obu_length, &obu_header) != 0) {
fprintf(stderr, "obudec: Failure reading first OBU.\n");
rewind(f);
return 0;
@@ -213,10 +236,10 @@
rewind(f);
return 0;
}
- } else {
+ } else if (!is_annexb) {
fprintf(stderr, "obudec: OBU size fields required, cannot decode input.\n");
rewind(f);
- return 0;
+ return (0);
}
// Appears that input is valid Section 5 AV1 stream.
@@ -246,6 +269,7 @@
return 1;
}
+ const int is_annexb = obu_ctx->is_annexb;
while (1) {
ObuHeader obu_header;
memset(&obu_header, 0, sizeof(obu_header));
@@ -254,7 +278,8 @@
uint8_t *data = obu_ctx->buffer + obu_ctx->bytes_buffered;
const size_t capacity = obu_ctx->buffer_capacity - obu_ctx->bytes_buffered;
- if (obudec_read_one_obu(f, capacity, data, &obu_size, &obu_header) != 0) {
+ if (obudec_read_one_obu(f, capacity, is_annexb, data, &obu_size,
+ &obu_header) != 0) {
fprintf(stderr, "obudec: read_one_obu failed in TU loop\n");
return -1;
}
diff --git a/obudec.h b/obudec.h
index 158202e..c413f20 100644
--- a/obudec.h
+++ b/obudec.h
@@ -22,9 +22,11 @@
uint8_t *buffer;
size_t buffer_capacity;
size_t bytes_buffered;
+ int is_annexb;
};
-// Returns 1 when file data starts with what appears to be a Temporal Delimiter
+// Returns 1 when file data starts (if Annex B stream, after reading the
+// size of the OBU) with what appears to be a Temporal Delimiter
// OBU as defined by Section 5 of the AV1 bitstream specification.
int file_is_obu(struct ObuDecInputContext *obu_ctx);