Upgrade libopenapv to v0.1.11.3 am: e512872da8

Original change: https://android-review.googlesource.com/c/platform/external/libopenapv/+/3528750

Change-Id: I55d916594230070fe55b6f8e0d4572ba50fe05e1
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f7a0947..0d0e37e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -65,7 +65,7 @@
 
       - name: Build ARM
         run: |
-          cmake -DCMAKE_BUILD_TYPE=Release -S ${{github.workspace}} -B ${{github.workspace}}/build-arm -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DARM=TRUE -DCMAKE_SYSTEM_PROCESSOR=aarch64
+          cmake -S ${{github.workspace}} -B ${{github.workspace}}/build-arm -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/arm64_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
           cmake --build ${{github.workspace}}/build-arm
 
   test-linux:
diff --git a/.github/workflows/release_packages.yml b/.github/workflows/release_packages.yml
index 861497b..cb92d2f 100644
--- a/.github/workflows/release_packages.yml
+++ b/.github/workflows/release_packages.yml
@@ -57,7 +57,7 @@
 
       - name: Build ARM
         run: |
-          cmake -DCMAKE_BUILD_TYPE=Release -S ${{github.workspace}} -B ${{github.workspace}}/build-arm -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DARM=TRUE -DCMAKE_SYSTEM_PROCESSOR=aarch64
+          cmake -S ${{github.workspace}} -B ${{github.workspace}}/build-arm -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/arm64_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
           cmake --build ${{github.workspace}}/build-arm
           cd ${{github.workspace}}/build-arm
           cpack -C Release
@@ -67,8 +67,8 @@
         with:
           name: openapv-arm-${{github.event.release.tag_name}}
           path: |
-            ${{ github.workspace }}/build/*.deb
-            ${{ github.workspace }}/build/*.md5
+            ${{ github.workspace }}/build-arm/*.deb
+            ${{ github.workspace }}/build-arm/*.md5
           retention-days: 7
 
       - name: Upload ARM assets to GitHub Release
@@ -76,7 +76,7 @@
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
-          file: "build/*.deb; build/*.md5"
+          file: "build-arm/*.deb; build-arm/*.md5"
           update_latest_release: true
           draft: false
           overwrite: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 92b1846..b86694e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -198,10 +198,10 @@
 set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
 
 set(CPACK_PACKAGE_NAME "OpenAPV")
-set(CPACK_PACKAGE_VENDOR "OpenAPV")
-set(CPACK_PACKAGE_CONTACT "https://github.com/openapv")
-set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/openapv/oapv/releases")
-set(CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/openapv/oapv")
+set(CPACK_PACKAGE_VENDOR "AcademySoftwareFoundation")
+set(CPACK_PACKAGE_CONTACT "https://github.com/AcademySoftwareFoundation")
+set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/AcademySoftwareFoundation/openapv/releases")
+set(CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/AcademySoftwareFoundation/openapv")
 set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open Advanced Professional Video Codec")
 set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
 set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
@@ -209,7 +209,9 @@
 set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
 set(CPACK_PACKAGE_CHECKSUM MD5)
 
-set(CPACK_DEBIAN_PACKAGE_MAINTAINER "OpenAPV")
+set(CPACK_DEBIAN_PACKAGE_MAINTAINER "AcademySoftwareFoundation")
+set(CPACK_DEBIAN_PACKAGE_SECTION "video")
+set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
 
 include(CPack)
 
diff --git a/METADATA b/METADATA
index c985b01..a0d5c2e 100644
--- a/METADATA
+++ b/METADATA
@@ -8,14 +8,14 @@
   license_type: NOTICE
   last_upgrade_date {
     year: 2025
-    month: 2
-    day: 18
+    month: 3
+    day: 5
   }
   homepage: "https://github.com/openapv/openapv"
   identifier {
     type: "Git"
     value: "https://github.com/openapv/openapv.git"
-    version: "v0.1.11"
+    version: "v0.1.11.3"
     primary_source: true
   }
 }
diff --git a/README.md b/README.md
index 8486a71..eb753b3 100644
--- a/README.md
+++ b/README.md
@@ -49,21 +49,21 @@
   - mingw-w64
   - mingw-w64-tools
 
-- Build Instructions PC
+- Build Instructions PC (Linux)
   ```
   cmake -DCMAKE_BUILD_TYPE=Release -S . -B build
   cmake --build build
   ```
 
-- Build Instructions ARM
+- Build Instructions ARM (Crosscompile)
   ```
-  cmake -DCMAKE_BUILD_TYPE=Release -S . -B build-arm -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DARM=TRUE -DCMAKE_SYSTEM_PROCESSOR=aarch64
+  cmake -S . -B build-arm -DCMAKE_TOOLCHAIN_FILE=aarch64_toolchain.cmake -DCMAKE_BUILD_TYPE=Release 
   cmake --build build-arm
   ```
 
 - Build Instructions Windows (Crosscompile)
   ```
-  cmake -S . -B build-windows -DCMAKE_TOOLCHAIN_FILE=windows_x86_64_toolchain.cmake
+  cmake -S . -B build-windows -DCMAKE_TOOLCHAIN_FILE=windows_x86_64_toolchain.cmake -DCMAKE_BUILD_TYPE=Release 
   cmake --build build-windows
   ```
 
@@ -97,6 +97,18 @@
 
     oapv_app_dec -i encoded.apv -o output.y4m
 
+## Utility
+
+### Graphical APV bitstream parser
+
+Pattern file of APV bitstream for [ImHex](https://github.com/WerWolv/ImHex) is provided [here](/util/apv.hexpat).
+1. Install [ImHex](https://github.com/WerWolv/ImHex) application
+2. Download [APV pattern file](/util/apv.hexpat)
+2. Open APV bitstream (\*.apv file) with ImHex
+3. Import the APV pattern file on Pattern editor view of ImHex and apply
+
+![APV_on_ImHex](/readme/img/apv_parser_on_imhex.png)
+
 ## Testing
 
 In build directory run ``ctest``
diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c
index d3e2c8e..58fa94c 100644
--- a/app/oapv_app_enc.c
+++ b/app/oapv_app_enc.c
@@ -499,9 +499,9 @@
     param->csp = vars->input_csp;
 
     /* update level idc */
