adding support for trailing bits write/read

Change-Id: I012e7af7370fa9de7acbcd03a271b16f2b8b5ae8
diff --git a/aom_dsp/bitwriter.h b/aom_dsp/bitwriter.h
index d6972a1..1f84df6 100644
--- a/aom_dsp/bitwriter.h
+++ b/aom_dsp/bitwriter.h
@@ -52,8 +52,8 @@
   aom_daala_start_encode(bc, buffer);
 }
 
-static INLINE void aom_stop_encode(aom_writer *bc) {
-  aom_daala_stop_encode(bc);
+static INLINE int aom_stop_encode(aom_writer *bc) {
+  return aom_daala_stop_encode(bc);
 }
 
 static INLINE void aom_write(aom_writer *br, int bit, int probability) {
diff --git a/aom_dsp/bitwriter_buffer.c b/aom_dsp/bitwriter_buffer.c
index 75260a8..cad52cb 100644
--- a/aom_dsp/bitwriter_buffer.c
+++ b/aom_dsp/bitwriter_buffer.c
@@ -15,6 +15,12 @@
 #include "./aom_config.h"
 #include "./bitwriter_buffer.h"
 
+#if CONFIG_TRAILING_BITS
+int aom_wb_is_byte_aligned(const struct aom_write_bit_buffer *wb) {
+  return (wb->bit_offset % CHAR_BIT == 0);
+}
+#endif
+
 uint32_t aom_wb_bytes_written(const struct aom_write_bit_buffer *wb) {
   return wb->bit_offset / CHAR_BIT + (wb->bit_offset % CHAR_BIT > 0);
 }
diff --git a/aom_dsp/bitwriter_buffer.h b/aom_dsp/bitwriter_buffer.h
index 85a7883..f273d76 100644
--- a/aom_dsp/bitwriter_buffer.h
+++ b/aom_dsp/bitwriter_buffer.h
@@ -23,6 +23,10 @@
   uint32_t bit_offset;
 };
 
+#if CONFIG_TRAILING_BITS
+int aom_wb_is_byte_aligned(const struct aom_write_bit_buffer *wb);
+#endif
+
 uint32_t aom_wb_bytes_written(const struct aom_write_bit_buffer *wb);
 
 void aom_wb_write_bit(struct aom_write_bit_buffer *wb, int bit);
diff --git a/aom_dsp/daalaboolwriter.c b/aom_dsp/daalaboolwriter.c
index 59af2a2..b24ffbf 100644
--- a/aom_dsp/daalaboolwriter.c
+++ b/aom_dsp/daalaboolwriter.c
@@ -18,11 +18,14 @@
   od_ec_enc_init(&br->ec, 62025);
 }
 
-void aom_daala_stop_encode(daala_writer *br) {
+int aom_daala_stop_encode(daala_writer *br) {
+  int nb_bits;
   uint32_t daala_bytes;
   unsigned char *daala_data;
   daala_data = od_ec_enc_done(&br->ec, &daala_bytes);
+  nb_bits = od_ec_enc_tell(&br->ec);
   memcpy(br->buffer, daala_data, daala_bytes);
   br->pos = daala_bytes;
   od_ec_enc_clear(&br->ec);
+  return nb_bits;
 }
diff --git a/aom_dsp/daalaboolwriter.h b/aom_dsp/daalaboolwriter.h
index e10885b..f9c596c 100644
--- a/aom_dsp/daalaboolwriter.h
+++ b/aom_dsp/daalaboolwriter.h
@@ -34,7 +34,7 @@
 typedef struct daala_writer daala_writer;
 
 void aom_daala_start_encode(daala_writer *w, uint8_t *buffer);
-void aom_daala_stop_encode(daala_writer *w);
+int aom_daala_stop_encode(daala_writer *w);
 
 static INLINE void aom_daala_write(daala_writer *w, int bit, int prob) {
   int p = (0x7FFFFF - (prob << 15) + prob) >> 8;
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 62523c4..3a9d57e 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -3329,8 +3329,14 @@
   }
 }
 
-int av1_decode_frame_headers_and_setup(AV1Decoder *pbi, const uint8_t *data,
+int av1_decode_frame_headers_and_setup(AV1Decoder *pbi,
+#if CONFIG_TRAILING_BITS
+                                       struct aom_read_bit_buffer *rb,
+#endif
+                                       const uint8_t *data,
+#if !CONFIG_TRAILING_BITS
                                        const uint8_t *data_end,
+#endif
                                        const uint8_t **p_data_end) {
   AV1_COMMON *const cm = &pbi->common;
   const int num_planes = av1_num_planes(cm);
@@ -3349,9 +3355,15 @@
   }
   xd->global_motion = cm->global_motion;
 
+#if !CONFIG_TRAILING_BITS
   struct aom_read_bit_buffer rb;
+#endif
   read_uncompressed_header(pbi,
+#if CONFIG_TRAILING_BITS
+                           rb);
+#else
                            av1_init_read_bit_buffer(pbi, &rb, data, data_end));
+#endif
 
   // If cm->single_tile_decoding = 0, the independent decoding of a single tile
   // or a section of a frame is not allowed.
@@ -3361,7 +3373,11 @@
     pbi->dec_tile_col = -1;
   }
 
+#if CONFIG_TRAILING_BITS
+  pbi->uncomp_hdr_size = aom_rb_bytes_read(rb);
+#else
   pbi->uncomp_hdr_size = aom_rb_bytes_read(&rb);
+#endif
   YV12_BUFFER_CONFIG *new_fb = get_frame_new_buffer(cm);
   xd->cur_buf = new_fb;
   if (av1_allow_intrabc(cm)) {
@@ -3371,8 +3387,12 @@
   }
 
   if (cm->show_existing_frame) {
-    // showing a frame directly
+  // showing a frame directly
+#if CONFIG_TRAILING_BITS
+    *p_data_end = data + aom_rb_bytes_read(rb);
+#else
     *p_data_end = data + aom_rb_bytes_read(&rb);
+#endif
 #if CONFIG_FWD_KF
     if (cm->reset_decoder_state) {
       // Use the default frame context values.
diff --git a/av1/decoder/decodeframe.h b/av1/decoder/decodeframe.h
index 4759426..7520251 100644
--- a/av1/decoder/decodeframe.h
+++ b/av1/decoder/decodeframe.h
@@ -30,8 +30,13 @@
 void av1_decode_frame(struct AV1Decoder *pbi, const uint8_t *data,
                       const uint8_t *data_end, const uint8_t **p_data_end);
 int av1_decode_frame_headers_and_setup(struct AV1Decoder *pbi,
+#if CONFIG_TRAILING_BITS
+                                       struct aom_read_bit_buffer *rb,
+#endif
                                        const uint8_t *data,
+#if !CONFIG_TRAILING_BITS
                                        const uint8_t *data_end,
+#endif
                                        const uint8_t **p_data_end);
 
 void av1_decode_tg_tiles_and_wrapup(struct AV1Decoder *pbi, const uint8_t *data,
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index 6667b9a..565be02 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -127,6 +127,23 @@
   return AOM_CODEC_OK;
 }
 
+#if CONFIG_TRAILING_BITS
+// Checks that the remaining bits start with a 1 and ends with 0s.
+// May consume an additional byte, if already byte aligned before the check.
+static int check_trailing_bits(AV1Decoder *pbi,
+                               struct aom_read_bit_buffer *rb) {
+  AV1_COMMON *const cm = &pbi->common;
+  // bit_offset is set to 0 (mod 8) when the reader is already byte aligned
+  int bits_before_alignment = 8 - rb->bit_offset % 8;
+  int trailing = aom_rb_read_literal(rb, bits_before_alignment);
+  if (trailing != (1 << (bits_before_alignment - 1))) {
+    cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
+    return 1;
+  }
+  return 0;
+}
+#endif
+
 static uint32_t read_temporal_delimiter_obu() { return 0; }
 
 static uint32_t read_sequence_header_obu(AV1Decoder *pbi,
@@ -162,10 +179,24 @@
   return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
 }
 
-static uint32_t read_frame_header_obu(AV1Decoder *pbi, const uint8_t *data,
+static uint32_t read_frame_header_obu(AV1Decoder *pbi,
+#if CONFIG_TRAILING_BITS
+                                      struct aom_read_bit_buffer *rb,
+#endif
+                                      const uint8_t *data,
+#if !CONFIG_TRAILING_BITS
                                       const uint8_t *data_end,
+#endif
                                       const uint8_t **p_data_end) {
-  av1_decode_frame_headers_and_setup(pbi, data, data_end, p_data_end);
+  av1_decode_frame_headers_and_setup(pbi,
+#if CONFIG_TRAILING_BITS
+                                     rb,
+#endif
+                                     data,
+#if !CONFIG_TRAILING_BITS
+                                     data_end,
+#endif
+                                     p_data_end);
   return (uint32_t)(pbi->uncomp_hdr_size);
 }
 
@@ -428,8 +459,18 @@
 #endif  // CONFIG_OBU_FRAME
         // Only decode first frame header received
         if (!frame_header_received) {
-          frame_header_size =
-              read_frame_header_obu(pbi, data, data_end, p_data_end);
+#if CONFIG_TRAILING_BITS
+          av1_init_read_bit_buffer(pbi, &rb, data, data_end);
+#endif
+          frame_header_size = read_frame_header_obu(pbi,
+#if CONFIG_TRAILING_BITS
+                                                    &rb,
+#endif
+                                                    data,
+#if !CONFIG_TRAILING_BITS
+                                                    data_end,
+#endif
+                                                    p_data_end);
           frame_header_received = 1;
         }
         decoded_payload_size = frame_header_size;
@@ -469,6 +510,18 @@
         break;
     }
 
+#if CONFIG_TRAILING_BITS
+    // Cannot check bit pattern at the end of frame, redundant frame headers,
+    // tile group, metadata, padding or unrecognized OBUs
+    // because the current code consumes or skips all bytes
+    if (payload_size > 0 &&
+        (obu_header.type == OBU_SEQUENCE_HEADER ||
+         obu_header.type == OBU_FRAME_HEADER) &&
+        check_trailing_bits(pbi, &rb)) {
+      return;
+    }
+#endif
+
     // Check that the signalled OBU size matches the actual amount of data read
     if (decoded_payload_size != payload_size) {
       cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 42aa3ef..6d2786c 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -3529,6 +3529,17 @@
   return length_field_size;
 }
 
+#if CONFIG_TRAILING_BITS
+static void add_trailing_bits(struct aom_write_bit_buffer *wb) {
+  if (aom_wb_is_byte_aligned(wb)) {
+    aom_wb_write_literal(wb, 0x80, 8);
+  } else {
+    // assumes that the other bits are already 0s
+    aom_wb_write_bit(wb, 1);
+  }
+}
+#endif
+
 static uint32_t write_sequence_header_obu(AV1_COMP *cpi, uint8_t *const dst
 #if CONFIG_SCALABILITY
                                           ,
@@ -3563,6 +3574,10 @@
   aom_wb_write_bit(&wb, cm->film_grain_params_present);
 #endif
 
+#if CONFIG_TRAILING_BITS
+  add_trailing_bits(&wb);
+#endif
+
   size = aom_wb_bytes_written(&wb);
   return size;
 }
@@ -3582,6 +3597,10 @@
     return total_size;
   }
 
+#if CONFIG_TRAILING_BITS
+  add_trailing_bits(&wb);
+#endif
+
   uncompressed_hdr_size = aom_wb_bytes_written(&wb);
   total_size = uncompressed_hdr_size;
   return total_size;
@@ -3799,6 +3818,9 @@
       const int is_last_col = (tile_col == tile_cols - 1);
       const int is_last_tile = is_last_col && is_last_row;
       int is_last_tile_in_tg = 0;
+#if CONFIG_TRAILING_BITS
+      int nb_bits = 0;
+#endif
 
       if (new_tg) {
         data = dst + total_size;
@@ -3857,10 +3879,31 @@
 
       aom_start_encode(&mode_bc, dst + total_size);
       write_modes(cpi, &tile_info, &mode_bc, &tok, tok_end);
+#if CONFIG_TRAILING_BITS
+      nb_bits = aom_stop_encode(&mode_bc);
+#else
       aom_stop_encode(&mode_bc);
+#endif
       tile_size = mode_bc.pos;
       assert(tile_size > 0);
 
+#if CONFIG_TRAILING_BITS
+      // similar to add_trailing_bits, but specific to end of last tile
+      if (is_last_tile) {
+        if (nb_bits % 8 == 0) {
+          // the arithmetic encoder ended on a byte boundary
+          // adding a 0b10000000 byte
+          *(dst + total_size + tile_size) = 0x80;
+          tile_size += 1;
+        } else {
+          // arithmetic encoder left several 0 bits
+          // changing the first 0 bit to 1
+          int bit_offset = 7 - nb_bits % 8;
+          *(dst + total_size + tile_size) |= 1 << bit_offset;
+        }
+      }
+#endif
+
       curr_tg_data_size += (tile_size + (is_last_tile_in_tg ? 0 : 4));
       buf->size = tile_size;
       if (tile_size > *max_tile_size) {
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 1327432..83fb26a 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -119,4 +119,5 @@
 set(CONFIG_SKIP_SGR 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_SPATIAL_SEGMENTATION 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TILE_INFO_FIRST 0 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_TRAILING_BITS 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_FILEOPTIONS 1 CACHE NUMBER "AV1 config option flag.")