-    double tmp_level = 0;
-    sscanf(vars->level, "%lf", &tmp_level);
-    param->level_idc = tmp_level * 30;
+    float tmp_level = 0;
+    sscanf(vars->level, "%f", &tmp_level);
+    param->level_idc = (int)((tmp_level * 30.0) + 0.5);
     /* update band idc */
     param->band_idc = vars->band;
 
diff --git a/arm64_toolchain.cmake b/arm64_toolchain.cmake
new file mode 100644
index 0000000..9e07152
--- /dev/null
+++ b/arm64_toolchain.cmake
@@ -0,0 +1,13 @@
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
+
+set(ARM=TRUE)
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64)
diff --git a/readme/empty.txt b/readme/empty.txt
deleted file mode 100644
index 588fe22..0000000
--- a/readme/empty.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is empy file
diff --git a/readme/img/apv_parser_on_imhex.png b/readme/img/apv_parser_on_imhex.png
new file mode 100644
index 0000000..3ad7a2e
--- /dev/null
+++ b/readme/img/apv_parser_on_imhex.png
Binary files differ
diff --git a/src/avx/oapv_tq_avx.c b/src/avx/oapv_tq_avx.c
index 2f7b299..22c8d4e 100644
--- a/src/avx/oapv_tq_avx.c
+++ b/src/avx/oapv_tq_avx.c
@@ -519,6 +519,8 @@
     shift = QUANT_SHIFT + tr_shift + (qp / 6);
     offset = (s64)deadzone_offset << (shift - 9);
     __m256i offset_vector = _mm256_set1_epi64x(offset);
+    __m256i reg_minval_int16 = _mm256_set1_epi32(-32768);
+    __m256i reg_maxval_int16 = _mm256_set1_epi32(32767);
 
     int pixels = (1 << (log2_w + log2_h));
     int i;
@@ -533,7 +535,7 @@
         0, 1, 4, 5, 8, 9, 12, 13,
         -128, -128, -128, -128, -128, -128, -128, -128,
         -128, -128, -128, -128, -128, -128, -128, -128);
-    
+
     for (i = 0; i < pixels; i += 8)
     {
         // Load first row
@@ -541,8 +543,8 @@
         __m128i coef_row = _mm_lddqu_si128((__m128i*)(coef + i));
 
         // Extract sign
-        __m256i coef_row_cast = _mm256_castsi128_si256(coef_row);
-        __m256i sign_mask = _mm256_srai_epi16(coef_row_cast, 15);
+        __m128i sign_mask = _mm_srai_epi16(coef_row, 15);
+        __m256i sign_mask_ext = _mm256_cvtepi16_epi32(sign_mask);
 
         // Convert to 32 bits and take abs()
         __m256i coef_row_ext = _mm256_cvtepi16_epi32(coef_row);
@@ -557,25 +559,23 @@
         // First level of combination
         lev2_low = _mm256_slli_epi64(lev2_low, 32);
         __m256i combined = _mm256_or_si256(lev2_low, lev2_high);
+        __m256i levx = _mm256_permutevar8x32_epi32(combined, shuffle0);
+
+        // Apply sign and clipping
+        levx = _mm256_sub_epi32(_mm256_xor_si256(levx, sign_mask_ext), sign_mask_ext);
+        levx = _mm256_max_epi32(levx, reg_minval_int16);
+        levx = _mm256_min_epi32(levx, reg_maxval_int16);
 
         // Second level of combination
-        __m256i levx = _mm256_permutevar8x32_epi32(combined, shuffle0);
-        __m128i levx_low = _mm256_castsi256_si128(levx);
-        __m256i levx_low_ext = _mm256_castsi128_si256(levx_low);
-        levx_low_ext = _mm256_shuffle_epi8(levx_low_ext, shuffle1);
+        __m256i levx_low_sh = _mm256_shuffle_epi8(levx, shuffle1);
         __m128i levx_high = _mm256_extracti128_si256(levx, 1);
         __m256i levx_high_ext = _mm256_castsi128_si256(levx_high);
-        levx_high_ext = _mm256_shuffle_epi8(levx_high_ext, shuffle2);
-        levx = _mm256_or_si256(levx_high_ext, levx_low_ext);
+        __m256i levx_high_sh = _mm256_shuffle_epi8(levx_high_ext, shuffle2);
+        levx = _mm256_or_si256(levx_high_sh, levx_low_sh);
 
-        // Apply sign
-        levx = _mm256_sub_epi16(_mm256_xor_si256(levx, sign_mask), sign_mask);
-
-        // Clip and store in coef
+        // store in coef
         __m128i lev4 = _mm256_castsi256_si128(levx);
-        __m128i lev5 = _mm_max_epi16(lev4, _mm_set1_epi16(-32768));
-        __m128i lev6 = _mm_min_epi16(lev5, _mm_set1_epi16(32767));
-        _mm_storeu_si128((__m128i*)(coef + i), lev6);
+        _mm_storeu_si128((__m128i*)(coef + i), lev4);
     }
     return OAPV_OK;
 }
diff --git a/src/oapv.c b/src/oapv.c
index 164d334..8fe2fa8 100644
--- a/src/oapv.c
+++ b/src/oapv.c
@@ -1467,7 +1467,7 @@
     param->tile_h_mb = 16;
 
     param->profile_idc = OAPV_PROFILE_422_10;
-    param->level_idc = (int)(4.1 * 30);
+    param->level_idc = (int)((4.1 * 30.0) + 0.5);
     param->band_idc = 2;
 
     param->use_q_matrix = 0;
@@ -1959,10 +1959,10 @@
         oapv_bsr_init(&ctx->bs, (u8 *)bitb->addr + cur_read_size, remain, NULL);
         bs = &ctx->bs;
 
-        ret = oapvd_vlc_pbu_size(bs, &pbu_size); // 4byte
+        ret = oapvd_vlc_pbu_size(bs, &pbu_size); // read pbu_size (4 byte)
         oapv_assert_g(OAPV_SUCCEEDED(ret), ERR);
-        oapv_assert_gv((pbu_size + 4) <= bs->size, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR);
-
+        remain -= 4; // size of pbu_size syntax
+        oapv_assert_gv(pbu_size <= remain, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR);
 
         ret = oapvd_vlc_pbu_header(bs, &pbuh);
         oapv_assert_g(OAPV_SUCCEEDED(ret), ERR);
@@ -2065,20 +2065,22 @@
 
     DUMP_SET(0);
 
+    /* 'au' address contains series of PBU */
     do {
         oapv_bs_t bs;
-        u32       pbu_size = 0;
+        u32 pbu_size = 0;
         u32 remain = au_size - cur_read_size;
         oapv_assert_rv((remain >= 8), OAPV_ERR_MALFORMED_BITSTREAM);
         oapv_bsr_init(&bs, (u8 *)au + cur_read_size, remain, NULL);
 
-        ret = oapvd_vlc_pbu_size(&bs, &pbu_size); // 4 byte
+        ret = oapvd_vlc_pbu_size(&bs, &pbu_size); // read pbu_size (4 byte)
         oapv_assert_rv(OAPV_SUCCEEDED(ret), ret);
-        oapv_assert_rv((pbu_size + 4) <= bs.size, OAPV_ERR_MALFORMED_BITSTREAM);
+        remain -= 4; // size of pbu_size syntax
+        oapv_assert_rv(pbu_size <= remain, OAPV_ERR_MALFORMED_BITSTREAM);
 
         /* pbu header */
         oapv_pbuh_t pbuh;
-        ret = oapvd_vlc_pbu_header(&bs, &pbuh); // 4 byte
+        ret = oapvd_vlc_pbu_header(&bs, &pbuh); // read pbu_header() (4 byte)
         oapv_assert_rv(OAPV_SUCCEEDED(ret), OAPV_ERR_MALFORMED_BITSTREAM);
         if(pbuh.pbu_type == OAPV_PBU_TYPE_AU_INFO) {
             // parse access_unit_info in PBU
@@ -2109,7 +2111,7 @@
             frm_count++;
         }
         aui->num_frms = frm_count;
-        cur_read_size += pbu_size + 4;
+        cur_read_size += pbu_size + 4; /* 4byte is for pbu_size syntax itself */
     } while(cur_read_size < au_size);
     DUMP_SET(1);
     return OAPV_OK;
diff --git a/src/oapv_tq.c b/src/oapv_tq.c
index 8745b91..af0bad5 100644
--- a/src/oapv_tq.c
+++ b/src/oapv_tq.c
@@ -97,6 +97,11 @@
 
 static int oapv_quant(s16 *coef, u8 qp, int q_matrix[OAPV_BLK_D], int log2_w, int log2_h, int bit_depth, int deadzone_offset)
 {
+    // coef is the output of the transform, the bit range is 16
+    // q_matrix has the value of q_scale * 16 / q_matrix, the bit range is 19
+    // (precision of q_scale is 15, and the range of q_mtrix is 1~255)
+    // lev is the product of abs(coef) and q_matrix, the bit range is 35
+
     s64 lev;
     s32 offset;
     int sign;
diff --git a/test/README.md b/test/README.md
index 2af8348..a404bfc 100644
--- a/test/README.md
+++ b/test/README.md
@@ -5,18 +5,18 @@
 
 | No. | Bitstream Name | Description                                                  | Profile&nbsp;&nbsp; | Level | Band | Frame Rate | Resolution | # of Frame | MD5 sum of bitstream             |
 |-----|----------------|--------------------------------------------------------------|---------------------|-------|------|------------|------------|------------|----------------------------------|
-| 1   | tile_A         | one-tile per   one-picture                                   | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | b6e1cef839381b2c90cb9ffcdf537d77 |
-| 2   | tile_B         | Tile size = min size   tile (256x128)                        | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 9a0cb5126d705b03a2e7bcdcbacf6fbf |
+| 1   | tile_A         | one-tile per   one-picture                                   | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 74c5c0ca1bd2cfb28c6e2e0673e965f9 |
+| 2   | tile_B         | Tile size = min size   tile (256x128)                        | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 666ec80235a1e8f59db044d77a89a495 |
 | 3   | tile_C         | # of Tiles: max num   tile (20x20)                           | 422-10              | 5     | 0    | 30 fps     | 7680x4320  | 3          | 75363d036965a9dccc90a9ce8d0ae652 |
-| 4   | tile_D         | tile dummy data test                                         | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 0394e3ac275e2bc595c07c5290dc9466 |
-| 5   | tile_E         | tile_size_present_in_fh_flag=on                              | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | fdf72572b6551bc6a9eed7f80ca0ec0f |
+| 4   | tile_D         | tile dummy data test                                         | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | dd492519c90409a9ca5710746f45c125 |
+| 5   | tile_E         | tile_size_present_in_fh_flag=on                              | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 134c4aa46cec9ab0299824682a89eecd |
 | 6   | qp_A           | QP matrix enabled                                            | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 5ca6d4ea0f65add261b44ed3532a0a73 |
-| 7   | qp_B           | Tile QP   variation in a frame                               | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 1b24d4f97c18545b7881002cc642839b |
+| 7   | qp_B           | Tile QP   variation in a frame                               | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 85bfa477911447d994c17dea9703a9c7 |
 | 8   | qp_C           | Set all the QPs in a   frame equal to min. QP (=0)           | 422-10              | 6     | 2    | 60 fps     | 3840x2160  | 3          | 8c2928ec05eb06d42d6a8bda0ceb7e8d |
-| 9   | qp_D           | Set all the QPs in a   frame equal to max. QP (=51)          | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | e5acd3d3a0aa7bd6a45a49af35980512 |
-| 10  | qp_E           | Set different QP   betwee luma and chroma                    | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | e58ea5df35750c0d19cffefde12e78c4 |
-| 11  | syn_A          | Exercise a synthetic   image with QP = 0 and QP = 51         | 422-10              | 4.1   | 2    | 60 fps     | 1920x1080  | 2          | e1593a670c62d69718986ff84d1150f3 |
-| 12  | syn_B          | Exercise a synthetic   image with Tile QP variation in Frame | 422-10              | 4.1   | 2    | 60 fps     | 1920x1080  | 2          | 9f188e39824829aa05db584034ab1fd0 |
+| 9   | qp_D           | Set all the QPs in a   frame equal to max. QP (=51)          | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 9c98e376fb59100f5a5585482fb33746 |
+| 10  | qp_E           | Set different QP   betwee luma and chroma                    | 422-10              | 4.1   | 2    | 60 fps     | 3840x2160  | 3          | 6d1a1bc982d412758f353c8d041979d1 |
+| 11  | syn_A          | Exercise a synthetic   image with QP = 0 and QP = 51         | 422-10              | 4.1   | 2    | 60 fps     | 1920x1080  | 2          | db9f8f7ce57871481e5b257b79149b1e |
+| 12  | syn_B          | Exercise a synthetic   image with Tile QP variation in Frame | 422-10              | 4.1   | 2    | 60 fps     | 1920x1080  | 2          | 5f6c57f0bfe7ceb2f97a56a3bec7fb7a |
 
 ## Test sequence
 "sequence" folder has the uncompressed video sequence for encoder testing.
diff --git a/test/bitstream/qp_B.apv b/test/bitstream/qp_B.apv
index fd3d776..f685630 100644
--- a/test/bitstream/qp_B.apv
+++ b/test/bitstream/qp_B.apv
Binary files differ
diff --git a/test/bitstream/qp_D.apv b/test/bitstream/qp_D.apv
index 6469e60..d094042 100644
--- a/test/bitstream/qp_D.apv
+++ b/test/bitstream/qp_D.apv
Binary files differ
diff --git a/test/bitstream/qp_E.apv b/test/bitstream/qp_E.apv
index 24d8091..62bde8c 100644
--- a/test/bitstream/qp_E.apv
+++ b/test/bitstream/qp_E.apv
Binary files differ
diff --git a/test/bitstream/syn_A.apv b/test/bitstream/syn_A.apv
index b220d05..1d325d1 100644
--- a/test/bitstream/syn_A.apv
+++ b/test/bitstream/syn_A.apv
Binary files differ
diff --git a/test/bitstream/syn_B.apv b/test/bitstream/syn_B.apv
index fb0565c..641a108 100644
--- a/test/bitstream/syn_B.apv
+++ b/test/bitstream/syn_B.apv
Binary files differ
diff --git a/test/bitstream/tile_A.apv b/test/bitstream/tile_A.apv
index 074701f..1f8d213 100644
--- a/test/bitstream/tile_A.apv
+++ b/test/bitstream/tile_A.apv
Binary files differ
diff --git a/test/bitstream/tile_B.apv b/test/bitstream/tile_B.apv
index 9930bf7..f796778 100644
--- a/test/bitstream/tile_B.apv
+++ b/test/bitstream/tile_B.apv
Binary files differ
diff --git a/test/bitstream/tile_D.apv b/test/bitstream/tile_D.apv
index cf0d33e..7d61d5c 100644
--- a/test/bitstream/tile_D.apv
+++ b/test/bitstream/tile_D.apv
Binary files differ
diff --git a/test/bitstream/tile_E.apv b/test/bitstream/tile_E.apv
index 029262b..66b1c3d 100644
--- a/test/bitstream/tile_E.apv
+++ b/test/bitstream/tile_E.apv
Binary files differ
diff --git a/util/apv.hexpat b/util/apv.hexpat
new file mode 100644
index 0000000..dd4ae2c
--- /dev/null
+++ b/util/apv.hexpat
@@ -0,0 +1,224 @@
+#pragma pattern for Advanced Professional Video (*.apv)
+
+import std.io;
+import std.mem;
+#pragma endian big
+
+/* PBU types */
+enum PbuType : u8 {
+    FRM_PRI = 1,
+    FRM_NONPRI = 2,
+    FRM_PREVIEW = 25,
+    FRM_DEPTH = 26,
+    FRM_ALPHA = 27,
+    AUI = 65,
+    METADATA = 66,
+    FILLER = 67
+};
+
+fn get_0xff_ext_var(auto addr) {
+    u32 read = 1;
+    u32 var = 0;
+    u8 ext = std::mem::read_unsigned(addr, 1);
+    
+    while (ext == 0xFF) {
+        var += 0xFF;
+        ext = std::mem::read_unsigned(addr + read, 1);
+        read += 1;
+    }
+    var += ext; 
+    return var;
+};
+
+fn get_0xff_ext_var_bytes(auto addr) {
+    u32 read = 1;
+    u8 ext = std::mem::read_unsigned(addr, 1);
+    
+    while (ext == 0xFF) {
+        ext = std::mem::read_unsigned(addr + read, 1);
+        read += 1;
+    }
+    return read;
+};
+
+struct PbuBase {
+
+    u32 read = 0;
+    str ptype_str = "";
+    
+    /*    
+    syntax code                                                   | type
+    --------------------------------------------------------------|-----
+    pbu_header(){                                                 |
+        pbu_type                                                  | u(8)
+        group_id                                                  | u(16)
+        reserved_zero_8bits                                       | u(8)
+    }
+    */
+        
+    u32 pbu_size; // originally, this syntax is part of AuccessUnit
+    u8 pbu_type;
+    u16 group_id;
+    u8 reserved_zero_8bits;
+    read += 4;
+};
+
+/*    
+syntax code                                                   | type
+--------------------------------------------------------------|-----
+frame_info(){                                                 |
+  profile_idc                                                 | u(8)
+  level_idc                                                   | u(8)
+  band_idc                                                    | u(3)
+  reserved_zero_5bits                                         | u(5)
+  frame_width                                                 | u(24)
+  frame_height                                                | u(24)
+  chroma_format_idc                                           | u(4)
+  bit_depth_minus8                                            | u(4)
+  capture_time_distance                                       | u(8)
+  reserved_zero_8bits                                         | u(8)
+}
+*/
+
+bitfield FrmInfo {
+    profile_idc : 8;
+    level_idc : 8;
+    band_idc : 3;
+    reserved_zero_5bits: 5;
+    frame_width: 24;
+    frame_height: 24;
+    chroma_format_idc: 4;
+    bit_depth_minus8: 4;
+    capture_time_distance: 8;
+    reserved_zero_8bits: 8;
+};
+
+struct PbuFrm:PbuBase {
+    
+    if(pbu_type == PbuType::FRM_PRI) ptype_str = "Frm(Pri)";
+    else if(pbu_type == PbuType::FRM_NONPRI) ptype_str = "Frm(Nonpri)";
+    else if(pbu_type == PbuType::FRM_PREVIEW) ptype_str = "Frm(Preview)";
+    else if(pbu_type == PbuType::FRM_DEPTH) ptype_str = "Frm(Depth)";
+    else if(pbu_type == PbuType::FRM_ALPHA) ptype_str = "Frm(Alpha)";
+    else ptype_str = "Frm(Unknown)";
+    
+    FrmInfo finfo [[name("frame_info()")]];
+    read += 12; // byte size of frame_info()
+    
+    u8 frameData[pbu_size - read] [[sealed]];
+};
+
+u32 metadata_payload_count = 0;
+
+struct MetadataPayload {
+    str ptype_str = "";
+    u32 read = 0;
+    
+    u32 payloadType = get_0xff_ext_var($) [[export]];
+    read += get_0xff_ext_var_bytes($);
+    $ += get_0xff_ext_var_bytes($); // update current reading point
+
+    u32 payloadSize = get_0xff_ext_var($) [[export]];
+    read += get_0xff_ext_var_bytes($);
+    $ += get_0xff_ext_var_bytes($); // update current reading point
+    
+    u64 endOffset = $ + payloadSize;
+
+
+    if (payloadType == 4) ptype_str = "itu_t_t35";
+    else if (payloadType == 5) ptype_str = "mdcv";
+    else if (payloadType == 6) ptype_str = "cll";
+    else if (payloadType == 10) ptype_str = "filler";
+    else if (payloadType == 170) ptype_str = "user_defined";
+    else ptype_str = "undefined";
+    
+    std::print("    metadata payload[{:d}] type = {:d}({}), size = {:d}", metadata_payload_count, payloadType, ptype_str, payloadSize);                 
+
+    u8 payloadData[while($ < endOffset)] [[sealed]];
+    
+    metadata_payload_count += 1;
+    
+    
+} [[name(std::format("MetadataPayload[{}]:{}", (metadata_payload_count - 1), ptype_str))]];
+
+struct PbuMetadata:PbuBase {
+    u64 endOffset = 0;
+    ptype_str = "Metadata";
+    metadata_payload_count = 0; // reset number of metadata payload
+     
+    u32 metadata_size; // syntax
+    
+    endOffset = $ + metadata_size;
+
+    MetadataPayload pay[while($ < endOffset)] [[inline]];
+};
+
+
+struct PbuAui:PbuBase {
+    ptype_str = "aui";
+    u8 data[pbu_size - read] [[sealed]];
+};
+
+struct PbuFiller:PbuBase {
+    ptype_str = "filler";
+    u8 data[pbu_size - read] [[sealed]];
+};
+
+struct PbuUnknown:PbuBase {
+    std::warning(std::format("Unknown PBU type ({})!!!", pbu_type));
+    ptype_str = "unknown";
+    u8 data[pbu_size - read] [[sealed]];
+};
+    
+u32 pbu_count = 0;
+
+struct PBU {  
+    u32 pbu_size = std::mem::read_unsigned($, 4, std::mem::Endian::Big);
+    u8 pbu_type = std::mem::read_unsigned($ + 4, 1, std::mem::Endian::Big);
+    
+    match (pbu_type) {
+        (PbuType::FRM_PRI) : PbuFrm Pbu [[inline]];
+        (PbuType::FRM_NONPRI) :  PbuFrm Pbu [[inline]];
+        (PbuType::FRM_PREVIEW): PbuFrm Pbu [[inline]];
+        (PbuType::FRM_DEPTH):  PbuFrm Pbu [[inline]];
+        (PbuType::FRM_ALPHA): PbuFrm Pbu [[inline]];
+        (PbuType::AUI): PbuAui Pbu [[inline]];
+        (PbuType::METADATA): PbuMetadata Pbu [[inline]];
+        (PbuType::FILLER): PbuFiller Pbu [[inline]];
+        (_) : PbuUnknown Pbu [[inline]];
+    }
+    
+    std::print("  PBU[{:d}] size = {:d}, {}", pbu_count, pbu_size, Pbu.ptype_str);    
+
+    pbu_count += 1;
+
+} [[name(std::format("PBU[{}]:{}", (pbu_count - 1), Pbu.ptype_str))]];
+
+
+u32 au_count = 0;
+
+struct AccessUnit {
+    u64 au_end = 0;
+        
+    u32 au_size; // originally this syntax is part of RawBitstream
+        
+    std::print("AU[{:d}] size = {:d}", au_count, au_size);
+                
+    au_end = $ + au_size;
+    
+    pbu_count = 0; // reset number of PBU
+    PBU pbu[while($ < au_end)] [[inline]];
+};
+
+u32 raw_count = 0;
+
+struct RawBitstream {       
+    AccessUnit AU [[name(std::format("AU[{}]", raw_count))]];
+    raw_count += 1;
+} [[name(std::format("Raw[{}]", (raw_count - 1)))]];
+
+struct ApvBitstream {
+    RawBitstream Raw [[inline]];
+}[[inline]];
+
+ApvBitstream APV[while(!std::mem::eof())] @ 0x0 [[inline]];
\ No newline at end of file
diff --git a/version.txt b/version.txt
index 5e12916..db4afdf 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-v0.1.10.1
+v0.1.11.1