├── .gitignore ├── ChangeLog ├── LICENSE ├── README.md ├── VERSION ├── debian ├── changelog ├── compat ├── control ├── copyright ├── libimxvpuapi2-dev.install ├── libimxvpuapi2.install ├── rules └── source │ └── format ├── example ├── decode-example.c ├── encode-example.c ├── h264_utils.c ├── h264_utils.h ├── jpeg-dec-example.c ├── jpeg-enc-example.c ├── main.c ├── main.h ├── test-320x240.h264 ├── y4m_io.c └── y4m_io.h ├── imxvpuapi2 ├── imxvpuapi2.c ├── imxvpuapi2.h ├── imxvpuapi2_imx6_coda.c ├── imxvpuapi2_imx6_coda_ipu.c ├── imxvpuapi2_imx6_coda_ipu.h ├── imxvpuapi2_imx8m_hantro_decoder.c ├── imxvpuapi2_imx8m_hantro_dummy_encoder.c ├── imxvpuapi2_imx8m_hantro_h1_encoder.c ├── imxvpuapi2_imx8m_hantro_vc8000_encoder.c ├── imxvpuapi2_jpeg.c ├── imxvpuapi2_jpeg.h ├── imxvpuapi2_priv.c └── imxvpuapi2_priv.h ├── libimxvpuapi2.pc.in ├── waf └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | /.waf* 2 | /.lock-waf* 3 | /build 4 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | ==== version 2.3.1 (2025-02-07) ==== 2 | 3 | * vpu: Add more options, flags, and frameskipping support to encoder API 4 | * vpu: Deprecate min_intra_refresh_mb_count and replace it with flag 5 | This is more practical, since the macroblock count if anyway usually set 6 | to resemble the GOP length, so this might as well be automatically 7 | calculated. 8 | * vpu: encoder: Add GOP size checks 9 | * vpu: encoder: Add imx_vpu_api_enc_get_encoded_frame_ext() 10 | This is a preparation for reworked intra refresh support. In intra 11 | refresh encoding, only the first frame is an I/IDR frame; all followup 12 | frames are P frames. The start of an intra refresh interval qualifies 13 | as a sync point. But since only P frames are present, callers callers 14 | can't rely on the frame type to detect sync points. To fix this, the 15 | imx_vpu_api_enc_get_encoded_frame_ext() is added to the API to be able 16 | to communicate to callers that a sync point was produced. 17 | * imx8m-hantro: Rewrite encoder to directly use the Hantro H1 API 18 | This allows for more fine grained control of the H1 encoder. 19 | Most notably, intra refresh works properly, and skipped frames 20 | are correctly handled. 21 | * imx8m-hantro: encoder: Add closed GOP interval support 22 | * imx8m-hantro: encoder: Rework intra refresh 23 | Use codec specific features for intra refresh. For h.264, use the Hantro 24 | GDR (Gradual Decoder Refresh) feature, since it also takes care of filling 25 | SEI message and SPS/PPS NALUs appropriately. For VP8, implement a manual 26 | method. (Intra refresh is not officially supported in VP8 though. See the 27 | note about VP8 intra refresh in the comment blocks in this commit.) 28 | * imx6-coda: return IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL from 29 | imx_vpu_api_enc_get_skipped_frame_info() 30 | This keeps the imx6 backend consistent with the API. Previously, that 31 | backend's implementation of that function was not returning anything at all. 32 | * imx8m-hantro: implement imx_vpu_api_enc_get_skipped_frame_info() stubs for 33 | all encoders 34 | Implement this function for all encoders to fix a linker error in 35 | gstreamer-imx. 36 | These encoders never return IMX_VPU_API_ENC_OUTPUT_CODE_FRAME_SKIPPED, 37 | so we always return IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL from 38 | imx_vpu_api_enc_get_skipped_frame_info(). 39 | * imx8m-hantro: dummy_encoder: implement imx_vpu_api_enc_get_encoded_frame_ext() 40 | Linking gstreamer-imx will fail if imx_vpu_api_enc_get_encoded_frame_ext) 41 | is not available. 42 | 43 | ==== version 2.3.0 (2023-07-30) ==== 44 | 45 | * vpu: Implement first working version of VC8000E encoder integration 46 | This adds support for the Hanto VC8000E encoder that can be found on 47 | the i.MX8m plus. The VC8000E encoder integration is not finished. 48 | h.264 encoding works fine. h.265 encoding is not yet done. And, 49 | changing framerate mid-stream is currently not supported. 50 | * Replace mxcfb.h with ipu.h in option description 51 | Some BSPs no longer ship with the mxcfb.h header. 52 | * imx6-coda: Fix ENC_ENABLE_SOF_STUFF check 53 | The check was being performed incorrectly, leading to nullbytes 54 | being inserted at the end of the SOF0 marker. 55 | * imx6-coda: Insert JFIF APP0 segment 56 | The VPU does not insert this segment on its own. Some programs need 57 | JFIF data to be present in JPEG files, so insert APP0 manually. 58 | * imx6-coda: Fix decoder and encoder alignments 59 | * imx6-coda: Add more IPU detiler logging 60 | * imx6-coda: Fix decoder Y/U/V offset alignments 61 | As it turns out, these must be aligned to 8-byte boundaries, not to 62 | 4096 byte ones. The latter caused incorrect offset calculations. 63 | 64 | ==== version 2.2.2 (2022-12-28) ==== 65 | 66 | * imx6-coda: Skip incomplete frames instead of just reporting them 67 | * imx6-coda: Only flush bit buffer when framebuffers were registered 68 | This prevents misleading error-level log lines from occurring 69 | * imx6-coda: Prefer semi planar modes over fully planar ones 70 | * imx8m-hantro: Reset decoder write_offset to fill_level when moving read_offset 71 | * Add imx_vpu_api_is_color_format_tiled() function 72 | * update waf to 2.0.24 73 | * Add closed_gop_interval field to ImxVpuApiEncOpenParams 74 | This allows for enforcing regularly occurring IDR 75 | boundaries when encoding to h.264 76 | * imx8m-hantro: Fix supported decoder color formats and include tiled formats 77 | * Fix and improve h.264 max level estimation for encoding 78 | * imx6-coda: Use 2-row alignment in encoder 79 | * imx6-coda: Fix JPEG encoding quantization parameter handling in encoder 80 | 81 | ==== version 2.2.1 (2022-05-10) ==== 82 | 83 | * Add option to not compile examples 84 | * imx8m-hantro: Allow for reusing decoder after drain mode finishes 85 | * imx6-coda: Reset drain mode after EOS processed 86 | * imx6-coda: Reset drain mode flags when flushing 87 | * Use libimxdmabuffer sync access functions to ensure cache coherence 88 | * Add imx_vpu_api_is_color_format_10bit() function 89 | * Documentation fixes 90 | 91 | ==== version 2.2.0 (2021-06-14) ==== 92 | 93 | * Add IMX_VPU_API_DEC_OUTPUT_CODE_VIDEO_PARAMETERS_CHANGED output code 94 | * Deprecate drain mode, better document decoder drain mode 95 | * Various documentation fixes and new overviews about en- and 96 | decoding to help with the basic concepts 97 | * Add note about the current state of i.MX8m plus support 98 | * New functions: 99 | imx_vpu_api_is_color_format_rgb() 100 | imx_vpu_api_vp8_profile_number() 101 | imx_vpu_api_vp8_partition_count_number() 102 | imx_vpu_api_vp9_profile_number() 103 | imx_vpu_api_enc_set_frame_rate() 104 | * imx6-coda: Fix decoded frame fb_context 105 | This was causing crashes when callers relied on said fb_context 106 | 107 | ==== version 2.1.2 (2021-04-25) ==== 108 | 109 | * imx6-coda: Fix skipped frame reporting 110 | * imx8m-hantro: Handle CODEC_ERROR_FRAME codec state 111 | * imx8m-hantro: Limit supported formats and profiles if building for m8xmm 112 | 113 | ==== version 2.1.1 (2021-04-23) ==== 114 | 115 | * imx6 coda: fix bug in IPU based detiling related to frame width/height 116 | * imx6 coda: use the frame width/height from open params if available 117 | * imx6 coda: fix duplicate VPU firmware unloading 118 | * imx8 hantro encoder: fix mid-stream bitrate changes and bitrate logging 119 | 120 | ==== version 2.1.0 (2020-10-18) ==== 121 | 122 | * introduce RGB and packed YUV formats since the Hantro encoder 123 | supports those 124 | * remove hardware specific public headers since they only added 125 | tiled pixel formats; instead, migrate these tiled formats into 126 | ImxVpuApiColorFormat 127 | * imx8 hantro decoder: reorder list of supported decoder color 128 | formats to favor 10 bit output 129 | * imx8 hantro decoder: clear new framebuffer fields to zero 130 | * imx8 hantro encoder: fix segfault caused by trying to unmap 131 | non existing staged raw frame 132 | 133 | ==== version 2.0.1 (2020-04-19) ==== 134 | 135 | * imx6: replace mxcfb.h check with i.MX6 specific imx header check 136 | the mcxfb.h check only makes sense with i.MX6 devices, so requiring 137 | those for others like i.MX8 led to build errors 138 | * update waf to 2.0.12 and switch wscript to use Python 3 139 | * imx6: fix encoder pointer usage in vpu_EncGiveCommand() call 140 | 141 | ==== version 2.0.0 (2019-07-21) ==== 142 | 143 | * complete rewrite to support i.MX6, i.MX8m, i.MX8mm VPUs 144 | 145 | ==== version 0.10.3 (2016-10-12) ==== 146 | 147 | * properly pass on color format in simplified JPEG encoder interface 148 | * add alternative write-callback-style encoding mode 149 | also add encode example variant which uses write-callback style output 150 | * add support for "fake grayscale mode" in encoders 151 | this is done by using I420 internally and filling the U and V planes 152 | with 0x80 bytes 153 | * make sure JPEG quantization table is copied in standardized zig zag order 154 | the VPU does not, so this has to be done explicitely 155 | 156 | ==== version 0.10.2 (2016-05-01) ==== 157 | 158 | * fix AUD NAL positioning in h.264 encoder output 159 | (SPS/PPS/AUD were ordered incorrectly - AUD has to come first, not last) 160 | * fix build error with examples when --enable-static is used 161 | * pass quality factor in simplified JPEG encoder interface correctly 162 | * add functions for querying and setting header data 163 | useful for modifying headers, like VUI data in the SPS RBSP 164 | * documentation updates 165 | 166 | ==== version 0.10.1 (2015-11-30) ==== 167 | 168 | * add debian packaging files 169 | * update waf to version 1.8.16 170 | * add workaround in wscript to prevent stale pkg-config .pc files 171 | * fix memory leak by adding missing IOFreeVirtMem() call in vpulib backend 172 | 173 | ==== version 0.10.0 (2015-11-02) ==== 174 | 175 | Initial release 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libimxvpuapi - frontend for i.MX hardware video codecs 2 | ====================================================== 3 | 4 | This library provides an API for using hardware video codecs on i.MX platforms. 5 | The API abstracts away platform specific details and allows for using the same 6 | code with different hardware video codecs on different i.MX platforms. 7 | 8 | The hardware video codec is referred to as the _VPU_. 9 | 10 | Currently, the following platforms are supported (listed with their VPUs): 11 | 12 | * i.MX6 (Chips&Media CODA960 codec) 13 | * i.MX8m quad (Hantro G1/G2 decoder, no encoder) 14 | it is also sometimes referred to as just "the i.MX8m" 15 | * i.MX8m mini (Hantro G1/G2 decoder, Hantro H1 encoder) 16 | * i.MX8m plus (Hantro G1/G2 decoder, Hantro VC8000E encoder) 17 | 18 | Not yet supported: 19 | 20 | * i.MX8 / i.MX8X (Amphion Malone codec) 21 | 22 | This is the second version of this library. Differences to the older version 23 | are described below. 24 | 25 | 26 | License 27 | ------- 28 | 29 | This library is licensed under the LGPL v2.1. 30 | 31 | 32 | Dependencies 33 | ------------ 34 | 35 | libimxvpuapi depends on [libimxdmabuffer](https://github.com/dv1/libimxdmabuffer). 36 | 37 | Additional dependencies are specific to the target platform: 38 | 39 | * i.MX6: `imx-vpu` package version 3.10.17 or newer. 40 | * i.MX8m quad & i.MX8m mini: `imx-vpu-hantro` 1.8.0 or newer. 41 | Please note that the build scripts assume that this is a version of the 42 | `imx-vpu-hantro` package with fixed header installation destination. Earlier 43 | versions installed all Hantro headers in the main include directory. Newer 44 | ones create `hantro_enc` and `hantro_dec`subdirectories. libimxvpuapi 45 | expects these directories to exist. 46 | * i.MX8m plus: `imx-vpu-hantro` 1.8.0 or newer, just like above 47 | (since the plus has the same G1 / G2 decoder as the i.MX8m mini), 48 | and also `imx-vpu-hantro-vc` 1.1.0 or newer (for the VC8000E encoder). 49 | 50 | 51 | Building and installing 52 | ----------------------- 53 | 54 | This project uses the [waf meta build system](https://waf.io/). 55 | To configure , first set the following environment variables to whatever is 56 | necessary for cross compilation for your platform: 57 | 58 | * `CC` 59 | * `CFLAGS` 60 | * `LDFLAGS` 61 | * `PKG_CONFIG_PATH` 62 | * `PKG_CONFIG_SYSROOT_DIR` 63 | 64 | Then, run: 65 | 66 | ./waf configure --prefix=PREFIX --imx-platform=IMX_PLATFORM --sysroot-path=SYSROOT 67 | 68 | (The aforementioned environment variables are only necessary for this 69 | configure call.) 70 | 71 | The arguments are as follows: 72 | * `PREFIX` defines the installation prefix, that is, where the built binaries 73 | will be installed. 74 | * `IMX_PLATFORM` specifies what i.MX platform to build for. See the list below 75 | for the valid values. 76 | * `SYSROOT` is the absolute path to the sysroot for the platform. This is the 77 | path where `usr/include/imx/mxcfb.h` can be found. In cross compilation 78 | environments like Yocto or buildroot, this is where the sysroot files for the 79 | target i.MX platforms are. 80 | 81 | Valid `IMX_PLATFORM` values are: 82 | 83 | * `imx6` : i.MX6 (all variants) 84 | * `imx8m` : i.MX8m quad 85 | * `imx8mm` : i.MX8m mini 86 | * `imx8mp` : i.MX8m plus 87 | 88 | 89 | Once configuration is complete, run: 90 | 91 | ./waf 92 | 93 | This builds the library. 94 | Finally, to install, run: 95 | 96 | ./waf install 97 | 98 | This will install the headers in `$PREFIX/include/imxvpuapi2/`, the libraries 99 | in `$PREFIX/lib/`, and generate a pkg-config .pc file, which is placed in 100 | `$PREFIX/lib/pkgconfig/` . 101 | 102 | 103 | API documentation 104 | ----------------- 105 | 106 | The API is documented in the `imxvpuapi/imxvpuapi.h` header. 107 | 108 | 109 | Examples 110 | -------- 111 | 112 | libimxvpuapi comes with these examples in the `example/` directory: 113 | 114 | * `decode-example.c` : demonstrates how to use the decoder API 115 | * `encode-example.c` : demonstrates how to use the encoder API 116 | 117 | (Other source files in the `example/` directory are common utility code used 118 | by all examples above.) 119 | 120 | Raw frames are read/written as [YUV4MPEG2 (y4m) data](https://wiki.multimedia.cx/index.php/YUV4MPEG2), 121 | and encoded to / decoded from h.264. 122 | 123 | 124 | Known issues 125 | ------------ 126 | 127 | **i.MX6 VPU timeout** 128 | 129 | If errors like `imx_vpu_api_dec_decode() failed: timeout` or `VPU blocking: timeout` 130 | are observed, check if the following workarounds for known problems help: 131 | 132 | * Overclocked VPU: The i.MX6 VPU is clocked at 266 MHz by default (according to the 133 | VPU documentation). Some configurations clock the VPU at 352 MHz, and can 134 | exhibit VPU timeout problems, particularly during h.264 encoding. Try running 135 | the VPU at 266 MHz. 136 | 137 | * Known issue with IPU configuration: As shown by [this Github entry](https://github.com/Freescale/libimxvpuapi/issues/11), 138 | the `CONFIG_IMX_IPUV3_CORE` kernel config flag can cause problems with the 139 | VPU. Disable it, then try again. 140 | 141 | * Low-level i.MX6 VPU library bug: imx-vpu versions prior to 5.4.31 also have been 142 | observed to cause VPU timeouts. These seem to be related to the 5.4.31 fix 143 | described as: "Fix VPU blocked in BWB module". 144 | 145 | **VP6 frames decoded upside down by Hantro G1 decoder** 146 | 147 | This seems to be a bug in imx-vpu-hantro. A workaround is currently not known. 148 | 149 | **imx_vpu_api_dec_add_framebuffers_to_pool() fails on Hantro VPUs, logs show "CODEC_ERROR_UNSPECIFIED"** 150 | This can happen when too many framebuffers are added. One common situation is when 151 | libimxvpuapi is used through gstreamer-imx, and downstream has a queue that soaks 152 | up too many buffers. Example: 153 | 154 | gst-launch-1.0 filesrc location=test-vp8.mkv ! matroskademux ! imxvpudec_vp8 ! queue ! autovideosink 155 | 156 | If the video sink takes a while to start, it is possible that the queue in this example 157 | keeps storing more and more buffers without releasing them. This causes the VPU to 158 | repeatedly request more framebuffers to decode more frames into. Newer versions of 159 | imx-vpu-hantro define upper limits to how many framebuffers can be added, and if these 160 | limits are exceeded, the error occurs. 161 | 162 | It is recommended to not hold on to too many framebuffers at once. Keep it below 16 163 | framebuffers. In the GStreamer case above, it is sufficient to set the `max-size-buffers` 164 | property of the queue there to 1, since a queue that is right before a video sink is 165 | meant for decoupling the upstream portion of that pipeline from the video sink. This 166 | helps with reducing the likelihood of lost frames. More than 1 buffer in the queue is 167 | not needed for this. 168 | 169 | 170 | DWL and EWL errors 171 | ------------------ 172 | 173 | On machines with Hantro based VPUs, "DWL" and "EWL" errors may appear. DWL and EWL 174 | are low level layers used by the Hantro drivers. Errors typically happen when certain 175 | device nodes are not available. If such errors occur, check for the presence of: 176 | 177 | * `/dev/ion` : Used with older imx kernels (<5.15) 178 | * `/dev/dma_heap/linux,cma-uncached` : Used with newer imx kernels (>=5.15) 179 | * `/dev/mxc_hantro` : Hantro G1 and G2 decoder ; found on the i.MX8m mini, 180 | i.MX8m quad, i.MX8m plus 181 | * `/dev/mxc_hantro_h1` : Hantro H1 encoder ; found on the i.MX8m mini 182 | * `/dev/mxc_hantro_vc8000e` : Hantro VC8000E encoder ; found on the i.MX8m plus 183 | 184 | If at least one of these are missing, check the kernel configuration. The ION 185 | and/or the dma-heap CMA allocator needs to be enabled. Also check that the 186 | Hantro VPU is enabled. 187 | 188 | 189 | Differences to the older libimxvpuapi version 190 | --------------------------------------------- 191 | 192 | This is version 2 of libimxvpuapi. Major changes are: 193 | 194 | * The API has been rewritten, and is incompatible with the older one. 195 | * The `imx_vpu_` prefix has been changed to `imx_vpu_api_`. 196 | * Files have been renamed from `imxvpuapi*` to `imxvpuapi2` to reflect the 197 | new and incompatible API and to allow for coexistence with the old version 198 | of the library. 199 | * DMA allocation functions have been factored out as a separate library, 200 | [libimxdmabuffer](https://github.com/Freescale/libimxdmabuffer). 201 | * The old API required checks with `imx_vpu_dec_check_if_can_decode()` to see 202 | if decoding is possible now to avoid a deadlock. This is no longer needed. 203 | Decoders either perform internal DMA-based copies of frames (i.MX6, in the 204 | same step as detiling from the VPU layout), or accept additional framebuffers 205 | in their framebuffer pools on the fly. This simplifies the use of the 206 | decoding API considerably. 207 | * API for getting global en/decoder information and list of supported formats, 208 | profiles, levels has been added. 209 | * Color formats have been merged with the semi/fully planar attribute. 210 | * h.265 colorimetry information has been added. 211 | * Encoder API now returns encoded data in a two-step approach: First, the size 212 | of the encoded data is returned. Then the user allocates a buffer with at 213 | least that size. Finally, that buffer is passed to the encoder to retrieve 214 | the encoded data. 215 | * the `imx_vpu_load()` and `imx_vpu_unload()` functions are gone. (Un)loading 216 | the i.MX6 VPU firmware is now done internally. 217 | * 10-bit support and tiled output support have been added (the latter's tiling 218 | layout is platform specific). 219 | * `codec_format` was renamed to `compression_format` to avoid confusion with 220 | the notion of hardware video codecs. 221 | 222 | 223 | To do 224 | ----- 225 | 226 | * Add more encoder options (and evaluate which ones to add) 227 | * More wiki entries describing format support per i.MX platform / codec 228 | * RealVideo decoding support 229 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libimxvpuapi (2.3.1) stable; urgency=low 2 | 3 | * vpu: Add more options, flags, and frameskipping support to encoder API 4 | * vpu: Deprecate min_intra_refresh_mb_count and replace it with flag 5 | This is more practical, since the macroblock count if anyway usually set 6 | to resemble the GOP length, so this might as well be automatically 7 | calculated. 8 | * vpu: encoder: Add GOP size checks 9 | * vpu: encoder: Add imx_vpu_api_enc_get_encoded_frame_ext() 10 | This is a preparation for reworked intra refresh support. In intra 11 | refresh encoding, only the first frame is an I/IDR frame; all followup 12 | frames are P frames. The start of an intra refresh interval qualifies 13 | as a sync point. But since only P frames are present, callers callers 14 | can't rely on the frame type to detect sync points. To fix this, the 15 | imx_vpu_api_enc_get_encoded_frame_ext() is added to the API to be able 16 | to communicate to callers that a sync point was produced. 17 | * imx8m-hantro: Rewrite encoder to directly use the Hantro H1 API 18 | This allows for more fine grained control of the H1 encoder. 19 | Most notably, intra refresh works properly, and skipped frames 20 | are correctly handled. 21 | * imx8m-hantro: encoder: Add closed GOP interval support 22 | * imx8m-hantro: encoder: Rework intra refresh 23 | Use codec specific features for intra refresh. For h.264, use the Hantro 24 | GDR (Gradual Decoder Refresh) feature, since it also takes care of filling 25 | SEI message and SPS/PPS NALUs appropriately. For VP8, implement a manual 26 | method. (Intra refresh is not officially supported in VP8 though. See the 27 | note about VP8 intra refresh in the comment blocks in this commit.) 28 | * imx6-coda: return IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL from 29 | imx_vpu_api_enc_get_skipped_frame_info() 30 | This keeps the imx6 backend consistent with the API. Previously, that 31 | backend's implementation of that function was not returning anything at all. 32 | * imx8m-hantro: implement imx_vpu_api_enc_get_skipped_frame_info() stubs for 33 | all encoders 34 | Implement this function for all encoders to fix a linker error in 35 | gstreamer-imx. 36 | These encoders never return IMX_VPU_API_ENC_OUTPUT_CODE_FRAME_SKIPPED, 37 | so we always return IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL from 38 | imx_vpu_api_enc_get_skipped_frame_info(). 39 | * imx8m-hantro: dummy_encoder: implement imx_vpu_api_enc_get_encoded_frame_ext() 40 | Linking gstreamer-imx will fail if imx_vpu_api_enc_get_encoded_frame_ext)( 41 | is not available. 42 | 43 | -- Carlos Rafael Giani Fri, 07 Feb 2025 08:01:00 +0100 44 | 45 | libimxvpuapi (2.3.0) stable; urgency=low 46 | 47 | * vpu: Implement first working version of VC8000E encoder integration 48 | This adds support for the Hanto VC8000E encoder that can be found on 49 | the i.MX8m plus. The VC8000E encoder integration is not finished. 50 | h.264 encoding works fine. h.265 encoding is not yet done. And, 51 | changing framerate mid-stream is currently not supported. 52 | * Replace mxcfb.h with ipu.h in option description 53 | Some BSPs no longer ship with the mxcfb.h header. 54 | * imx6-coda: Fix ENC_ENABLE_SOF_STUFF check 55 | The check was being performed incorrectly, leading to nullbytes 56 | being inserted at the end of the SOF0 marker. 57 | * imx6-coda: Insert JFIF APP0 segment 58 | The VPU does not insert this segment on its own. Some programs need 59 | JFIF data to be present in JPEG files, so insert APP0 manually. 60 | * imx6-coda: Fix decoder and encoder alignments 61 | * imx6-coda: Add more IPU detiler logging 62 | * imx6-coda: Fix decoder Y/U/V offset alignments 63 | As it turns out, these must be aligned to 8-byte boundaries, not to 64 | 4096 byte ones. The latter caused incorrect offset calculations. 65 | 66 | -- Carlos Rafael Giani Tue, 30 Jul 2023 09:33:00 +0200 67 | 68 | libimxvpuapi (2.2.2) stable; urgency=low 69 | 70 | * imx6-coda: Skip incomplete frames instead of just reporting them 71 | * imx6-coda: Only flush bit buffer when framebuffers were registered 72 | This prevents misleading error-level log lines from occurring 73 | * imx6-coda: Prefer semi planar modes over fully planar ones 74 | * imx8m-hantro: Reset decoder write_offset to fill_level when moving 75 | read_offset 76 | * Add imx_vpu_api_is_color_format_tiled() function 77 | * update waf to 2.0.24 78 | * Add closed_gop_interval field to ImxVpuApiEncOpenParams 79 | This allows for enforcing regularly occurring IDR 80 | boundaries when encoding to h.264 81 | * imx8m-hantro: Fix supported decoder color formats and include tiled 82 | formats 83 | * Fix and improve h.264 max level estimation for encoding 84 | * imx6-coda: Use 2-row alignment in encoder 85 | * imx6-coda: Fix JPEG encoding quantization parameter handling in encoder 86 | 87 | -- Carlos Rafael Giani Tue, 28 Dec 2022 18:21:00 +0200 88 | 89 | libimxvpuapi (2.2.1) stable; urgency=low 90 | 91 | * Add option to not compile examples 92 | * imx8m-hantro: Allow for reusing decoder after drain mode finishes 93 | * imx6-coda: Reset drain mode after EOS processed 94 | * imx6-coda: Reset drain mode flags when flushing 95 | * Use libimxdmabuffer sync access functions to ensure cache coherence 96 | * Add imx_vpu_api_is_color_format_10bit() function 97 | * Documentation fixes 98 | 99 | -- Carlos Rafael Giani Tue, 10 May 2022 18:31:00 +0200 100 | 101 | libimxvpuapi (2.2.0) stable; urgency=low 102 | 103 | * Add IMX_VPU_API_DEC_OUTPUT_CODE_VIDEO_PARAMETERS_CHANGED output code 104 | * Deprecate drain mode, better document decoder drain mode 105 | * Various documentation fixes and new overviews about en- and 106 | decoding to help with the basic concepts 107 | * Add note about the current state of i.MX8m plus support 108 | * New functions: 109 | imx_vpu_api_is_color_format_rgb() 110 | imx_vpu_api_vp8_profile_number() 111 | imx_vpu_api_vp8_partition_count_number() 112 | imx_vpu_api_vp9_profile_number() 113 | imx_vpu_api_enc_set_frame_rate() 114 | * imx6-coda: Fix decoded frame fb_context 115 | This was causing crashes when callers relied on said fb_context 116 | 117 | -- Carlos Rafael Giani Mon, 14 Jun 2021 18:45:00 +0200 118 | 119 | libimxvpuapi (2.1.2) stable; urgency=low 120 | 121 | * imx6-coda: Fix skipped frame reporting 122 | * imx8m-hantro: Handle CODEC_ERROR_FRAME codec state 123 | * imx8m-hantro: Limit supported formats and profiles if building for m8xmm 124 | 125 | -- Carlos Rafael Giani Fri, 25 Apr 2021 12:20:00 +0200 126 | 127 | libimxvpuapi (2.1.1) stable; urgency=low 128 | 129 | * imx6 coda: fix bug in IPU based detiling related to frame width/height 130 | * imx6 coda: use the frame width/height from open params if available 131 | * imx6 coda: fix duplicate VPU firmware unloading 132 | * imx8 hantro encoder: fix mid-stream bitrate changes and bitrate logging 133 | 134 | -- Carlos Rafael Giani Fri, 23 Apr 2021 21:20:00 +0200 135 | 136 | libimxvpuapi (2.1.0) stable; urgency=low 137 | 138 | * introduce RGB and packed YUV formats since the Hantro encoder 139 | supports those 140 | * remove hardware specific public headers since they only added 141 | tiled pixel formats; instead, migrate these tiled formats into 142 | ImxVpuApiColorFormat 143 | * imx8 hantro decoder: reorder list of supported decoder color 144 | formats to favor 10 bit output 145 | * imx8 hantro decoder: clear new framebuffer fields to zero 146 | * imx8 hantro encoder: fix segfault caused by trying to unmap 147 | non existing staged raw frame 148 | 149 | -- Carlos Rafael Giani Sun, 10 Oct 2020 13:37:00 +0200 150 | 151 | libimxvpuapi (2.0.1) stable; urgency=low 152 | 153 | * imx6: replace mxcfb.h check with i.MX6 specific imx header check 154 | the mcxfb.h check only makes sense with i.MX6 devices, so requiring 155 | those for others like i.MX8 led to build errors 156 | * update waf to 2.0.12 and switch wscript to use Python 3 157 | * imx6: fix encoder pointer usage in vpu_EncGiveCommand() call 158 | 159 | -- Carlos Rafael Giani Sun, 19 Apr 2020 13:35:00 +0200 160 | 161 | libimxvpuapi (2.0.0) stable; urgency=low 162 | 163 | * complete rewrite to support i.MX6, i.MX8m, i.MX8mm VPUs 164 | 165 | -- Carlos Rafael Giani Sun, 21 Jul 2019 13:40:00 +0200 166 | 167 | libimxvpuapi (0.10.3) stable; urgency=low 168 | 169 | * properly pass on color format in simplified JPEG encoder interface 170 | * add alternative write-callback-style encoding mode 171 | also add encode example variant which uses write-callback style output 172 | * add support for "fake grayscale mode" in encoders 173 | this is done by using I420 internally and filling the U and V planes 174 | with 0x80 bytes 175 | * make sure JPEG quantization table is copied in standardized zig zag order 176 | the VPU does not, so this has to be done explicitely 177 | 178 | -- Carlos Rafael Giani Sun, 12 Oct 2016 09:31:00 +0200 179 | 180 | libimxvpuapi (0.10.2) stable; urgency=low 181 | 182 | * Fix AUD NAL positioning in h.264 encoder output 183 | (SPS/PPS/AUD were ordered incorrectly - AUD has to come first, not last) 184 | * Fix build error with examples when --enable-static is used 185 | * Pass quality factor in simplified JPEG encoder interface correctly 186 | * Add functions for querying and setting header data 187 | useful for modifying headers, like VUI data in the SPS RBSP 188 | * Documentation updates 189 | 190 | -- Carlos Rafael Giani Sun, 01 May 2016 13:10:22 +0100 191 | 192 | libimxvpuapi (0.10.1) stable; urgency=low 193 | 194 | * Update waf to version 1.8.16 195 | * Add workaround in wscript to prevent stale pkg-config .pc files 196 | * Fix memory leak by adding missing IOFreeVirtMem() call in vpulib backend 197 | 198 | -- Carlos Rafael Giani Thu, 30 Nov 2015 11:11:56 +0100 199 | 200 | libimxvpuapi (0.10.0) stable; urgency=low 201 | 202 | * Initial Release. 203 | 204 | -- Carlos Rafael Giani Thu, 05 Nov 2015 12:43:32 +0100 205 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libimxvpuapi 2 | Priority: extra 3 | Maintainer: Carlos Rafael Giani 4 | Build-Depends: 5 | debhelper (>= 8.0.0), 6 | python, 7 | libvpu-imx6-dev, 8 | Standards-Version: 3.9.3 9 | Section: libs 10 | Homepage: https://github.com/Freescale/libimxvpuapi 11 | Vcs-Browser: https://github.com/Freescale/libimxvpuapi 12 | 13 | Package: libimxvpuapi2-dev 14 | Section: libdevel 15 | Architecture: any 16 | Depends: libimxvpuapi2 (= ${binary:Version}), ${misc:Depends} 17 | Description: development files for libimxvpuapi2 18 | This package provides the development files required to build software against libimxvpuapi version 2. 19 | 20 | Package: libimxvpuapi2 21 | Section: libs 22 | Architecture: any 23 | Multi-Arch: same 24 | Depends: ${shlibs:Depends}, ${misc:Depends} 25 | Description: frontend for the i.MX6 / i.MX8 VPU hardware video engines 26 | This library provides an API for using hardware video codecs on i.MX platforms. The API abstracts away platform specific details and allows for using the same code with different hardware video codecs on different i.MX platforms. 27 | This is version 2 of the library, and can coexist on the same filesystem with version 1. 28 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: libimxvpuapi 3 | Source: https://github.com/Freescale/libimxvpuapi 4 | 5 | Files: * 6 | Copyright: 2014-2019 Carlos Rafael Giani 7 | License: LGPL-2.1+ 8 | 9 | Files: debian/* 10 | Copyright: 2015 Josua Mayer 11 | License: LGPL-2.1+ 12 | 13 | License: LGPL-2.1+ 14 | This library is free software; you can redistribute it and/or 15 | modify it under the terms of the GNU Lesser General Public 16 | License as published by the Free Software Foundation; either 17 | version 2.1 of the License, or (at your option) any later version. 18 | . 19 | This package is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | . 24 | You should have received a copy of the GNU Lesser General Public 25 | License along with this package; if not, write to the Free Software 26 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 27 | USA 28 | . 29 | On Debian systems, the complete text of the GNU Lesser General 30 | Public License can be found in "/usr/share/common-licenses/LGPL-2.1". 31 | -------------------------------------------------------------------------------- /debian/libimxvpuapi2-dev.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/*.so 2 | usr/lib/*/pkgconfig/*.pc 3 | usr/include/* 4 | -------------------------------------------------------------------------------- /debian/libimxvpuapi2.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/*.so.* 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | # install all files into temporary destdir 13 | DESTDIR=$(CURDIR)/debian/tmp 14 | 15 | %: 16 | dh $@ 17 | 18 | override_dh_auto_clean: 19 | rm -rf build-* 20 | rm -rf .waf* 21 | rm -rf .lock* 22 | 23 | override_dh_auto_configure: 24 | ./waf configure \ 25 | --prefix=/usr \ 26 | --libdir=/usr/lib/$(DEB_BUILD_MULTIARCH) 27 | 28 | override_dh_auto_install: 29 | ./waf install \ 30 | --destdir=$(DESTDIR) 31 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /example/decode-example.c: -------------------------------------------------------------------------------- 1 | /* example for how to use the imxvpuapi decoder interface 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "main.h" 28 | #include "h264_utils.h" 29 | #include "y4m_io.h" 30 | 31 | 32 | 33 | /* This is a simple example of how to decode with the imxvpuapi library. 34 | * It decodes h.264-encoded frames and writes out the decoded frames to 35 | * a file. 36 | * 37 | * The input must be h.264 byte-stream data. Depending on the encoder, 38 | * it may or may not support/expect access unit delimiters. */ 39 | 40 | 41 | #define FRAME_CONTEXT_START 0x1000 42 | #define FRAMEBUFFER_CONTEXT_START 0x2000 43 | 44 | 45 | struct _Context 46 | { 47 | /* Input/output file handles. */ 48 | FILE *h264_input_file; 49 | FILE *y4m_output_file; 50 | 51 | /* Y4M I/O for decoded raw frames. */ 52 | Y4MContext y4m_context; 53 | 54 | /* Helper structure to parse h.264 byte-stream data. */ 55 | h264_context h264_ctx; 56 | 57 | /* DMA buffer allocator for the decoder's framebuffer pool and 58 | * for output frames. */ 59 | ImxDmaBufferAllocator *allocator; 60 | 61 | /* The actual VPU decoder. */ 62 | ImxVpuApiDecoder *decoder; 63 | 64 | /* Stream buffer, used during decoding. */ 65 | ImxDmaBuffer *stream_buffer; 66 | 67 | /* Pointer to the decoder's global information. 68 | * Since this information is constant, it is not strictly 69 | * necessary to keep a pointer to it here. This is just done 70 | * for sake of convenience and clarity. */ 71 | ImxVpuApiDecGlobalInfo const *dec_global_info; 72 | 73 | /* Copy of information about the stream that is to be decoded. This 74 | * stream info becomes available when imx_vpu_api_dec_decode() output 75 | * code is IMX_VPU_API_DEC_OUTPUT_CODE_NEW_STREAM_INFO_AVAILABLE . */ 76 | ImxVpuApiDecStreamInfo stream_info; 77 | 78 | /* Framebuffer DMA buffers that are part of the decoder's 79 | * framebuffer pool. */ 80 | ImxDmaBuffer **fb_pool_dmabuffers; 81 | size_t num_fb_pool_framebuffers; 82 | 83 | /* Output DMA buffer. Only used if dec_global_info's 84 | * IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 85 | * flag is _not_ set. */ 86 | ImxDmaBuffer *output_dmabuffer; 87 | 88 | /* A counter used here to simulate a frame context. Corresponding 89 | * input and output frames have the same context. Not to be confused 90 | * with a framebuffer context (see allocate_and_add_fb_pool_framebuffers). */ 91 | unsigned int frame_context_counter; 92 | }; 93 | 94 | 95 | /* Allocates the output DMA buffer that is used if the 96 | * IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 97 | * flag in the decoder's ImxVpuApiDecGlobalInfo is _not_ set. This is 98 | * called once the stream info is known. */ 99 | static int allocate_output_framebuffer(Context *ctx) 100 | { 101 | int err; 102 | 103 | /* Discard any existing buffer first. */ 104 | if (ctx->output_dmabuffer != NULL) 105 | { 106 | imx_dma_buffer_deallocate(ctx->output_dmabuffer); 107 | ctx->output_dmabuffer = NULL; 108 | } 109 | 110 | /* Allocate a new DMA buffer. We use the stream info for _output_ DMA buffers. 111 | * This one can be different from the info for framebuffer pool DMA buffers. 112 | * For example, some decoders need some extra space at the end of the FB pool 113 | * DMA buffers for internal data, while output buffers do not ever need to 114 | * make room for that (they only ever contain actual frame data). */ 115 | ctx->output_dmabuffer = imx_dma_buffer_allocate( 116 | ctx->allocator, 117 | ctx->stream_info.min_output_framebuffer_size, 118 | ctx->stream_info.output_framebuffer_alignment, 119 | &err 120 | ); 121 | if (ctx->output_dmabuffer == NULL) 122 | { 123 | fprintf(stderr, "could not allocate DMA buffer for FB pool framebuffer: %s (%d)\n", strerror(err), err); 124 | return 0; 125 | } 126 | 127 | return 1; 128 | } 129 | 130 | 131 | /* Allocates the DMA buffers for the decoder's framebuffer pool. This is 132 | * called once the stream info is known, and is always called, even if 133 | * IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 134 | * is not set. We only _add_ framebuffers here, since framebuffers cannot 135 | * be removed from a decoder's pool once added (until the decoder is closed). */ 136 | static int allocate_and_add_fb_pool_framebuffers(Context *ctx, size_t num_fb_pool_framebuffers_to_add) 137 | { 138 | int ret = 1; 139 | size_t old_num_fb_pool_framebuffers; 140 | size_t new_num_fb_pool_framebuffers; 141 | ImxDmaBuffer **new_fb_pool_dmabuffers_array; 142 | void **fb_contexts = NULL; 143 | ImxVpuApiDecReturnCodes dec_ret; 144 | int err; 145 | size_t i; 146 | 147 | /* Nothing to add? Skip to the end. */ 148 | if (num_fb_pool_framebuffers_to_add == 0) 149 | goto finish; 150 | 151 | /* Calculate new number of framebuffer and keep track of the old one for 152 | * reallocation and for knowing where to add the new ones in the array. */ 153 | old_num_fb_pool_framebuffers = ctx->num_fb_pool_framebuffers; 154 | new_num_fb_pool_framebuffers = old_num_fb_pool_framebuffers + num_fb_pool_framebuffers_to_add; 155 | 156 | /* Reallocate to make space for more framebuffer DMA buffer pointers. */ 157 | new_fb_pool_dmabuffers_array = realloc(ctx->fb_pool_dmabuffers, new_num_fb_pool_framebuffers * sizeof(ImxDmaBuffer *)); 158 | assert(new_fb_pool_dmabuffers_array != NULL); 159 | /* Set the new array elements to an initial value of 0. */ 160 | memset(new_fb_pool_dmabuffers_array + old_num_fb_pool_framebuffers, 0, num_fb_pool_framebuffers_to_add * sizeof(ImxDmaBuffer *)); 161 | 162 | /* Store the new array. */ 163 | ctx->fb_pool_dmabuffers = new_fb_pool_dmabuffers_array; 164 | ctx->num_fb_pool_framebuffers = new_num_fb_pool_framebuffers; 165 | 166 | /* Allocate an array here to place the simulated FB context information 167 | * in. This also shows how FB context array parameters for the 168 | * imx_vpu_api_dec_add_framebuffers_to_pool() function does not continue 169 | * to exist after the function call. */ 170 | fb_contexts = malloc(num_fb_pool_framebuffers_to_add * sizeof(void *)); 171 | assert(fb_contexts != NULL); 172 | 173 | /* Allocate new framebuffers and fill the FB context array. */ 174 | for (i = old_num_fb_pool_framebuffers; i < ctx->num_fb_pool_framebuffers; ++i) 175 | { 176 | ctx->fb_pool_dmabuffers[i] = imx_dma_buffer_allocate( 177 | ctx->allocator, 178 | ctx->stream_info.min_fb_pool_framebuffer_size, 179 | ctx->stream_info.fb_pool_framebuffer_alignment, 180 | &err 181 | ); 182 | if (ctx->fb_pool_dmabuffers[i] == NULL) 183 | { 184 | fprintf(stderr, "could not allocate DMA buffer for FB pool framebuffer: %s (%d)\n", strerror(err), err); 185 | ret = 0; 186 | goto finish; 187 | } 188 | 189 | fb_contexts[i - old_num_fb_pool_framebuffers] = (void *)(FRAMEBUFFER_CONTEXT_START + i); 190 | } 191 | 192 | /* Add the newly allocated framebuffers to the decoder's pool. */ 193 | dec_ret = imx_vpu_api_dec_add_framebuffers_to_pool(ctx->decoder, ctx->fb_pool_dmabuffers + old_num_fb_pool_framebuffers, fb_contexts, num_fb_pool_framebuffers_to_add); 194 | if (dec_ret != IMX_VPU_API_DEC_RETURN_CODE_OK) 195 | { 196 | fprintf(stderr, "could not add framebuffers to VPU pool: %s\n", imx_vpu_api_dec_return_code_string(dec_ret)); 197 | ret = 0; 198 | goto finish; 199 | } 200 | 201 | 202 | finish: 203 | free(fb_contexts); 204 | return ret; 205 | } 206 | 207 | 208 | /* Deallocate all previously allocated framebuffers. Before calling this, 209 | * the decoder must have been closed, since otherwise, deallocating its 210 | * FB pool framebuffer DMA buffers could cause undefined behavior. 211 | * (It is also valid to call this when the decoder reports a new stream 212 | * info. See decode_encoded_frames() for details.) */ 213 | static void deallocate_framebuffers(Context *ctx) 214 | { 215 | size_t i; 216 | 217 | for (i = 0; i < ctx->num_fb_pool_framebuffers; ++i) 218 | { 219 | ImxDmaBuffer *dmabuffer = ctx->fb_pool_dmabuffers[i]; 220 | if (dmabuffer != NULL) 221 | imx_dma_buffer_deallocate(dmabuffer); 222 | } 223 | free(ctx->fb_pool_dmabuffers); 224 | 225 | ctx->num_fb_pool_framebuffers = 0; 226 | ctx->fb_pool_dmabuffers = NULL; 227 | } 228 | 229 | 230 | /* Push h.264 input frame into the decoder. */ 231 | static Retval push_encoded_input_frame(Context *ctx) 232 | { 233 | int ok; 234 | ImxVpuApiEncodedFrame encoded_frame; 235 | ImxVpuApiDecReturnCodes vpudec_ret; 236 | 237 | ok = h264_ctx_read_access_unit(&(ctx->h264_ctx)); 238 | 239 | if (ctx->h264_ctx.au_end_offset <= ctx->h264_ctx.au_start_offset) 240 | return RETVAL_EOS; 241 | 242 | encoded_frame.data = ctx->h264_ctx.in_buffer + ctx->h264_ctx.au_start_offset; 243 | encoded_frame.data_size = ctx->h264_ctx.au_end_offset - ctx->h264_ctx.au_start_offset; 244 | encoded_frame.context = (void*)((uintptr_t)(ctx->frame_context_counter)); 245 | 246 | ctx->frame_context_counter++; 247 | 248 | fprintf(stderr, "pushing encoded frame with context %p and %zu byte into encoder\n", encoded_frame.context, encoded_frame.data_size); 249 | 250 | vpudec_ret = imx_vpu_api_dec_push_encoded_frame(ctx->decoder, &encoded_frame); 251 | if (vpudec_ret != IMX_VPU_API_DEC_RETURN_CODE_OK) 252 | { 253 | fprintf(stderr, "imx_vpu_api_dec_push_encoded_frame() failed: %s\n", imx_vpu_api_dec_return_code_string(vpudec_ret)); 254 | return RETVAL_ERROR; 255 | } 256 | 257 | return ok ? RETVAL_OK : RETVAL_EOS; 258 | } 259 | 260 | 261 | /* Decode encoded frames that were previously pushed into the decoder 262 | * in the push_encoded_input_frame() function. The core of this function 263 | * is the imx_vpu_api_dec_decode() call, which is repeatedly called 264 | * until another encoded input frame needs to be pushed into the encoder, 265 | * EOS is reported, an error occurs, or the decoder is flushed by the 266 | * imx_vpu_api_dec_flush() call (which we do not use in this example). */ 267 | static Retval decode_encoded_frames(Context *ctx) 268 | { 269 | ImxVpuApiDecOutputCodes output_code; 270 | ImxVpuApiDecReturnCodes dec_ret; 271 | Retval retval = RETVAL_OK; 272 | int do_loop = 1; 273 | 274 | /* output_dmabuffer is allocated in allocate_output_framebuffer(), 275 | * which is only called if the ImxVpuApiDecGlobalInfo flag 276 | * IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 277 | * is set. */ 278 | if (ctx->output_dmabuffer != NULL) 279 | imx_vpu_api_dec_set_output_frame_dma_buffer(ctx->decoder, ctx->output_dmabuffer, (void *)FRAMEBUFFER_CONTEXT_START); 280 | 281 | do 282 | { 283 | /* Perform a decoding step. */ 284 | if ((dec_ret = imx_vpu_api_dec_decode(ctx->decoder, &output_code)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 285 | { 286 | fprintf(stderr, "imx_vpu_dec_decode() failed: %s\n", imx_vpu_api_dec_return_code_string(dec_ret)); 287 | return RETVAL_ERROR; 288 | } 289 | 290 | switch (output_code) 291 | { 292 | case IMX_VPU_API_DEC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: 293 | /* Decoder did not produce a decoded frame yet, and has nothing 294 | * else to report. Continue calling imx_vpu_api_dec_decode(). */ 295 | break; 296 | 297 | case IMX_VPU_API_DEC_OUTPUT_CODE_EOS: 298 | /* Decoder reached the end-of-stream. No more frames can be decoded. 299 | * At this point, all that can be done is to close the decoder, 300 | * so exit the loop. */ 301 | fprintf(stderr, "VPU reports EOS; no more decoded frames available\n"); 302 | retval = RETVAL_EOS; 303 | do_loop = 0; 304 | break; 305 | 306 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEW_STREAM_INFO_AVAILABLE: 307 | { 308 | /* There is new stream information. This indicates that the decoder 309 | * found a new stream. With most formats, this happens only once, 310 | * at the beginning of the stream. Some formats like h.264 may change 311 | * parameters like the video resolution mid-stream. In these cases, 312 | * we may enter this location again to process the new stream info. 313 | * 314 | * In here, we allocate DMA buffers for the FB pool. If any DMA 315 | * buffers were previously allocated, we deallocate them. This is 316 | * because as soon as there is a new stream info, any previous FB 317 | * pool will have been torn down by the decoder, and its associated 318 | * DMA buffers will no longer be in use. */ 319 | 320 | ImxVpuApiFramebufferMetrics const *fb_metrics; 321 | ImxVpuApiDecStreamInfo const *stream_info = imx_vpu_api_dec_get_stream_info(ctx->decoder); 322 | 323 | deallocate_framebuffers(ctx); 324 | 325 | ctx->stream_info = *stream_info; 326 | 327 | if (!allocate_and_add_fb_pool_framebuffers(ctx, stream_info->min_num_required_framebuffers)) 328 | { 329 | fprintf(stderr, "Could not allocate %zu framebuffer(s)\n", stream_info->min_num_required_framebuffers); 330 | retval = RETVAL_ERROR; 331 | do_loop = 0; 332 | break; 333 | } 334 | 335 | /* If the IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 336 | * flag is not set, then the decoder places decoded frames into an external 337 | * DMA buffer that needs to be allocated and set as the output buffer. */ 338 | if (!(ctx->dec_global_info->flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL)) 339 | { 340 | if (!allocate_output_framebuffer(ctx)) 341 | { 342 | fprintf(stderr, "Could not allocate output framebuffer\n"); 343 | retval = RETVAL_ERROR; 344 | do_loop = 0; 345 | break; 346 | } 347 | 348 | /* Set the newly allocated DMA buffer as the output buffer right here, 349 | * before the next imx_vpu_api_dec_decode() call. This is necessary, 350 | * otherwise the decoder has nowhere to place the decoded frame in. */ 351 | imx_vpu_api_dec_set_output_frame_dma_buffer(ctx->decoder, ctx->output_dmabuffer, (void *)FRAMEBUFFER_CONTEXT_START); 352 | } 353 | 354 | /* Set up Y4M writer to write the decoded frames to the output file. */ 355 | fb_metrics = &(stream_info->decoded_frame_framebuffer_metrics); 356 | ctx->y4m_context.width = fb_metrics->actual_frame_width; 357 | ctx->y4m_context.height = fb_metrics->actual_frame_height; 358 | ctx->y4m_context.y_stride = fb_metrics->y_stride; 359 | ctx->y4m_context.uv_stride = fb_metrics->uv_stride; 360 | ctx->y4m_context.interlacing = IMX_VPU_API_INTERLACING_MODE_NO_INTERLACING; 361 | ctx->y4m_context.color_format = stream_info->color_format; 362 | if (!y4m_init(ctx->y4m_output_file, &(ctx->y4m_context), 0)) 363 | { 364 | retval = RETVAL_ERROR; 365 | do_loop = 0; 366 | break; 367 | } 368 | 369 | break; 370 | } 371 | 372 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: 373 | /* Decoder needs one more framebuffer added to its pool. 374 | * Add one, otherwise decoding cannot continue. Then continue 375 | * calling imx_vpu_api_dec_decode(). */ 376 | 377 | if (!allocate_and_add_fb_pool_framebuffers(ctx, 1)) 378 | { 379 | retval = RETVAL_ERROR; 380 | do_loop = 0; 381 | } 382 | 383 | break; 384 | 385 | case IMX_VPU_API_DEC_OUTPUT_CODE_DECODED_FRAME_AVAILABLE: 386 | { 387 | /* Decoder produced a decoded frame. Get it, and write its 388 | * pixels to the output Y4M file. */ 389 | 390 | int err; 391 | uint8_t *virtual_address; 392 | ImxVpuApiRawFrame decoded_frame; 393 | ImxVpuApiFramebufferMetrics const *fb_metrics = &(ctx->stream_info.decoded_frame_framebuffer_metrics); 394 | 395 | /* Get the decoded frame. This call is mandatory; the decoder 396 | * cannot continue until the decoded frame is retrieved. */ 397 | if ((dec_ret = imx_vpu_api_dec_get_decoded_frame(ctx->decoder, &decoded_frame)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 398 | { 399 | fprintf(stderr, "imx_vpu_api_dec_get_decoded_frame() failed: %s\n", imx_vpu_api_dec_return_code_string(dec_ret)); 400 | return RETVAL_ERROR; 401 | } 402 | 403 | virtual_address = imx_dma_buffer_map(decoded_frame.fb_dma_buffer, IMX_DMA_BUFFER_MAPPING_FLAG_READ, &err); 404 | assert(virtual_address != NULL); 405 | 406 | fprintf(stderr, "got decoded frame\n"); 407 | 408 | y4m_write_frame( 409 | &(ctx->y4m_context), 410 | virtual_address + fb_metrics->y_offset, 411 | virtual_address + fb_metrics->u_offset, 412 | virtual_address + fb_metrics->v_offset 413 | ); 414 | 415 | imx_dma_buffer_unmap(decoded_frame.fb_dma_buffer); 416 | 417 | /* Return the decoded frame. After this call, the DMA buffer 418 | * must not be touched, unless sometime later it is returned 419 | * by the decoder in an ImxVpuApiRawFrame struct again. 420 | * If however the ImxVpuApiDecGlobalInfo flag 421 | * IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL 422 | * is not set, then calling this is not strictly necessary. 423 | * It is not necessary however to check for that flag before 424 | * calling this, since this function does nothing if the 425 | * flag isn't set, so it is safe to always call it. */ 426 | imx_vpu_api_dec_return_framebuffer_to_decoder(ctx->decoder, decoded_frame.fb_dma_buffer); 427 | 428 | break; 429 | } 430 | 431 | case IMX_VPU_API_DEC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: 432 | /* Decoding cannot continue, since the decoder ran out of 433 | * encoded frames to decode. Exit the loop so that in the 434 | * run() function, push_encoded_input_frame() can be called 435 | * again to push a new encoded frame into the decoder. Then 436 | * decoding can continue. */ 437 | do_loop = 0; 438 | break; 439 | 440 | case IMX_VPU_API_DEC_OUTPUT_CODE_FRAME_SKIPPED: 441 | { 442 | /* Frame was skipped by the decoder. Log this and continue 443 | * the decoder loop. */ 444 | ImxVpuApiDecSkippedFrameReasons reason; 445 | void *context; 446 | uint64_t pts, dts; 447 | imx_vpu_api_dec_get_skipped_frame_info(ctx->decoder, &reason, &context, &pts, &dts); 448 | fprintf(stderr, "frame got skipped: reason %s context %p PTS %" PRIu64 " DTS %" PRIu64 "\n", imx_vpu_api_dec_skipped_frame_reason_string(reason), context, pts, dts); 449 | break; 450 | } 451 | 452 | default: 453 | fprintf(stderr, "UNKNOWN OUTPUT CODE %s (%d)\n", imx_vpu_api_dec_output_code_string(output_code), (int)output_code); 454 | assert(0); 455 | } 456 | } 457 | while (do_loop); 458 | 459 | return retval; 460 | } 461 | 462 | 463 | Context* init(FILE *input_file, FILE *output_file) 464 | { 465 | size_t i; 466 | int err; 467 | Context *ctx; 468 | ImxVpuApiDecOpenParams open_params; 469 | ImxVpuApiDecReturnCodes dec_ret; 470 | uint32_t dec_flags; 471 | 472 | ctx = calloc(1, sizeof(Context)); 473 | ctx->h264_input_file = input_file; 474 | ctx->y4m_output_file = output_file; 475 | ctx->frame_context_counter = FRAME_CONTEXT_START; 476 | 477 | /* Retrieve global, static, invariant information about the decoder. */ 478 | ctx->dec_global_info = imx_vpu_api_dec_get_global_info(); 479 | assert(ctx->dec_global_info != NULL); 480 | 481 | dec_flags = ctx->dec_global_info->flags; 482 | 483 | /* Check that the codec actually supports decoding. */ 484 | if (!(dec_flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_HAS_DECODER)) 485 | { 486 | fprintf(stderr, "HW codec does not support decoding!\n"); 487 | goto error; 488 | } 489 | 490 | /* Print global decoder information. */ 491 | fprintf(stderr, "global decoder information:\n"); 492 | fprintf(stderr, "semi planar frames supported: %d\n", !!(dec_flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_SEMI_PLANAR_FRAMES_SUPPORTED)); 493 | fprintf(stderr, "fully planar frames supported: %d\n", !!(dec_flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_FULLY_PLANAR_FRAMES_SUPPORTED)); 494 | fprintf(stderr, "decoded frames are from buffer pool: %d\n", !!(dec_flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL)); 495 | fprintf(stderr, "min required stream buffer size: %zu\n", ctx->dec_global_info->min_required_stream_buffer_size); 496 | fprintf(stderr, "required stream buffer physaddr alignment: %zu\n", ctx->dec_global_info->required_stream_buffer_physaddr_alignment); 497 | fprintf(stderr, "required stream buffer size alignment: %zu\n", ctx->dec_global_info->required_stream_buffer_size_alignment); 498 | fprintf(stderr, "num supported compression formats: %zu\n", ctx->dec_global_info->num_supported_compression_formats); 499 | for (i = 0; i < ctx->dec_global_info->num_supported_compression_formats; ++i) 500 | fprintf(stderr, " %s\n", imx_vpu_api_compression_format_string(ctx->dec_global_info->supported_compression_formats[i])); 501 | 502 | /* Set up the h264context helper to parse h.264 byte-stream data. */ 503 | h264_ctx_init(&(ctx->h264_ctx), ctx->h264_input_file); 504 | 505 | /* Set up the DMA buffer allocator. We use this to allocate framebuffers 506 | * and the stream buffer for the decoder. */ 507 | ctx->allocator = imx_dma_buffer_allocator_new(&err); 508 | if (ctx->allocator == NULL) 509 | { 510 | fprintf(stderr, "could not create DMA buffer allocator: %s (%d)\n", strerror(err), err); 511 | goto error; 512 | } 513 | 514 | /* Set the open params. Enable frame reordering, use h.264 as the codec format. 515 | * The memset() call ensures the other values are set to their default. 516 | * (We do not need to set frame_width/frame_height for h.264, since these values 517 | * are contained within the h.264 SPS NALUs. Also, h.264 byte-stream data does 518 | * not require extra header data.) Also, enable semi-planar frames if supported. */ 519 | memset(&(open_params), 0, sizeof(open_params)); 520 | open_params.compression_format = IMX_VPU_API_COMPRESSION_FORMAT_H264; 521 | open_params.flags = 522 | IMX_VPU_API_DEC_OPEN_PARAMS_FLAG_ENABLE_FRAME_REORDERING 523 | | IMX_VPU_API_DEC_OPEN_PARAMS_FLAG_USE_SEMI_PLANAR_COLOR_FORMAT 524 | ; 525 | 526 | /* Allocate the stream buffer that is used throughout the decoding process. */ 527 | ctx->stream_buffer = imx_dma_buffer_allocate( 528 | ctx->allocator, 529 | ctx->dec_global_info->min_required_stream_buffer_size, 530 | ctx->dec_global_info->required_stream_buffer_physaddr_alignment, 531 | 0 532 | ); 533 | assert(ctx->stream_buffer != NULL); 534 | 535 | /* Open a decoder instance, using the previously allocated bitstream buffer */ 536 | if ((dec_ret = imx_vpu_api_dec_open(&(ctx->decoder), &open_params, ctx->stream_buffer)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 537 | { 538 | fprintf(stderr, "could not open decoder instance: %s\n", imx_vpu_api_dec_return_code_string(dec_ret)); 539 | goto error; 540 | } 541 | 542 | 543 | finish: 544 | return ctx; 545 | 546 | error: 547 | shutdown(ctx); 548 | ctx = NULL; 549 | goto finish; 550 | } 551 | 552 | 553 | Retval run(Context *ctx) 554 | { 555 | /* Feed frames to decoder & decode & output, until we run out of input data. */ 556 | for (;;) 557 | { 558 | Retval ret; 559 | 560 | /* Push encoded input frame into the decoder. */ 561 | ret = push_encoded_input_frame(ctx); 562 | if (ret == RETVAL_EOS) 563 | break; 564 | else if (ret != RETVAL_OK) 565 | return RETVAL_ERROR; 566 | 567 | /* Decode the previously pushed input data. */ 568 | ret = decode_encoded_frames(ctx); 569 | if (ret == RETVAL_EOS) 570 | break; 571 | else if (ret == RETVAL_ERROR) 572 | return RETVAL_ERROR; 573 | } 574 | 575 | /* Enable drain mode. In this mode, any decoded frames that are still in the 576 | * decoder are output. 577 | * No input data is given, since there isn't any input data anymore. */ 578 | fprintf(stderr, "draining decoder\n"); 579 | imx_vpu_api_dec_enable_drain_mode(ctx->decoder); 580 | 581 | /* Now drain the remaining not-yet-encoded frames from the decoder. */ 582 | for (;;) 583 | { 584 | Retval ret = decode_encoded_frames(ctx); 585 | if (ret == RETVAL_EOS) 586 | break; 587 | else if (ret == RETVAL_ERROR) 588 | return RETVAL_ERROR; 589 | } 590 | 591 | return RETVAL_OK; 592 | } 593 | 594 | 595 | void shutdown(Context *ctx) 596 | { 597 | if (ctx == NULL) 598 | return; 599 | 600 | /* Close the previously opened decoder instance. */ 601 | if (ctx->decoder != NULL) 602 | { 603 | imx_vpu_api_dec_close(ctx->decoder); 604 | ctx->decoder = NULL; 605 | } 606 | 607 | /* Free all allocated memory. */ 608 | deallocate_framebuffers(ctx); 609 | if (ctx->output_dmabuffer) 610 | { 611 | imx_dma_buffer_deallocate(ctx->output_dmabuffer); 612 | ctx->output_dmabuffer = NULL; 613 | } 614 | if (ctx->stream_buffer != NULL) 615 | { 616 | imx_dma_buffer_deallocate(ctx->stream_buffer); 617 | ctx->stream_buffer = NULL; 618 | } 619 | 620 | /* Discard the DMA buffer allocator. */ 621 | if (ctx->allocator != NULL) 622 | { 623 | imx_dma_buffer_allocator_destroy(ctx->allocator); 624 | ctx->allocator = NULL; 625 | } 626 | 627 | /* Discard the h264context helper that was used to parse h.264 cata. */ 628 | h264_ctx_cleanup(&(ctx->h264_ctx)); 629 | 630 | free(ctx); 631 | } 632 | -------------------------------------------------------------------------------- /example/encode-example.c: -------------------------------------------------------------------------------- 1 | /* example for how to use the imxvpuapi encoder interface 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "main.h" 28 | #include "y4m_io.h" 29 | 30 | 31 | /* This is a simple example of how to encode with the imxvpuapi library. 32 | * It reads raw frames from Y4M data, encodes it to h.264, and dumps the 33 | * encoded frames to a file. */ 34 | 35 | 36 | #define FRAME_CONTEXT_START 0x1000 37 | 38 | 39 | struct _Context 40 | { 41 | /* Input/output file handles. */ 42 | FILE *y4m_input_file; 43 | FILE *h264_output_file; 44 | 45 | /* Y4M I/O for raw input frames. */ 46 | Y4MContext y4m_context; 47 | 48 | /* DMA buffer allocator for the encoder's framebuffer pool and 49 | * for output frames. */ 50 | ImxDmaBufferAllocator *allocator; 51 | 52 | /* The actual VPU encoder. */ 53 | ImxVpuApiEncoder *encoder; 54 | 55 | /* Stream buffer, used during encoding. */ 56 | ImxDmaBuffer *stream_buffer; 57 | 58 | /* Pointer to the encoder's global information. 59 | * Since this information is constant, it is not strictly 60 | * necessary to keep a pointer to it here. This is just done 61 | * for sake of convenience and clarity. */ 62 | ImxVpuApiEncGlobalInfo const *enc_global_info; 63 | 64 | /* Copy of information about the stream that is to be encoded. This 65 | * stream info becomes available right after opening the encoder 66 | * instance with imx_vpu_api_enc_open(). The stream info is then 67 | * available by calling imx_vpu_api_enc_get_stream_info(). */ 68 | ImxVpuApiEncStreamInfo stream_info; 69 | 70 | /* Framebuffer DMA buffers that are part of the encoder's 71 | * framebuffer pool. These are used only internally by the encoder, 72 | * and not for delivering encoded output frames. Also, not all 73 | * encoders use a framebuffer pool. */ 74 | ImxDmaBuffer **fb_pool_dmabuffers; 75 | size_t num_fb_pool_framebuffers; 76 | 77 | /* DMA buffer for raw input frames to encode. */ 78 | ImxDmaBuffer *input_dmabuffer; 79 | 80 | /* Buffer for containing encoded frames. Unlike other buffers, 81 | * this is not a DMA buffer, it is regular system memory. */ 82 | void *encoded_frame_buffer; 83 | size_t encoded_frame_buffer_size; 84 | 85 | /* A counter used here to simulate a frame context. Corresponding 86 | * input and output frames have the same context. */ 87 | unsigned int frame_context_counter; 88 | }; 89 | 90 | 91 | /* Allocates the DMA buffers for the encoders's framebuffer pool. This is 92 | * called once the stream info is known, and is always called. We only 93 | * _add_ framebuffers here, since framebuffers cannot be removed from an 94 | * encoder's pool once added (until the encoder is closed). */ 95 | static int allocate_and_add_fb_pool_framebuffers(Context *ctx, size_t num_fb_pool_framebuffers_to_add) 96 | { 97 | int ret = 1; 98 | size_t old_num_fb_pool_framebuffers; 99 | size_t new_num_fb_pool_framebuffers; 100 | ImxDmaBuffer **new_fb_pool_dmabuffers_array; 101 | ImxVpuApiEncReturnCodes enc_ret; 102 | int err; 103 | size_t i; 104 | 105 | /* Nothing to add? Skip to the end. */ 106 | if (num_fb_pool_framebuffers_to_add == 0) 107 | goto finish; 108 | 109 | /* Calculate new number of framebuffer and keep track of the old one for 110 | * reallocation and for knowing where to add the new ones in the array. */ 111 | old_num_fb_pool_framebuffers = ctx->num_fb_pool_framebuffers; 112 | new_num_fb_pool_framebuffers = old_num_fb_pool_framebuffers + num_fb_pool_framebuffers_to_add; 113 | 114 | /* Reallocate to make space for more framebuffer DMA buffer pointers. */ 115 | new_fb_pool_dmabuffers_array = realloc(ctx->fb_pool_dmabuffers, new_num_fb_pool_framebuffers * sizeof(ImxDmaBuffer *)); 116 | assert(new_fb_pool_dmabuffers_array != NULL); 117 | /* Set the new array elements to an initial value of 0. */ 118 | memset(new_fb_pool_dmabuffers_array + old_num_fb_pool_framebuffers, 0, num_fb_pool_framebuffers_to_add * sizeof(ImxDmaBuffer *)); 119 | 120 | /* Store the new array. */ 121 | ctx->fb_pool_dmabuffers = new_fb_pool_dmabuffers_array; 122 | ctx->num_fb_pool_framebuffers = new_num_fb_pool_framebuffers; 123 | 124 | /* Allocate new framebuffers and fill the FB context array. */ 125 | for (i = old_num_fb_pool_framebuffers; i < ctx->num_fb_pool_framebuffers; ++i) 126 | { 127 | ctx->fb_pool_dmabuffers[i] = imx_dma_buffer_allocate( 128 | ctx->allocator, 129 | ctx->stream_info.min_framebuffer_size, 130 | ctx->stream_info.framebuffer_alignment, 131 | &err 132 | ); 133 | if (ctx->fb_pool_dmabuffers[i] == NULL) 134 | { 135 | fprintf(stderr, "could not allocate DMA buffer for FB pool framebuffer: %s (%d)\n", strerror(err), err); 136 | ret = 0; 137 | goto finish; 138 | } 139 | } 140 | 141 | /* Add the newly allocated framebuffers to the encoder's pool. */ 142 | enc_ret = imx_vpu_api_enc_add_framebuffers_to_pool(ctx->encoder, ctx->fb_pool_dmabuffers + old_num_fb_pool_framebuffers, num_fb_pool_framebuffers_to_add); 143 | if (enc_ret != IMX_VPU_API_ENC_RETURN_CODE_OK) 144 | { 145 | fprintf(stderr, "could not add framebuffers to VPU pool: %s\n", imx_vpu_api_enc_return_code_string(enc_ret)); 146 | ret = 0; 147 | goto finish; 148 | } 149 | 150 | 151 | finish: 152 | return ret; 153 | } 154 | 155 | 156 | /* Deallocate all previously allocated framebuffers. Before calling this, 157 | * the encoder must have been closed, since otherwise, deallocating its 158 | * FB pool framebuffer DMA buffers could cause undefined behavior. */ 159 | static void deallocate_framebuffers(Context *ctx) 160 | { 161 | size_t i; 162 | 163 | for (i = 0; i < ctx->num_fb_pool_framebuffers; ++i) 164 | { 165 | ImxDmaBuffer *dmabuffer = ctx->fb_pool_dmabuffers[i]; 166 | if (dmabuffer != NULL) 167 | imx_dma_buffer_deallocate(dmabuffer); 168 | } 169 | free(ctx->fb_pool_dmabuffers); 170 | 171 | ctx->num_fb_pool_framebuffers = 0; 172 | ctx->fb_pool_dmabuffers = NULL; 173 | } 174 | 175 | 176 | /* Resize the system memory buffer that will hold the encoded data. 177 | * This is called after imx_vpu_api_enc_encode() returns the output 178 | * code IMX_VPU_API_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE, but 179 | * before imx_vpu_api_enc_get_encoded_frame() is called, to make sure 180 | * there is enough space for the encoded data. */ 181 | static void resize_encoded_frame_buffer(Context *ctx, size_t new_size) 182 | { 183 | void *new_buffer; 184 | 185 | if (ctx->encoded_frame_buffer_size >= new_size) 186 | return; 187 | 188 | new_buffer = realloc(ctx->encoded_frame_buffer, new_size); 189 | assert(new_buffer != NULL); 190 | 191 | ctx->encoded_frame_buffer = new_buffer; 192 | ctx->encoded_frame_buffer_size = new_size; 193 | } 194 | 195 | 196 | /* Push a raw input frame into the encoder. In this example, we have to 197 | * copy frame pixels into a DMA buffer (input_dmabuffer). CPU-based 198 | * copies like this one are not strictly necessary; if raw frames are 199 | * already stored in DMA buffers, they can be used directly, facilitating 200 | * a "zero-copy"-esque approach that avoids unnecessary CPU usage (the 201 | * encoder can pull the frame pixels through DMA channels). */ 202 | static Retval push_raw_input_frame(Context *ctx) 203 | { 204 | ImxVpuApiRawFrame raw_input_frame; 205 | uint8_t *mapped_virtual_address; 206 | int y4m_ret; 207 | ImxVpuApiFramebufferMetrics const *fb_metrics; 208 | ImxVpuApiEncReturnCodes enc_ret; 209 | 210 | fb_metrics = &(ctx->stream_info.frame_encoding_framebuffer_metrics); 211 | 212 | /* Set up the input frame. The only field that needs to be 213 | * set is the input framebuffer. The encoder will read from it. 214 | * The rest can remain zero/NULL. */ 215 | memset(&raw_input_frame, 0, sizeof(raw_input_frame)); 216 | raw_input_frame.fb_dma_buffer = ctx->input_dmabuffer; 217 | raw_input_frame.context = (void*)((uintptr_t)(ctx->frame_context_counter)); 218 | 219 | ctx->frame_context_counter++; 220 | 221 | fprintf(stderr, "pushing raw frame with context %p into encoder\n", raw_input_frame.context); 222 | 223 | /* Read uncompressed pixels into the input DMA buffer */ 224 | mapped_virtual_address = imx_dma_buffer_map(ctx->input_dmabuffer, IMX_DMA_BUFFER_MAPPING_FLAG_WRITE, NULL); 225 | y4m_ret = y4m_read_frame( 226 | &(ctx->y4m_context), 227 | mapped_virtual_address + fb_metrics->y_offset, 228 | mapped_virtual_address + fb_metrics->u_offset, 229 | mapped_virtual_address + fb_metrics->v_offset 230 | ); 231 | imx_dma_buffer_unmap(ctx->input_dmabuffer); 232 | if (!y4m_ret) 233 | return RETVAL_EOS; 234 | 235 | if ((enc_ret = imx_vpu_api_enc_push_raw_frame(ctx->encoder, &raw_input_frame)) != IMX_VPU_API_ENC_RETURN_CODE_OK) 236 | { 237 | fprintf(stderr, "could not push raw frame into encoder: %s\n", imx_vpu_api_enc_return_code_string(enc_ret)); 238 | return RETVAL_ERROR; 239 | } 240 | 241 | return RETVAL_OK; 242 | } 243 | 244 | 245 | /* Encode the previously inserted raw frame. The core of this function 246 | * is the imx_vpu_api_enc_encode() call, which is repeatedly called until 247 | * another raw input frame needs to be pushed into the encoder, EOS is 248 | * reported, an error occurs, or the encoder is flushed by the 249 | * imx_vpu_api_enc_flush() call (which we do not use in this example). */ 250 | static Retval encode_raw_frame(Context *ctx) 251 | { 252 | ImxVpuApiEncOutputCodes output_code; 253 | size_t encoded_frame_size; 254 | ImxVpuApiEncReturnCodes enc_ret; 255 | Retval retval = RETVAL_OK; 256 | int do_loop = 1; 257 | 258 | do 259 | { 260 | /* Perform an encoding step. */ 261 | if ((enc_ret = imx_vpu_api_enc_encode(ctx->encoder, &encoded_frame_size, &output_code)) != IMX_VPU_API_ENC_RETURN_CODE_OK) 262 | { 263 | fprintf(stderr, "imx_vpu_api_enc_encode failed(): %s\n", imx_vpu_api_enc_return_code_string(enc_ret)); 264 | return RETVAL_ERROR; 265 | } 266 | 267 | switch (output_code) 268 | { 269 | case IMX_VPU_API_ENC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: 270 | /* Encoder did not produce an encoded frame yet, and has nothing 271 | * else to report. Continue calling imx_vpu_api_enc_encode(). */ 272 | break; 273 | 274 | case IMX_VPU_API_ENC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: 275 | /* Encoder needs one more framebuffer added to its pool. 276 | * Add one, otherwise encoding cannot continue. Then continue 277 | * calling imx_vpu_api_enc_encode(). */ 278 | 279 | if (!allocate_and_add_fb_pool_framebuffers(ctx, 1)) 280 | { 281 | retval = RETVAL_ERROR; 282 | do_loop = 0; 283 | } 284 | 285 | break; 286 | 287 | case IMX_VPU_API_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE: 288 | { 289 | /* Encoder produced an encoded frame. First, make sure that 290 | * we have a buffer big enough for that data. Then, retrieve 291 | * the encoded frame data, and output it. We have to retrieve 292 | * the encoded data, otherwise encoding cannot continue. */ 293 | 294 | ImxVpuApiEncodedFrame output_frame; 295 | 296 | resize_encoded_frame_buffer(ctx, encoded_frame_size); 297 | 298 | memset(&output_frame, 0, sizeof(output_frame)); 299 | output_frame.data = ctx->encoded_frame_buffer; 300 | output_frame.data_size = encoded_frame_size; 301 | 302 | if ((enc_ret = imx_vpu_api_enc_get_encoded_frame(ctx->encoder, &output_frame)) != IMX_VPU_API_ENC_RETURN_CODE_OK) 303 | { 304 | fprintf(stderr, "could not retrieve encoded frame: %s\n", imx_vpu_api_enc_return_code_string(enc_ret)); 305 | return RETVAL_ERROR; 306 | } 307 | 308 | fprintf(stderr, "got encoded frame with %zu byte and context %p from encoder\n", output_frame.data_size, output_frame.context); 309 | 310 | fwrite(output_frame.data, 1, output_frame.data_size, ctx->h264_output_file); 311 | 312 | break; 313 | } 314 | 315 | case IMX_VPU_API_ENC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: 316 | /* Encoding cannot continue, since the encoded ran out of 317 | * raw frames to encode. Exit the loop so that in the 318 | * run() function, push_raw_input_frame() can be called 319 | * again to push a new raw frame into the encoder. Then 320 | * encoding can continue. */ 321 | do_loop = 0; 322 | break; 323 | 324 | case IMX_VPU_API_ENC_OUTPUT_CODE_EOS: 325 | /* Encoder reached the end-of-stream. No more frames can be encoded. 326 | * At this point, all that can be done is to close the decoder, 327 | * so exit the loop. */ 328 | fprintf(stderr, "VPU reports EOS; no more encoded frames available\n"); 329 | retval = RETVAL_EOS; 330 | do_loop = 0; 331 | break; 332 | 333 | default: 334 | fprintf(stderr, "UNKNOWN OUTPUT CODE %s (%d)\n", imx_vpu_api_enc_output_code_string(output_code), (int)output_code); 335 | assert(0); 336 | } 337 | 338 | } 339 | while (do_loop); 340 | 341 | return retval; 342 | } 343 | 344 | 345 | Context* init(FILE *input_file, FILE *output_file) 346 | { 347 | size_t i; 348 | int err; 349 | Context *ctx; 350 | ImxVpuApiEncOpenParams open_params; 351 | ImxVpuApiEncReturnCodes enc_ret; 352 | ImxVpuApiEncStreamInfo const *enc_stream_info; 353 | uint32_t enc_flags; 354 | 355 | ctx = calloc(1, sizeof(Context)); 356 | ctx->y4m_input_file = input_file; 357 | ctx->h264_output_file = output_file; 358 | ctx->frame_context_counter = FRAME_CONTEXT_START; 359 | 360 | /* Retrieve global, static, invariant information about the encoder. */ 361 | ctx->enc_global_info = imx_vpu_api_enc_get_global_info(); 362 | assert(ctx->enc_global_info != NULL); 363 | 364 | enc_flags = ctx->enc_global_info->flags; 365 | 366 | /* Check that the codec actually supports encoding. */ 367 | if (!(enc_flags & IMX_VPU_API_ENC_GLOBAL_INFO_FLAG_HAS_ENCODER)) 368 | { 369 | fprintf(stderr, "HW codec does not support encoding!\n"); 370 | goto error; 371 | } 372 | 373 | /* Print global encoder information. */ 374 | fprintf(stderr, "global encoder information:\n"); 375 | fprintf(stderr, "semi planar frames supported: %d\n", !!(enc_flags & IMX_VPU_API_ENC_GLOBAL_INFO_FLAG_SEMI_PLANAR_FRAMES_SUPPORTED)); 376 | fprintf(stderr, "fully planar frames supported: %d\n", !!(enc_flags & IMX_VPU_API_ENC_GLOBAL_INFO_FLAG_FULLY_PLANAR_FRAMES_SUPPORTED)); 377 | fprintf(stderr, "min required stream buffer size: %zu\n", ctx->enc_global_info->min_required_stream_buffer_size); 378 | fprintf(stderr, "required stream buffer physaddr alignment: %zu\n", ctx->enc_global_info->required_stream_buffer_physaddr_alignment); 379 | fprintf(stderr, "required stream buffer size alignment: %zu\n", ctx->enc_global_info->required_stream_buffer_size_alignment); 380 | fprintf(stderr, "num supported compression formats: %zu\n", ctx->enc_global_info->num_supported_compression_formats); 381 | for (i = 0; i < ctx->enc_global_info->num_supported_compression_formats; ++i) 382 | fprintf(stderr, " %s\n", imx_vpu_api_compression_format_string(ctx->enc_global_info->supported_compression_formats[i])); 383 | 384 | /* If the encoder can handle semi-planar frames, produce such frames. 385 | * This is done by setting use_semi_planar_uv to a nonzero value. 386 | * In that case, y4m_init() will produce a semi-planar color 387 | * format, and y4m_read_frame() will read the frames into input_dmabuffer 388 | * in a semi-planar fashion (that is, it will interleave U and V pixels 389 | * in a combined UV plane). */ 390 | ctx->y4m_context.use_semi_planar_uv = (ctx->enc_global_info->flags & IMX_VPU_API_ENC_GLOBAL_INFO_FLAG_SEMI_PLANAR_FRAMES_SUPPORTED); 391 | if (!y4m_init(ctx->y4m_input_file, &(ctx->y4m_context), 1)) 392 | { 393 | fprintf(stderr, "could not open Y4M input file\n"); 394 | goto error; 395 | } 396 | 397 | /* Set up the DMA buffer allocator. We use this to allocate framebuffers 398 | * and the stream buffer for the encoder. */ 399 | ctx->allocator = imx_dma_buffer_allocator_new(&err); 400 | if (ctx->allocator == NULL) 401 | { 402 | fprintf(stderr, "could not create DMA buffer allocator: %s (%d)\n", strerror(err), err); 403 | goto error; 404 | } 405 | 406 | memset(&(open_params), 0, sizeof(open_params)); 407 | imx_vpu_api_enc_set_default_open_params(IMX_VPU_API_COMPRESSION_FORMAT_H264, ctx->y4m_context.color_format, ctx->y4m_context.width, ctx->y4m_context.height, &open_params); 408 | 409 | /* Allocate the stream buffer that is used throughout the encoding process. */ 410 | ctx->stream_buffer = imx_dma_buffer_allocate( 411 | ctx->allocator, 412 | ctx->enc_global_info->min_required_stream_buffer_size, 413 | ctx->enc_global_info->required_stream_buffer_physaddr_alignment, 414 | 0 415 | ); 416 | assert(ctx->stream_buffer != NULL); 417 | 418 | /* Open an encoder instance, using the previously allocated stream buffer. */ 419 | enc_ret = imx_vpu_api_enc_open(&(ctx->encoder), &open_params, ctx->stream_buffer); 420 | if (enc_ret != IMX_VPU_API_ENC_RETURN_CODE_OK) 421 | goto error; 422 | 423 | /* Get the stream info from the encoder. */ 424 | enc_stream_info = imx_vpu_api_enc_get_stream_info(ctx->encoder); 425 | ctx->stream_info = *enc_stream_info; 426 | 427 | /* Set the strides to make sure we can read frames from the Y4M output file. */ 428 | ctx->y4m_context.y_stride = ctx->stream_info.frame_encoding_framebuffer_metrics.y_stride; 429 | ctx->y4m_context.uv_stride = ctx->stream_info.frame_encoding_framebuffer_metrics.uv_stride; 430 | 431 | if (!allocate_and_add_fb_pool_framebuffers(ctx, enc_stream_info->min_num_required_framebuffers)) 432 | { 433 | fprintf(stderr, "Could not allocate %zu framebuffer(s)\n", enc_stream_info->min_num_required_framebuffers); 434 | goto error; 435 | } 436 | 437 | ctx->input_dmabuffer = imx_dma_buffer_allocate(ctx->allocator, enc_stream_info->min_framebuffer_size, enc_stream_info->framebuffer_alignment, &err); 438 | if (ctx->input_dmabuffer == NULL) 439 | { 440 | fprintf(stderr, "could not allocate DMA buffer for input framebuffer: %s (%d)\n", strerror(err), err); 441 | goto error; 442 | } 443 | 444 | 445 | finish: 446 | return ctx; 447 | 448 | error: 449 | shutdown(ctx); 450 | ctx = NULL; 451 | goto finish; 452 | } 453 | 454 | 455 | Retval run(Context *ctx) 456 | { 457 | /* Feed frames to encoder & encode & output, until we run out of input data. */ 458 | for (;;) 459 | { 460 | Retval ret; 461 | 462 | /* Push raw input frame into the encoder. */ 463 | ret = push_raw_input_frame(ctx); 464 | if (ret == RETVAL_EOS) 465 | break; 466 | else if (ret != RETVAL_OK) 467 | return RETVAL_ERROR; 468 | 469 | /* Encode the previously pushed input data. */ 470 | ret = encode_raw_frame(ctx); 471 | if (ret == RETVAL_EOS) 472 | break; 473 | else if (ret == RETVAL_ERROR) 474 | return RETVAL_ERROR; 475 | } 476 | 477 | return RETVAL_OK; 478 | } 479 | 480 | 481 | void shutdown(Context *ctx) 482 | { 483 | if (ctx == NULL) 484 | return; 485 | 486 | /* Close the previously opened encoder instance. */ 487 | if (ctx->encoder != NULL) 488 | imx_vpu_api_enc_close(ctx->encoder); 489 | 490 | /* Free all allocated memory. */ 491 | deallocate_framebuffers(ctx); 492 | if (ctx->input_dmabuffer) 493 | imx_dma_buffer_deallocate(ctx->input_dmabuffer); 494 | if (ctx->stream_buffer != NULL) 495 | imx_dma_buffer_deallocate(ctx->stream_buffer); 496 | 497 | /* Discard the DMA buffer allocator. */ 498 | if (ctx->allocator != NULL) 499 | imx_dma_buffer_allocator_destroy(ctx->allocator); 500 | 501 | free(ctx); 502 | } 503 | -------------------------------------------------------------------------------- /example/h264_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "h264_utils.h" 5 | 6 | 7 | void h264_ctx_init(h264_context *ctx, FILE *fin) 8 | { 9 | ctx->fin = fin; 10 | 11 | ctx->in_buffer = NULL; 12 | ctx->in_buffer_allocated_size = 0; 13 | ctx->in_buffer_data_size = 0; 14 | 15 | ctx->au_start_offset = 0; 16 | ctx->au_end_offset = 0; 17 | ctx->au_finished = 0; 18 | ctx->first_au = 1; 19 | } 20 | 21 | 22 | void h264_ctx_cleanup(h264_context *ctx) 23 | { 24 | if (ctx->in_buffer != NULL) 25 | free(ctx->in_buffer); 26 | } 27 | 28 | 29 | int h264_ctx_read_access_unit(h264_context *ctx) 30 | { 31 | static size_t const alloc_step_size = (256*1024); 32 | static size_t const read_size = (64*1024); 33 | int num_aud_found = 0; 34 | size_t cur_offset = 0; 35 | 36 | if (ctx->au_finished) 37 | { 38 | unsigned int data_size = ctx->in_buffer_data_size - ctx->au_end_offset; 39 | memmove(ctx->in_buffer, ctx->in_buffer + ctx->au_end_offset, data_size); 40 | ctx->in_buffer_data_size = data_size; 41 | ctx->au_start_offset = 0; 42 | ctx->au_end_offset = 0; 43 | ctx->au_finished = 0; 44 | } 45 | 46 | while (1) 47 | { 48 | uint8_t *newptr; 49 | size_t num_read; 50 | 51 | while ((ctx->in_buffer != NULL) && (cur_offset < ctx->in_buffer_data_size)) 52 | { 53 | uint8_t const *bytes = &(ctx->in_buffer[cur_offset]); 54 | 55 | if ((bytes[0] == 0x00) && (bytes[1] == 0x00) && (bytes[2] == 0x01)) 56 | { 57 | if ((bytes[3] & 0xF) == 0x09) 58 | { 59 | ++num_aud_found; 60 | if (num_aud_found == 1) 61 | { 62 | if (ctx->first_au) 63 | ctx->au_start_offset = 0; 64 | else 65 | ctx->au_start_offset = cur_offset; 66 | } 67 | else if (num_aud_found == 2) 68 | { 69 | ctx->au_end_offset = cur_offset; 70 | ctx->au_finished = 1; 71 | ctx->first_au = 0; 72 | //fprintf(stderr, "AU finished, start %zu end %zu (%zu bytes)\n", ctx->au_start_offset, ctx->au_end_offset, ctx->au_end_offset - ctx->au_start_offset); 73 | return 1; 74 | } 75 | } 76 | cur_offset += 4; 77 | } 78 | else 79 | ++cur_offset; 80 | } 81 | 82 | //fprintf(stderr, "need to read more bytes, current stat: %zu bytes %zu allocated\n", ctx->in_buffer_data_size, ctx->in_buffer_allocated_size); 83 | 84 | if ((ctx->in_buffer_data_size + read_size) >= ctx->in_buffer_allocated_size) 85 | { 86 | newptr = realloc(ctx->in_buffer, ctx->in_buffer_allocated_size + alloc_step_size); 87 | if (newptr == NULL) 88 | { 89 | ctx->au_end_offset = cur_offset; 90 | return 0; 91 | } 92 | ctx->in_buffer = newptr; 93 | ctx->in_buffer_allocated_size += alloc_step_size; 94 | } 95 | 96 | num_read = fread(ctx->in_buffer + ctx->in_buffer_data_size, 1, read_size, ctx->fin); 97 | if (num_read < 4) /* 4 because of the 3-byte start code prefix + the NAL type */ 98 | { 99 | ctx->au_end_offset = cur_offset; 100 | if (ferror(ctx->fin)) 101 | fprintf(stderr, "Reading failed: %s", strerror(errno)); 102 | return 0; 103 | } 104 | ctx->in_buffer_data_size += num_read; 105 | } 106 | 107 | return 0; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /example/h264_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef H264_UTILS_H____ 2 | #define H264_UTILS_H____ 3 | 4 | #include 5 | #include 6 | 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | 13 | typedef struct 14 | { 15 | FILE *fin; 16 | uint8_t *in_buffer; 17 | size_t in_buffer_allocated_size, in_buffer_data_size; 18 | size_t au_start_offset, au_end_offset; 19 | int au_finished, first_au; 20 | } 21 | h264_context; 22 | 23 | 24 | void h264_ctx_init(h264_context *ctx, FILE *fin); 25 | void h264_ctx_cleanup(h264_context *ctx); 26 | int h264_ctx_read_access_unit(h264_context *ctx); 27 | 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /example/jpeg-dec-example.c: -------------------------------------------------------------------------------- 1 | /* example for how to use the imxvpuapi decoder interface to decode JPEGs 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "main.h" 26 | #include "imxvpuapi2/imxvpuapi2_jpeg.h" 27 | 28 | 29 | 30 | /* This is a simple example of how to decode JPEGs with the imxvpuapi library. 31 | * It reads the given JPEG file and configures the VPU to decode JPEG data. 32 | * Then, the decoded pixels are written to the output file, as raw pixels. 33 | * 34 | * Note that using the JPEG decoder is optional, and it is perfectly OK to use 35 | * the lower-level video decoder API for JPEGs as well. (In fact, this is what 36 | * the JPEG decoder does internally.) The JPEG decoder is considerably easier 37 | * to use and requires less boilerplate code, however. */ 38 | 39 | 40 | 41 | struct _Context 42 | { 43 | /* Input/output file handles. */ 44 | FILE *jpeg_input_file, *raw_output_file; 45 | 46 | /* The actual VPU JPEG decoder. */ 47 | ImxVpuApiJpegDecoder *jpeg_decoder; 48 | 49 | /* DMA buffer allocator for the decoder's framebuffer pool and 50 | * for output frames. */ 51 | ImxDmaBufferAllocator *allocator; 52 | }; 53 | 54 | 55 | Context* init(FILE *input_file, FILE *output_file) 56 | { 57 | int err; 58 | Context *ctx; 59 | 60 | ctx = calloc(1, sizeof(Context)); 61 | ctx->jpeg_input_file = input_file; 62 | ctx->raw_output_file = output_file; 63 | 64 | /* Set up the DMA buffer allocator. We use this to allocate framebuffers 65 | * and the stream buffer for the decoder. */ 66 | ctx->allocator = imx_dma_buffer_allocator_new(&err); 67 | if (ctx->allocator == NULL) 68 | { 69 | fprintf(stderr, "could not create DMA buffer allocator: %s (%d)\n", strerror(err), err); 70 | goto error; 71 | } 72 | 73 | /* Open the JPEG decoder. */ 74 | if (!imx_vpu_api_jpeg_dec_open(&(ctx->jpeg_decoder), ctx->allocator)) 75 | { 76 | fprintf(stderr, "could not open VPU JPEG decoder\n"); 77 | goto error; 78 | } 79 | 80 | 81 | finish: 82 | return ctx; 83 | 84 | error: 85 | shutdown(ctx); 86 | ctx = NULL; 87 | goto finish; 88 | } 89 | 90 | 91 | Retval run(Context *ctx) 92 | { 93 | int err; 94 | long size; 95 | void *buf; 96 | ImxVpuApiJpegDecInfo const *info; 97 | uint8_t *mapped_virtual_address; 98 | 99 | 100 | /* Determine size of the input file to be able to read all of its bytes in one go. */ 101 | fseek(ctx->jpeg_input_file, 0, SEEK_END); 102 | size = ftell(ctx->jpeg_input_file); 103 | fseek(ctx->jpeg_input_file, 0, SEEK_SET); 104 | 105 | 106 | /* Allocate buffer for the input data, and read the data into it. */ 107 | buf = malloc(size); 108 | fread(buf, 1, size, ctx->jpeg_input_file); 109 | 110 | fprintf(stderr, "encoded input frame: size: %ld byte\n", size); 111 | 112 | /* Perform the actual JPEG decoding. */ 113 | info = imx_vpu_api_jpeg_dec_decode(ctx->jpeg_decoder, buf, size); 114 | if (info == NULL) 115 | { 116 | free(buf); 117 | fprintf(stderr, "could not decode this JPEG image\n"); 118 | return RETVAL_ERROR; 119 | } 120 | 121 | /* Input data is not needed anymore, so free the input buffer */ 122 | free(buf); 123 | 124 | 125 | fprintf( 126 | stderr, 127 | "aligned frame size: %zu x %zu pixel actual frame size: %zu x %zu pixel Y/Cb/Cr stride: %zu/%zu/%zu Y/Cb/Cr size: %zu/%zu/%zu Y/Cb/Cr offset: %zu/%zu/%zu color format: %s\n", 128 | info->framebuffer_metrics->aligned_frame_width, info->framebuffer_metrics->aligned_frame_height, 129 | info->framebuffer_metrics->actual_frame_width, info->framebuffer_metrics->actual_frame_height, 130 | info->framebuffer_metrics->y_stride, info->framebuffer_metrics->uv_stride, info->framebuffer_metrics->uv_stride, 131 | info->framebuffer_metrics->y_size, info->framebuffer_metrics->uv_size, info->framebuffer_metrics->uv_size, 132 | info->framebuffer_metrics->y_offset, info->framebuffer_metrics->u_offset, info->framebuffer_metrics->v_offset, 133 | imx_vpu_api_color_format_string(info->color_format) 134 | ); 135 | 136 | 137 | /* Map the DMA buffer of the decoded picture, write out the decoded pixels, and unmap the buffer again. */ 138 | fprintf(stderr, "decoded output picture: writing %zu byte\n", info->total_frame_size); 139 | mapped_virtual_address = imx_dma_buffer_map(info->fb_dma_buffer, IMX_DMA_BUFFER_MAPPING_FLAG_READ, &err); 140 | if (mapped_virtual_address == NULL) 141 | { 142 | fprintf(stderr, "could not map decoded frame DMA buffer: %s (%d)\n", strerror(err), err); 143 | return RETVAL_ERROR; 144 | } 145 | fwrite(mapped_virtual_address, 1, info->total_frame_size, ctx->raw_output_file); 146 | imx_dma_buffer_unmap(info->fb_dma_buffer); 147 | 148 | 149 | return RETVAL_OK; 150 | } 151 | 152 | 153 | void shutdown(Context *ctx) 154 | { 155 | if (ctx == NULL) 156 | return; 157 | 158 | /* Shut down the JPEG decoder */ 159 | imx_vpu_api_jpeg_dec_close(ctx->jpeg_decoder); 160 | 161 | free(ctx); 162 | } 163 | -------------------------------------------------------------------------------- /example/jpeg-enc-example.c: -------------------------------------------------------------------------------- 1 | /* example for how to use the imxvpuapi encoder interface to encode JPEGs 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "main.h" 26 | #include "imxvpuapi2/imxvpuapi2_jpeg.h" 27 | 28 | 29 | 30 | /* This is a simple example of how to encode JPEGs with the imxvpuapi library. 31 | * It reads the given raw YUV file and configures the VPU to encode JPEG data. 32 | * Then, the encoded JPEG data is written to the output file. 33 | * 34 | * This example expects as input a file with uncompressed 320x240 i420 frames. 35 | * 36 | * Note that using the JPEG encoder is optional, and it is perfectly OK to use 37 | * the lower-level video encoder API for JPEGs as well. (In fact, this is what 38 | * the JPEG encoder does internally.) The JPEG encoder is considerably easier 39 | * to use and requires less boilerplate code, however. */ 40 | 41 | 42 | #define FRAME_WIDTH 768 43 | #define FRAME_HEIGHT 576 44 | #define COLOR_FORMAT IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT 45 | 46 | 47 | struct _Context 48 | { 49 | /* Input/output file handles. */ 50 | FILE *raw_input_file, *jpeg_output_file; 51 | 52 | /* The actual VPU JPEG encoder. */ 53 | ImxVpuApiJpegEncoder *jpeg_encoder; 54 | 55 | /* DMA buffer allocator for the encoder's framebuffer pool and 56 | * for output frames. */ 57 | ImxDmaBufferAllocator *allocator; 58 | 59 | ImxVpuApiFramebufferMetrics const *fb_metrics; 60 | }; 61 | 62 | 63 | Context* init(FILE *input_file, FILE *output_file) 64 | { 65 | int err; 66 | Context *ctx; 67 | ImxVpuApiJpegEncParams params; 68 | 69 | ctx = calloc(1, sizeof(Context)); 70 | ctx->raw_input_file = input_file; 71 | ctx->jpeg_output_file = output_file; 72 | 73 | 74 | /* Set up the DMA buffer allocator. We use this to allocate framebuffers 75 | * and the stream buffer for the decoder. */ 76 | ctx->allocator = imx_dma_buffer_allocator_new(&err); 77 | if (ctx->allocator == NULL) 78 | { 79 | fprintf(stderr, "could not create DMA buffer allocator: %s (%d)\n", strerror(err), err); 80 | goto error; 81 | } 82 | 83 | 84 | /* Open the JPEG encoder. */ 85 | if (!imx_vpu_api_jpeg_enc_open(&(ctx->jpeg_encoder), ctx->allocator)) 86 | { 87 | fprintf(stderr, "could not open VPU JPEG encoder\n"); 88 | goto error; 89 | } 90 | 91 | 92 | /* Configure the JPEG encoder. */ 93 | memset(¶ms, 0, sizeof(params)); 94 | params.frame_width = FRAME_WIDTH; 95 | params.frame_height = FRAME_HEIGHT; 96 | params.color_format = COLOR_FORMAT; 97 | params.quality_factor = 5; 98 | if (!imx_vpu_api_jpeg_enc_set_params(ctx->jpeg_encoder, ¶ms)) 99 | { 100 | fprintf(stderr, "could not set JPEG encoding parameters\n"); 101 | goto error; 102 | } 103 | 104 | 105 | ctx->fb_metrics = imx_vpu_api_jpeg_enc_get_framebuffer_metrics(ctx->jpeg_encoder); 106 | 107 | 108 | finish: 109 | return ctx; 110 | 111 | error: 112 | shutdown(ctx); 113 | ctx = NULL; 114 | goto finish; 115 | } 116 | 117 | 118 | Retval run(Context *ctx) 119 | { 120 | Retval ret = RETVAL_OK; 121 | int err; 122 | long size; 123 | void *encoded_jpeg_data = NULL; 124 | ImxDmaBuffer *frame_dma_buffer = NULL; 125 | uint8_t *mapped_virtual_address; 126 | size_t encoded_data_size; 127 | 128 | 129 | /* Determine size of the input file to be able to read all of its bytes in one go. */ 130 | fseek(ctx->raw_input_file, 0, SEEK_END); 131 | size = ftell(ctx->raw_input_file); 132 | fseek(ctx->raw_input_file, 0, SEEK_SET); 133 | 134 | 135 | /* Allocate a DMA buffer for the raw input frame. */ 136 | frame_dma_buffer = imx_dma_buffer_allocate(ctx->allocator, size, 1, &err); 137 | if (frame_dma_buffer == NULL) 138 | { 139 | fprintf(stderr, "could not allocate DMA buffer for raw input frame: %s (%d)\n", strerror(err), err); 140 | goto error; 141 | } 142 | 143 | 144 | /* Map the DMA buffer and read the raw input data into it. */ 145 | mapped_virtual_address = imx_dma_buffer_map(frame_dma_buffer, IMX_DMA_BUFFER_MAPPING_FLAG_WRITE, &err); 146 | if (mapped_virtual_address == NULL) 147 | { 148 | fprintf(stderr, "could not map raw frame DMA buffer: %s (%d)\n", strerror(err), err); 149 | goto error; 150 | } 151 | fread(mapped_virtual_address, 1, size, ctx->raw_input_file); 152 | imx_dma_buffer_unmap(frame_dma_buffer); 153 | 154 | 155 | /* Encode the frame and write the encoded data to the output file. */ 156 | if (!imx_vpu_api_jpeg_enc_encode(ctx->jpeg_encoder, frame_dma_buffer, &encoded_data_size)) 157 | { 158 | fprintf(stderr, "could not encode frame to JPEG"); 159 | goto error; 160 | } 161 | 162 | encoded_jpeg_data = malloc(encoded_data_size); 163 | assert(encoded_jpeg_data != NULL); 164 | 165 | if (!imx_vpu_api_jpeg_enc_get_encoded_data(ctx->jpeg_encoder, encoded_jpeg_data)) 166 | { 167 | fprintf(stderr, "could not retrieve encoded JPEG data"); 168 | goto error; 169 | } 170 | 171 | fwrite(encoded_jpeg_data, 1, encoded_data_size, ctx->jpeg_output_file); 172 | 173 | 174 | finish: 175 | free(encoded_jpeg_data); 176 | if (frame_dma_buffer != NULL) 177 | imx_dma_buffer_deallocate(frame_dma_buffer); 178 | 179 | return ret; 180 | 181 | 182 | error: 183 | ret = RETVAL_ERROR; 184 | goto finish; 185 | } 186 | 187 | 188 | void shutdown(Context *ctx) 189 | { 190 | if (ctx == NULL) 191 | return; 192 | 193 | /* Shut down the JPEG encoder */ 194 | imx_vpu_api_jpeg_enc_close(ctx->jpeg_encoder); 195 | 196 | free(ctx); 197 | } 198 | -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | /* main code used by all examples 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "main.h" 29 | 30 | 31 | static void logging_fn(ImxVpuApiLogLevel level, char const *file, int const line, char const *fn, const char *format, ...) 32 | { 33 | va_list args; 34 | 35 | char const *lvlstr = ""; 36 | switch (level) 37 | { 38 | case IMX_VPU_API_LOG_LEVEL_ERROR: lvlstr = "ERROR"; break; 39 | case IMX_VPU_API_LOG_LEVEL_WARNING: lvlstr = "WARNING"; break; 40 | case IMX_VPU_API_LOG_LEVEL_INFO: lvlstr = "info"; break; 41 | case IMX_VPU_API_LOG_LEVEL_DEBUG: lvlstr = "debug"; break; 42 | case IMX_VPU_API_LOG_LEVEL_TRACE: lvlstr = "trace"; break; 43 | case IMX_VPU_API_LOG_LEVEL_LOG: lvlstr = "log"; break; 44 | default: break; 45 | } 46 | 47 | fprintf(stderr, "%s:%d (%s) %s: ", file, line, fn, lvlstr); 48 | 49 | va_start(args, format); 50 | vfprintf(stderr, format, args); 51 | va_end(args); 52 | 53 | fprintf(stderr, "\n"); 54 | } 55 | 56 | 57 | static void usage(char *progname) 58 | { 59 | static char options[] = 60 | "\t-i input file\n" 61 | "\t-o output file\n" 62 | ; 63 | 64 | fprintf(stderr, "usage:\t%s [option]\n\noption:\n%s\n", progname, options); 65 | } 66 | 67 | 68 | static int parse_args(int argc, char **argv, char **input_filename, char **output_filename) 69 | { 70 | int opt; 71 | 72 | *input_filename = NULL; 73 | *output_filename = NULL; 74 | 75 | while ((opt = getopt(argc, argv, "i:o:")) != -1) 76 | { 77 | switch (opt) 78 | { 79 | case 'i': 80 | *input_filename = optarg; 81 | break; 82 | case 'o': 83 | *output_filename = optarg; 84 | break; 85 | default: 86 | usage(argv[0]); 87 | return 0; 88 | } 89 | } 90 | 91 | if (*input_filename == NULL) 92 | { 93 | fprintf(stderr, "Missing input filename\n\n"); 94 | usage(argv[0]); 95 | return RETVAL_ERROR; 96 | } 97 | 98 | if (*output_filename == NULL) 99 | { 100 | fprintf(stderr, "Missing output filename\n\n"); 101 | usage(argv[0]); 102 | return RETVAL_ERROR; 103 | } 104 | 105 | return RETVAL_OK; 106 | } 107 | 108 | 109 | 110 | 111 | int main(int argc, char *argv[]) 112 | { 113 | FILE *input_file = NULL; 114 | FILE *output_file = NULL; 115 | char *input_filename = NULL; 116 | char *output_filename = NULL; 117 | Context *ctx = NULL; 118 | int ret = 0; 119 | 120 | if (parse_args(argc, argv, &input_filename, &output_filename) != RETVAL_OK) 121 | { 122 | ret = 1; 123 | goto cleanup; 124 | } 125 | 126 | input_file = fopen(input_filename, "rb"); 127 | if (input_file == NULL) 128 | { 129 | fprintf(stderr, "Opening %s for reading failed: %s\n", input_filename, strerror(errno)); 130 | ret = 1; 131 | goto cleanup; 132 | } 133 | 134 | output_file = fopen(output_filename, "wb"); 135 | if (output_file == NULL) 136 | { 137 | fprintf(stderr, "Opening %s for writing failed: %s\n", output_filename, strerror(errno)); 138 | ret = 1; 139 | goto cleanup; 140 | } 141 | 142 | imx_vpu_api_set_logging_threshold(IMX_VPU_API_LOG_LEVEL_TRACE); 143 | imx_vpu_api_set_logging_function(logging_fn); 144 | 145 | if ((ctx = init(input_file, output_file)) == NULL) 146 | { 147 | ret = 1; 148 | goto cleanup; 149 | } 150 | 151 | if (run(ctx) == RETVAL_ERROR) 152 | ret = 1; 153 | 154 | cleanup: 155 | if (ctx != NULL) 156 | shutdown(ctx); 157 | 158 | if (input_file != NULL) 159 | fclose(input_file); 160 | if (output_file != NULL) 161 | fclose(output_file); 162 | 163 | return ret; 164 | } 165 | -------------------------------------------------------------------------------- /example/main.h: -------------------------------------------------------------------------------- 1 | /* main code used by all examples 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | #ifndef MAIN_H_____________ 21 | #define MAIN_H_____________ 22 | 23 | #include 24 | #include "imxvpuapi2/imxvpuapi2.h" 25 | 26 | 27 | typedef enum 28 | { 29 | RETVAL_OK = 0, 30 | RETVAL_ERROR = 1, 31 | RETVAL_EOS = 2 32 | } 33 | Retval; 34 | 35 | 36 | typedef struct _Context Context; 37 | 38 | Context* init(FILE *input_file, FILE *output_file); 39 | Retval run(Context *ctx); 40 | void shutdown(Context *ctx); 41 | 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /example/test-320x240.h264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freescale/libimxvpuapi/37095a854aa176bb763a25ce98ceb6a787501271/example/test-320x240.h264 -------------------------------------------------------------------------------- /example/y4m_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "y4m_io.h" 4 | 5 | 6 | int y4m_init(FILE *file, Y4MContext *context, int read_y4m) 7 | { 8 | char token[1000]; 9 | 10 | context->file = file; 11 | 12 | if (read_y4m) 13 | { 14 | if ((fscanf(file, "%s ", token) <= 0) || (strncmp(token, "YUV4MPEG2", 9) != 0)) 15 | return 0; 16 | 17 | while (fscanf(file, "%s ", token) > 0) 18 | { 19 | if (strncmp(token, "FRAME", 5) == 0) 20 | { 21 | context->frame_token_seen = 1; 22 | break; 23 | } 24 | 25 | switch (token[0]) 26 | { 27 | case 'W': 28 | { 29 | context->width = atoi(&(token[1])); 30 | if (context->width == 0) 31 | return 0; 32 | break; 33 | } 34 | 35 | case 'H': 36 | { 37 | context->height = atoi(&(token[1])); 38 | if (context->height == 0) 39 | return 0; 40 | break; 41 | } 42 | 43 | case 'F': 44 | { 45 | if (sscanf(&(token[1]), "%u:%u", &(context->fps_num), &(context->fps_denom)) <= 0) 46 | return 0; 47 | break; 48 | } 49 | 50 | case 'I': 51 | { 52 | switch (token[1]) 53 | { 54 | case 'p': context->interlacing = IMX_VPU_API_INTERLACING_MODE_NO_INTERLACING; break; 55 | case 't': context->interlacing = IMX_VPU_API_INTERLACING_MODE_TOP_FIELD_FIRST; break; 56 | case 'b': context->interlacing = IMX_VPU_API_INTERLACING_MODE_BOTTOM_FIELD_FIRST; break; 57 | case 'm': 58 | default: context->interlacing = IMX_VPU_API_INTERLACING_MODE_UNKNOWN; break; 59 | } 60 | break; 61 | } 62 | 63 | case 'A': 64 | { 65 | if (sscanf(&(token[1]), "%u:%u", &(context->par_num), &(context->par_denom)) <= 0) 66 | return 0; 67 | break; 68 | } 69 | 70 | case 'C': 71 | { 72 | if (strncmp(&(token[1]), "420", 3) == 0) 73 | { 74 | context->color_format = context->use_semi_planar_uv ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT; 75 | } 76 | else if (strncmp(&(token[1]), "422", 3) == 0) 77 | { 78 | context->color_format = context->use_semi_planar_uv ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT; 79 | } 80 | else if (strncmp(&(token[1]), "444", 3) == 0) 81 | { 82 | context->color_format = context->use_semi_planar_uv ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT; 83 | } 84 | else 85 | return 0; 86 | break; 87 | } 88 | 89 | default: 90 | break; 91 | } 92 | } 93 | } 94 | else 95 | { 96 | char interlacing_char; 97 | char const *colorspace_str; 98 | 99 | switch (context->interlacing) 100 | { 101 | case IMX_VPU_API_INTERLACING_MODE_NO_INTERLACING: interlacing_char = 'p'; break; 102 | case IMX_VPU_API_INTERLACING_MODE_TOP_FIELD_FIRST: interlacing_char = 't'; break; 103 | case IMX_VPU_API_INTERLACING_MODE_BOTTOM_FIELD_FIRST: interlacing_char = 'b'; break; 104 | case IMX_VPU_API_INTERLACING_MODE_UNKNOWN: interlacing_char = 'p'; break; 105 | default: 106 | return 0; 107 | } 108 | 109 | switch (context->color_format) 110 | { 111 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT: 112 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT: 113 | colorspace_str = "420"; 114 | break; 115 | 116 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT: 117 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT: 118 | colorspace_str = "422"; 119 | break; 120 | 121 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT: 122 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT: 123 | colorspace_str = "444"; 124 | break; 125 | 126 | default: 127 | return 0; 128 | } 129 | 130 | context->use_semi_planar_uv = imx_vpu_api_is_color_format_semi_planar(context->color_format); 131 | 132 | if (fprintf(file, "YUV4MPEG2 W%zu H%zu F%u:%u I%c A%u:%u C%s\n", context->width, context->height, context->fps_num, context->fps_denom, interlacing_char, context->par_num, context->par_denom, colorspace_str) < 0) 133 | return 0; 134 | } 135 | 136 | switch (context->color_format) 137 | { 138 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT: 139 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT: 140 | context->y_size[0] = context->width; 141 | context->y_size[1] = context->height; 142 | context->uv_size[0] = context->width / 2; 143 | context->uv_size[1] = context->height / 2; 144 | break; 145 | 146 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT: 147 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT: 148 | context->y_size[0] = context->width; 149 | context->y_size[1] = context->height; 150 | context->uv_size[0] = context->width; 151 | context->uv_size[1] = context->height / 2; 152 | break; 153 | 154 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT: 155 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT: 156 | context->y_size[0] = context->width; 157 | context->y_size[1] = context->height; 158 | context->uv_size[0] = context->width; 159 | context->uv_size[1] = context->height; 160 | break; 161 | 162 | default: 163 | return 0; 164 | } 165 | 166 | return 1; 167 | } 168 | 169 | 170 | int y4m_read_frame(Y4MContext *context, void *y_dest, void *u_dest, void *v_dest) 171 | { 172 | size_t y; 173 | uint8_t *dest; 174 | 175 | if (!context->frame_token_seen) 176 | { 177 | char token[1000]; 178 | if (fread(token, 1, 5, context->file) < 5) 179 | return 0; 180 | 181 | if (strncmp(token, "FRAME", 5) != 0) 182 | { 183 | return 0; 184 | } 185 | 186 | for (;;) 187 | { 188 | int c = fgetc(context->file); 189 | 190 | if (c < 0) 191 | return 0; 192 | else if (c == '\n') 193 | break; 194 | } 195 | } 196 | 197 | context->frame_token_seen = 0; 198 | 199 | dest = y_dest; 200 | for (y = 0; y < context->y_size[1]; ++y) 201 | { 202 | if (fread(dest, 1, context->y_size[0], context->file) < context->y_size[0]) 203 | return 0; 204 | dest += context->y_stride; 205 | } 206 | 207 | if (context->use_semi_planar_uv) 208 | { 209 | int plane; 210 | 211 | for (plane = 0; plane < 2; ++plane) 212 | { 213 | dest = u_dest; 214 | for (y = 0; y < context->uv_size[1]; ++y) 215 | { 216 | size_t x; 217 | for (x = 0; x < context->uv_size[0]; ++x) 218 | { 219 | int b = fgetc(context->file); 220 | if (b < 0) 221 | return 0; 222 | dest[(x * 2) + plane] = b; 223 | } 224 | dest += context->uv_stride; 225 | } 226 | } 227 | } 228 | else 229 | { 230 | dest = u_dest; 231 | for (y = 0; y < context->uv_size[1]; ++y) 232 | { 233 | if (fread(dest, 1, context->uv_size[0], context->file) < context->uv_size[0]) 234 | return 0; 235 | dest += context->uv_stride; 236 | } 237 | 238 | dest = v_dest; 239 | for (y = 0; y < context->uv_size[1]; ++y) 240 | { 241 | if (fread(dest, 1, context->uv_size[0], context->file) < context->uv_size[0]) 242 | return 0; 243 | dest += context->uv_stride; 244 | } 245 | } 246 | 247 | return 1; 248 | } 249 | 250 | 251 | int y4m_write_frame(Y4MContext const *context, void const *y_src, void const *u_src, void const *v_src) 252 | { 253 | size_t y; 254 | uint8_t const *src; 255 | 256 | fprintf(context->file, "FRAME\n"); 257 | 258 | src = y_src; 259 | for (y = 0; y < context->y_size[1]; ++y) 260 | { 261 | if (fwrite(src, 1, context->y_size[0], context->file) < context->y_size[0]) 262 | return 0; 263 | src += context->y_stride; 264 | } 265 | 266 | if (context->use_semi_planar_uv) 267 | { 268 | int plane; 269 | 270 | for (plane = 0; plane < 2; ++plane) 271 | { 272 | src = u_src; 273 | for (y = 0; y < context->uv_size[1]; ++y) 274 | { 275 | size_t x; 276 | for (x = 0; x < context->uv_size[0]; ++x) 277 | { 278 | if (fputc(src[(x * 2) + plane], context->file) < 0) 279 | return 0; 280 | } 281 | src += context->uv_stride; 282 | } 283 | } 284 | } 285 | else 286 | { 287 | src = u_src; 288 | for (y = 0; y < context->uv_size[1]; ++y) 289 | { 290 | if (fwrite(src, 1, context->uv_size[0], context->file) < context->uv_size[0]) 291 | return 0; 292 | src += context->uv_stride; 293 | } 294 | 295 | src = v_src; 296 | for (y = 0; y < context->uv_size[1]; ++y) 297 | { 298 | if (fwrite(src, 1, context->uv_size[0], context->file) < context->uv_size[0]) 299 | return 0; 300 | src += context->uv_stride; 301 | } 302 | } 303 | 304 | return 1; 305 | } 306 | -------------------------------------------------------------------------------- /example/y4m_io.h: -------------------------------------------------------------------------------- 1 | /* YUV4MPEG2 (y4m) IO code used by all examples 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | #ifndef Y4M_IO_H_____________ 21 | #define Y4M_IO_H_____________ 22 | 23 | #include 24 | #include 25 | #include "imxvpuapi2/imxvpuapi2.h" 26 | 27 | 28 | typedef struct 29 | { 30 | size_t width, height; 31 | size_t y_stride, uv_stride; 32 | unsigned int fps_num, fps_denom; 33 | unsigned par_num, par_denom; 34 | 35 | ImxVpuApiInterlacingMode interlacing; 36 | 37 | ImxVpuApiColorFormat color_format; 38 | int use_semi_planar_uv; 39 | 40 | 41 | FILE *file; 42 | int frame_token_seen; 43 | size_t y_size[2], uv_size[2]; 44 | } 45 | Y4MContext; 46 | 47 | 48 | int y4m_init(FILE *file, Y4MContext *context, int read_y4m); 49 | int y4m_read_frame(Y4MContext *context, void *y_dest, void *u_dest, void *v_dest); 50 | int y4m_write_frame(Y4MContext const *context, void const *y_src, void const *u_src, void const *v_src); 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "imxvpuapi2.h" 5 | #include "imxvpuapi2_priv.h" 6 | 7 | 8 | /***********************/ 9 | /******* LOGGING *******/ 10 | /***********************/ 11 | 12 | 13 | static void default_logging_fn(ImxVpuApiLogLevel level, char const *file, int const line, char const *fn, const char *format, ...) 14 | { 15 | IMX_VPU_API_UNUSED_PARAM(level); 16 | IMX_VPU_API_UNUSED_PARAM(file); 17 | IMX_VPU_API_UNUSED_PARAM(line); 18 | IMX_VPU_API_UNUSED_PARAM(fn); 19 | IMX_VPU_API_UNUSED_PARAM(format); 20 | } 21 | 22 | ImxVpuApiLogLevel imx_vpu_api_cur_log_level_threshold = IMX_VPU_API_LOG_LEVEL_ERROR; 23 | ImxVpuApiLoggingFunc imx_vpu_api_cur_logging_fn = default_logging_fn; 24 | 25 | void imx_vpu_api_set_logging_function(ImxVpuApiLoggingFunc logging_fn) 26 | { 27 | imx_vpu_api_cur_logging_fn = (logging_fn != NULL) ? logging_fn : default_logging_fn; 28 | } 29 | 30 | void imx_vpu_api_set_logging_threshold(ImxVpuApiLogLevel threshold) 31 | { 32 | imx_vpu_api_cur_log_level_threshold = threshold; 33 | } 34 | 35 | 36 | 37 | 38 | /******************************************************/ 39 | /******* MISCELLANEOUS STRUCTURES AND FUNCTIONS *******/ 40 | /******************************************************/ 41 | 42 | 43 | char const *imx_vpu_api_frame_type_string(ImxVpuApiFrameType frame_type) 44 | { 45 | switch (frame_type) 46 | { 47 | case IMX_VPU_API_FRAME_TYPE_I: return "I"; 48 | case IMX_VPU_API_FRAME_TYPE_P: return "P"; 49 | case IMX_VPU_API_FRAME_TYPE_B: return "B"; 50 | case IMX_VPU_API_FRAME_TYPE_IDR: return "IDR"; 51 | case IMX_VPU_API_FRAME_TYPE_BI: return "BI"; 52 | case IMX_VPU_API_FRAME_TYPE_SKIP: return "SKIP"; 53 | case IMX_VPU_API_FRAME_TYPE_UNKNOWN: 54 | default: return ""; 55 | } 56 | } 57 | 58 | 59 | char const *imx_vpu_api_interlacing_mode_string(ImxVpuApiInterlacingMode mode) 60 | { 61 | switch (mode) 62 | { 63 | case IMX_VPU_API_INTERLACING_MODE_NO_INTERLACING: return "no interlacing"; 64 | case IMX_VPU_API_INTERLACING_MODE_TOP_FIELD_FIRST: return "top field first"; 65 | case IMX_VPU_API_INTERLACING_MODE_BOTTOM_FIELD_FIRST: return "bottom field first"; 66 | case IMX_VPU_API_INTERLACING_MODE_TOP_FIELD_ONLY: return "top field only"; 67 | case IMX_VPU_API_INTERLACING_MODE_BOTTOM_FIELD_ONLY: return "bottom field only"; 68 | case IMX_VPU_API_FRAME_TYPE_UNKNOWN: 69 | default: return ""; 70 | } 71 | } 72 | 73 | 74 | char const *imx_vpu_api_compression_format_string(ImxVpuApiCompressionFormat format) 75 | { 76 | switch (format) 77 | { 78 | case IMX_VPU_API_COMPRESSION_FORMAT_JPEG: return "JPEG"; 79 | case IMX_VPU_API_COMPRESSION_FORMAT_WEBP: return "WebP"; 80 | case IMX_VPU_API_COMPRESSION_FORMAT_MPEG2: return "MPEG-2 part 2"; 81 | case IMX_VPU_API_COMPRESSION_FORMAT_MPEG4: return "MPEG-4 part 2"; 82 | case IMX_VPU_API_COMPRESSION_FORMAT_H263: return "h.263"; 83 | case IMX_VPU_API_COMPRESSION_FORMAT_H264: return "h.264 / AVC"; 84 | case IMX_VPU_API_COMPRESSION_FORMAT_H265: return "h.265 / HEVC"; 85 | case IMX_VPU_API_COMPRESSION_FORMAT_WMV3: return "WMV3 / Windows Media Video 9"; 86 | case IMX_VPU_API_COMPRESSION_FORMAT_WVC1: return "VC-1 / Windows Media Video 9 Advanced Profile"; 87 | case IMX_VPU_API_COMPRESSION_FORMAT_VP6: return "VP6"; 88 | case IMX_VPU_API_COMPRESSION_FORMAT_VP8: return "VP8"; 89 | case IMX_VPU_API_COMPRESSION_FORMAT_VP9: return "VP9"; 90 | case IMX_VPU_API_COMPRESSION_FORMAT_AVS: return "AVS"; 91 | case IMX_VPU_API_COMPRESSION_FORMAT_RV30: return "RealVideo 8 (RV30)"; 92 | case IMX_VPU_API_COMPRESSION_FORMAT_RV40: return "RealVideo 9 & 10 (RV40)"; 93 | case IMX_VPU_API_COMPRESSION_FORMAT_DIVX3: return "DivX 3"; 94 | case IMX_VPU_API_COMPRESSION_FORMAT_DIVX4: return "DivX 4"; 95 | case IMX_VPU_API_COMPRESSION_FORMAT_DIVX5: return "DivX 5"; 96 | case IMX_VPU_API_COMPRESSION_FORMAT_SORENSON_SPARK: return "Sorenson Spark"; 97 | default: return ""; 98 | } 99 | } 100 | 101 | 102 | char const *imx_vpu_api_color_format_string(ImxVpuApiColorFormat color_format) 103 | { 104 | switch (color_format) 105 | { 106 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT: return "fully planar YUV 4:2:0 8-bit"; 107 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_10BIT: return "fully planar YUV 4:2:0 10-bit"; 108 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT: return "semi planar YUV 4:2:0 8-bit"; 109 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_10BIT: return "semi planar YUV 4:2:0 10-bit"; 110 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV411_8BIT: return "fully planar YUV 4:1:1 8-bit"; 111 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV411_10BIT: return "fully planar YUV 4:1:1 10-bit"; 112 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV411_8BIT: return "semi planar YUV 4:1:1 8-bit"; 113 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV411_10BIT: return "semi planar YUV 4:1:1 10-bit"; 114 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT: return "fully planar YUV 4:2:2 horizontal 8-bit"; 115 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_10BIT: return "fully planar YUV 4:2:2 horizontal 10-bit"; 116 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT: return "semi planar YUV 4:2:2 horizontal 8-bit"; 117 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_10BIT: return "semi planar YUV 4:2:2 horizontal 10-bit"; 118 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_VERTICAL_8BIT: return "fully planar YUV 2:2:4 vertical 8-bit"; 119 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_VERTICAL_10BIT: return "fully planar YUV 2:2:4 vertical 10-bit"; 120 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_8BIT: return "semi planar YUV 2:2:4 vertical 8-bit"; 121 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_10BIT: return "semi planar YUV 2:2:4 vertical 10-bit"; 122 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT: return "fully planar YUV 4:4:4 8-bit"; 123 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_10BIT: return "fully planar YUV 4:4:4 10-bit"; 124 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT: return "semi planar YUV 4:4:4 8-bit"; 125 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_10BIT: return "semi planar YUV 4:4:4 10-bit"; 126 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_P010_10BIT: return "semi planar YUV 4:2:0 Microsoft P010 10-bit"; 127 | case IMX_VPU_API_COLOR_FORMAT_YUV400_8BIT: return "YUV 4:0:0 (8-bit grayscale)"; 128 | case IMX_VPU_API_COLOR_FORMAT_YUV400_10BIT: return "YUV 4:0:0 (10-bit grayscale)"; 129 | 130 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_8BIT: return "VeriSilicon Hantro G2 semi planar 4x4 tiled YUV 4:2:0 8-bit"; 131 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_10BIT: return "VeriSilicon Hantro G2 semi planar 4x4 tiled YUV 4:2:0 10-bit"; 132 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_8BIT: return "VeriSilicon Hantro G1 semi planar 8x4 tiled YUV 4:2:0 8-bit"; 133 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_10BIT: return "VeriSilicon Hantro G1 semi planar 8x4 tiled YUV 4:2:0 10-bit"; 134 | 135 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_8BIT: return "Amphion semi planar 8x128 tiled YUV 4:2:0 8-bit"; 136 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_10BIT: return "Amphion semi planar 8x128 tiled YUV 4:2:0 10-bit"; 137 | 138 | case IMX_VPU_API_COLOR_FORMAT_PACKED_YUV422_UYVY_8BIT: return "packed YUV 4:2:2 U0-Y0-V0-Y1 8-bit"; 139 | case IMX_VPU_API_COLOR_FORMAT_PACKED_YUV422_YUYV_8BIT: return "packed YUV 4:2:2 Y0-U0-Y1-V0 8-bit"; 140 | 141 | case IMX_VPU_API_COLOR_FORMAT_RGB565: return "RGB 5:6:5 (16 bits per pixel)"; 142 | case IMX_VPU_API_COLOR_FORMAT_BGR565: return "BGR 5:6:5 (16 bits per pixel)"; 143 | case IMX_VPU_API_COLOR_FORMAT_ARGB1555: return "ARGB 1:5:5:5 (15 bits per pixel, 1 MSB padding)"; 144 | case IMX_VPU_API_COLOR_FORMAT_RGBA8888: return "RGBA 8:8:8:8 (32 bits per pixel)"; 145 | case IMX_VPU_API_COLOR_FORMAT_BGRA8888: return "BGRA 8:8:8:8 (32 bits per pixel)"; 146 | 147 | default: return ""; 148 | } 149 | } 150 | 151 | 152 | int imx_vpu_api_is_color_format_semi_planar(ImxVpuApiColorFormat color_format) 153 | { 154 | switch (color_format) 155 | { 156 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT: 157 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_10BIT: 158 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV411_8BIT: 159 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV411_10BIT: 160 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT: 161 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_10BIT: 162 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_8BIT: 163 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_10BIT: 164 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT: 165 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_10BIT: 166 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_P010_10BIT: 167 | return 1; 168 | 169 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_8BIT: 170 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_10BIT: 171 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_8BIT: 172 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_10BIT: 173 | return 1; 174 | 175 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_8BIT: 176 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_10BIT: 177 | return 1; 178 | 179 | default: 180 | break; 181 | } 182 | 183 | return 0; 184 | } 185 | 186 | 187 | int imx_vpu_api_is_color_format_rgb(ImxVpuApiColorFormat color_format) 188 | { 189 | switch (color_format) 190 | { 191 | case IMX_VPU_API_COLOR_FORMAT_RGB565: 192 | case IMX_VPU_API_COLOR_FORMAT_BGR565: 193 | case IMX_VPU_API_COLOR_FORMAT_RGB444: 194 | case IMX_VPU_API_COLOR_FORMAT_ARGB4444: 195 | case IMX_VPU_API_COLOR_FORMAT_ARGB1555: 196 | case IMX_VPU_API_COLOR_FORMAT_RGBA8888: 197 | case IMX_VPU_API_COLOR_FORMAT_BGRA8888: 198 | return 1; 199 | 200 | default: 201 | break; 202 | } 203 | 204 | return 0; 205 | } 206 | 207 | 208 | int imx_vpu_api_is_color_format_10bit(ImxVpuApiColorFormat color_format) 209 | { 210 | switch (color_format) 211 | { 212 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_10BIT: 213 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_10BIT: 214 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV411_10BIT: 215 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV411_10BIT: 216 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_10BIT: 217 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_10BIT: 218 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_VERTICAL_10BIT: 219 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_10BIT: 220 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_10BIT: 221 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_10BIT: 222 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_P010_10BIT: 223 | case IMX_VPU_API_COLOR_FORMAT_YUV400_10BIT: 224 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_10BIT: 225 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_10BIT: 226 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_10BIT: 227 | return 1; 228 | 229 | default: 230 | break; 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | 237 | int imx_vpu_api_is_color_format_tiled(ImxVpuApiColorFormat color_format) 238 | { 239 | switch (color_format) 240 | { 241 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_8BIT: 242 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_4x4TILED_10BIT: 243 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_8BIT: 244 | case IMX_VPU_API_HANTRO_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x4TILED_10BIT: 245 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_8BIT: 246 | case IMX_VPU_API_AMPHION_COLOR_FORMAT_YUV420_SEMI_PLANAR_8x128TILED_10BIT: 247 | return 1; 248 | 249 | default: 250 | return 0; 251 | } 252 | } 253 | 254 | 255 | char const * imx_vpu_api_h264_profile_string(ImxVpuApiH264Profile profile) 256 | { 257 | switch (profile) 258 | { 259 | case IMX_VPU_API_H264_PROFILE_CONSTRAINED_BASELINE: return "constrained baseline"; 260 | case IMX_VPU_API_H264_PROFILE_BASELINE: return "baseline"; 261 | case IMX_VPU_API_H264_PROFILE_MAIN: return "main"; 262 | case IMX_VPU_API_H264_PROFILE_HIGH: return "high"; 263 | case IMX_VPU_API_H264_PROFILE_HIGH10: return "high10"; 264 | default: return ""; 265 | } 266 | } 267 | 268 | 269 | char const * imx_vpu_api_h264_level_string(ImxVpuApiH264Level level) 270 | { 271 | switch (level) 272 | { 273 | case IMX_VPU_API_H264_LEVEL_UNDEFINED: return ""; 274 | case IMX_VPU_API_H264_LEVEL_1: return "1"; 275 | case IMX_VPU_API_H264_LEVEL_1B: return "1b"; 276 | case IMX_VPU_API_H264_LEVEL_1_1: return "1.1"; 277 | case IMX_VPU_API_H264_LEVEL_1_2: return "1.2"; 278 | case IMX_VPU_API_H264_LEVEL_1_3: return "1.3"; 279 | case IMX_VPU_API_H264_LEVEL_2: return "2"; 280 | case IMX_VPU_API_H264_LEVEL_2_1: return "2.1"; 281 | case IMX_VPU_API_H264_LEVEL_2_2: return "2.2"; 282 | case IMX_VPU_API_H264_LEVEL_3: return "3"; 283 | case IMX_VPU_API_H264_LEVEL_3_1: return "3.1"; 284 | case IMX_VPU_API_H264_LEVEL_3_2: return "3.2"; 285 | case IMX_VPU_API_H264_LEVEL_4: return "4"; 286 | case IMX_VPU_API_H264_LEVEL_4_1: return "4.1"; 287 | case IMX_VPU_API_H264_LEVEL_4_2: return "4.2"; 288 | case IMX_VPU_API_H264_LEVEL_5: return "5"; 289 | case IMX_VPU_API_H264_LEVEL_5_1: return "5.1"; 290 | case IMX_VPU_API_H264_LEVEL_5_2: return "5.2"; 291 | case IMX_VPU_API_H264_LEVEL_6: return "6"; 292 | case IMX_VPU_API_H264_LEVEL_6_1: return "6.1"; 293 | case IMX_VPU_API_H264_LEVEL_6_2: return "6.2"; 294 | default: return ""; 295 | } 296 | } 297 | 298 | 299 | char const * imx_vpu_api_h265_level_string(ImxVpuApiH265Level level) 300 | { 301 | switch (level) 302 | { 303 | case IMX_VPU_API_H265_LEVEL_UNDEFINED: return ""; 304 | case IMX_VPU_API_H265_LEVEL_1: return "1"; 305 | case IMX_VPU_API_H265_LEVEL_2: return "2"; 306 | case IMX_VPU_API_H265_LEVEL_2_1: return "2.1"; 307 | case IMX_VPU_API_H265_LEVEL_3: return "3"; 308 | case IMX_VPU_API_H265_LEVEL_3_1: return "3.1"; 309 | case IMX_VPU_API_H265_LEVEL_4: return "4"; 310 | case IMX_VPU_API_H265_LEVEL_4_1: return "4.1"; 311 | case IMX_VPU_API_H265_LEVEL_5: return "5"; 312 | case IMX_VPU_API_H265_LEVEL_5_1: return "5.1"; 313 | case IMX_VPU_API_H265_LEVEL_5_2: return "5.2"; 314 | case IMX_VPU_API_H265_LEVEL_6: return "6"; 315 | case IMX_VPU_API_H265_LEVEL_6_1: return "6.1"; 316 | case IMX_VPU_API_H265_LEVEL_6_2: return "6.2"; 317 | default: return ""; 318 | } 319 | } 320 | 321 | 322 | int imx_vpu_api_vp8_profile_number(ImxVpuApiVP8Profile profile) 323 | { 324 | switch (profile) 325 | { 326 | case IMX_VPU_API_VP8_PROFILE_0: return 0; 327 | case IMX_VPU_API_VP8_PROFILE_1: return 1; 328 | case IMX_VPU_API_VP8_PROFILE_2: return 2; 329 | case IMX_VPU_API_VP8_PROFILE_3: return 3; 330 | default: return -1; 331 | } 332 | } 333 | 334 | 335 | int imx_vpu_api_vp9_profile_number(ImxVpuApiVP9Profile profile) 336 | { 337 | switch (profile) 338 | { 339 | case IMX_VPU_API_VP9_PROFILE_0: return 0; 340 | case IMX_VPU_API_VP9_PROFILE_1: return 1; 341 | case IMX_VPU_API_VP9_PROFILE_2: return 2; 342 | case IMX_VPU_API_VP9_PROFILE_3: return 3; 343 | default: return -1; 344 | } 345 | } 346 | 347 | 348 | 349 | 350 | /************************************************/ 351 | /******* DECODER STRUCTURES AND FUNCTIONS *******/ 352 | /************************************************/ 353 | 354 | 355 | char const * imx_vpu_api_dec_return_code_string(ImxVpuApiDecReturnCodes code) 356 | { 357 | switch (code) 358 | { 359 | case IMX_VPU_API_DEC_RETURN_CODE_OK: return "ok"; 360 | case IMX_VPU_API_DEC_RETURN_CODE_INVALID_PARAMS: return "invalid parameters"; 361 | case IMX_VPU_API_DEC_RETURN_CODE_DMA_MEMORY_ACCESS_ERROR: return "DMA memory access error"; 362 | case IMX_VPU_API_DEC_RETURN_CODE_UNSUPPORTED_COMPRESSION_FORMAT: return "unsupported compression format"; 363 | case IMX_VPU_API_DEC_RETURN_CODE_INVALID_EXTRA_HEADER_DATA: return "invalid extra header data"; 364 | case IMX_VPU_API_DEC_RETURN_CODE_INSUFFICIENT_STREAM_BUFFER_SIZE: return "insufficient stream buffer size"; 365 | case IMX_VPU_API_DEC_RETURN_CODE_UNSUPPORTED_BITSTREAM: return "unsupported bitstream format"; 366 | case IMX_VPU_API_DEC_RETURN_CODE_INSUFFICIENT_FRAMEBUFFERS: return "insufficient framebuffers"; 367 | case IMX_VPU_API_DEC_RETURN_CODE_INVALID_CALL: return "invalid call"; 368 | case IMX_VPU_API_DEC_RETURN_CODE_TIMEOUT: return "timeout"; 369 | case IMX_VPU_API_DEC_RETURN_CODE_ERROR: return "error"; 370 | default: return ""; 371 | } 372 | } 373 | 374 | 375 | char const * imx_vpu_api_dec_output_code_string(ImxVpuApiDecOutputCodes code) 376 | { 377 | switch (code) 378 | { 379 | case IMX_VPU_API_DEC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: return "no output yet available"; 380 | case IMX_VPU_API_DEC_OUTPUT_CODE_EOS: return "eos"; 381 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEW_STREAM_INFO_AVAILABLE: return "new stream info available"; 382 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: return "need additional framebuffer"; 383 | case IMX_VPU_API_DEC_OUTPUT_CODE_DECODED_FRAME_AVAILABLE: return "decoded frame available"; 384 | case IMX_VPU_API_DEC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: return "more input data needed"; 385 | case IMX_VPU_API_DEC_OUTPUT_CODE_FRAME_SKIPPED: return "frame skipped"; 386 | case IMX_VPU_API_DEC_OUTPUT_CODE_VIDEO_PARAMETERS_CHANGED: return "video parameters changed"; 387 | default: return ""; 388 | } 389 | } 390 | 391 | 392 | char const * imx_vpu_api_dec_skipped_frame_reason_string(ImxVpuApiDecSkippedFrameReasons reason) 393 | { 394 | switch (reason) 395 | { 396 | case IMX_VPU_API_DEC_SKIPPED_FRAME_REASON_CORRUPTED_FRAME: return "corrupted frame"; 397 | case IMX_VPU_API_DEC_SKIPPED_FRAME_REASON_INTERNAL_FRAME: return "internal frame"; 398 | default: return ""; 399 | } 400 | } 401 | 402 | 403 | 404 | 405 | /************************************************/ 406 | /******* ENCODER STRUCTURES AND FUNCTIONS *******/ 407 | /************************************************/ 408 | 409 | 410 | char const * imx_vpu_api_enc_return_code_string(ImxVpuApiEncReturnCodes code) 411 | { 412 | switch (code) 413 | { 414 | case IMX_VPU_API_ENC_RETURN_CODE_OK: return "ok"; 415 | case IMX_VPU_API_ENC_RETURN_CODE_INVALID_PARAMS: return "invalid parameters"; 416 | case IMX_VPU_API_ENC_RETURN_CODE_DMA_MEMORY_ACCESS_ERROR: return "DMA memory access error"; 417 | case IMX_VPU_API_ENC_RETURN_CODE_UNSUPPORTED_COMPRESSION_FORMAT: return "unsupported compression format"; 418 | case IMX_VPU_API_ENC_RETURN_CODE_UNSUPPORTED_COMPRESSION_FORMAT_PARAMS: return "unsupported compression format parameters"; 419 | case IMX_VPU_API_ENC_RETURN_CODE_UNSUPPORTED_COLOR_FORMAT: return "unsupported color format"; 420 | case IMX_VPU_API_ENC_RETURN_CODE_INSUFFICIENT_STREAM_BUFFER_SIZE: return "insufficient stream buffer size"; 421 | case IMX_VPU_API_ENC_RETURN_CODE_INSUFFICIENT_FRAMEBUFFERS: return "insufficient framebuffers"; 422 | case IMX_VPU_API_ENC_RETURN_CODE_FRAMES_TOO_LARGE: return "frames are too large"; 423 | case IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL: return "invalid call"; 424 | case IMX_VPU_API_ENC_RETURN_CODE_TIMEOUT: return "timeout"; 425 | case IMX_VPU_API_ENC_RETURN_CODE_ERROR: return "error"; 426 | default: return ""; 427 | } 428 | } 429 | 430 | 431 | char const * imx_vpu_api_enc_output_code_string(ImxVpuApiEncOutputCodes code) 432 | { 433 | switch (code) 434 | { 435 | case IMX_VPU_API_ENC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: return "no output yet available"; 436 | case IMX_VPU_API_ENC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: return "need additional framebuffer"; 437 | case IMX_VPU_API_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE: return "encoded frame available"; 438 | case IMX_VPU_API_ENC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: return "more input data needed"; 439 | case IMX_VPU_API_ENC_OUTPUT_CODE_EOS: return "eos"; 440 | default: return ""; 441 | } 442 | } 443 | 444 | 445 | int imx_vpu_api_vp8_partition_count_number(ImxVpuApiEncVP8PartitionCount partition_count) 446 | { 447 | switch (partition_count) 448 | { 449 | case IMX_VPU_API_ENC_VP8_PARTITION_COUNT_1: return 1; 450 | case IMX_VPU_API_ENC_VP8_PARTITION_COUNT_2: return 2; 451 | case IMX_VPU_API_ENC_VP8_PARTITION_COUNT_4: return 4; 452 | case IMX_VPU_API_ENC_VP8_PARTITION_COUNT_8: return 8; 453 | default: return -1; 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_imx6_coda_ipu.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* ipu.h #defines these types for some reason, even though these 4 | * definitions aren't used anywhere within ipu.h. However, they 5 | * do cause collisions with the datatype definitions in stdint.h, 6 | * so we must get rid of them before including the other headers. */ 7 | #ifdef uint8_t 8 | #undef uint8_t 9 | #endif 10 | #ifdef uint16_t 11 | #undef uint16_t 12 | #endif 13 | #ifdef uint32_t 14 | #undef uint32_t 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "imxvpuapi2_imx6_coda_ipu.h" 23 | 24 | 25 | static uint32_t get_ipu_pixel_format(ImxVpuApiColorFormat color_format) 26 | { 27 | switch (color_format) 28 | { 29 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT: return IPU_PIX_FMT_YUV420P; 30 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT: return IPU_PIX_FMT_NV12; 31 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV411_8BIT: return IPU_PIX_FMT_YUV410P; 32 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT: return IPU_PIX_FMT_YUV422P; 33 | case IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT: return IPU_PIX_FMT_NV16; 34 | case IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT: return IPU_PIX_FMT_YUV444P; 35 | case IMX_VPU_API_COLOR_FORMAT_YUV400_8BIT: return IPU_PIX_FMT_GREY; 36 | default: return 0; 37 | } 38 | } 39 | 40 | 41 | int imx_vpu_api_imx6_coda_open_ipu_voda_fd(void) 42 | { 43 | int ipu_vdoa_fd = open("/dev/mxc_ipu", O_RDWR, 0); 44 | if (ipu_vdoa_fd < 0) 45 | { 46 | IMX_VPU_API_ERROR("could not open /dev/mxc_ipu: %s (%d)", strerror(errno), errno); 47 | return -1; 48 | } 49 | 50 | IMX_VPU_API_TRACE("opened IPU VDOA file descriptor %d", ipu_vdoa_fd); 51 | 52 | return ipu_vdoa_fd; 53 | } 54 | 55 | 56 | void imx_vpu_api_imx6_coda_close_ipu_voda_fd(int fd) 57 | { 58 | if (fd < 0) 59 | return; 60 | 61 | close(fd); 62 | 63 | IMX_VPU_API_TRACE("closed IPU VDOA file descriptor %d", fd); 64 | } 65 | 66 | 67 | BOOL imx_vpu_api_imx6_coda_detile_and_copy_frame_with_ipu_vdoa( 68 | int ipu_vdoa_fd, 69 | ImxDmaBuffer *src_fb_dma_buffer, 70 | ImxDmaBuffer *dest_fb_dma_buffer, 71 | size_t total_padded_input_width, size_t total_padded_input_height, 72 | size_t total_padded_output_width, size_t total_padded_output_height, 73 | size_t actual_frame_width, size_t actual_frame_height, 74 | ImxVpuApiColorFormat color_format 75 | ) 76 | { 77 | int ioctl_ret; 78 | struct ipu_task task = { 0 }; 79 | 80 | imx_physical_address_t src_paddr = imx_dma_buffer_get_physical_address(src_fb_dma_buffer); 81 | imx_physical_address_t dest_paddr = imx_dma_buffer_get_physical_address(dest_fb_dma_buffer); 82 | 83 | task.overlay_en = 0; 84 | task.priority = IPU_TASK_PRIORITY_NORMAL; 85 | task.task_id = IPU_TASK_ID_ANY; 86 | task.timeout = 0; 87 | 88 | IMX_VPU_API_LOG( 89 | "ipu task: total padded input/output size %zux%zu / %zux%zu actual size %zux%zu src/dest paddr %" IMX_PHYSICAL_ADDRESS_FORMAT "/%" IMX_PHYSICAL_ADDRESS_FORMAT " output color format: %s", 90 | total_padded_input_width, total_padded_input_height, 91 | total_padded_output_width, total_padded_output_height, 92 | actual_frame_width, actual_frame_height, 93 | src_paddr, dest_paddr, 94 | imx_vpu_api_color_format_string(color_format) 95 | ); 96 | 97 | task.input.width = total_padded_input_width; 98 | task.input.height = total_padded_input_height; 99 | task.input.format = IPU_PIX_FMT_TILED_NV12; 100 | task.input.crop.pos.x = 0; 101 | task.input.crop.pos.y = 0; 102 | task.input.crop.w = total_padded_input_width; 103 | task.input.crop.h = total_padded_input_height; 104 | task.input.paddr = src_paddr; 105 | task.input.paddr_n = 0; 106 | task.input.deinterlace.enable = 0; 107 | task.input.deinterlace.motion = HIGH_MOTION; 108 | 109 | task.output.width = total_padded_output_width; 110 | task.output.height = total_padded_output_height; 111 | task.output.format = get_ipu_pixel_format(color_format); 112 | task.output.rotate = IPU_ROTATE_NONE; 113 | task.output.crop.pos.x = 0; 114 | task.output.crop.pos.y = 0; 115 | task.output.crop.w = total_padded_output_width; 116 | task.output.crop.h = total_padded_output_height; 117 | task.output.paddr = dest_paddr; 118 | 119 | if (task.output.format == 0) 120 | { 121 | IMX_VPU_API_ERROR("IPU does not support pixel format %s (%d)", imx_vpu_api_color_format_string(color_format), color_format); 122 | return FALSE; 123 | } 124 | 125 | if ((ioctl_ret = ioctl(ipu_vdoa_fd, IPU_QUEUE_TASK, &task)) == -1) 126 | { 127 | IMX_VPU_API_ERROR("queuing IPU task failed: %s (%d)", strerror(errno), errno); 128 | return FALSE; 129 | } 130 | 131 | return TRUE; 132 | } 133 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_imx6_coda_ipu.h: -------------------------------------------------------------------------------- 1 | #ifndef IMXVPUAPI2_IMX6_CODA_IPU_H 2 | #define IMXVPUAPI2_IMX6_CODA_IPU_H 3 | 4 | #include 5 | #include "imxvpuapi2.h" 6 | #include "imxvpuapi2_priv.h" 7 | 8 | 9 | int imx_vpu_api_imx6_coda_open_ipu_voda_fd(void); 10 | void imx_vpu_api_imx6_coda_close_ipu_voda_fd(int fd); 11 | 12 | BOOL imx_vpu_api_imx6_coda_detile_and_copy_frame_with_ipu_vdoa( 13 | int ipu_vdoa_fd, 14 | ImxDmaBuffer *src_fb_dma_buffer, 15 | ImxDmaBuffer *dest_fb_dma_buffer, 16 | size_t total_padded_input_width, size_t total_padded_input_height, 17 | size_t total_padded_output_width, size_t total_padded_output_height, 18 | size_t actual_frame_width, size_t actual_frame_height, 19 | ImxVpuApiColorFormat color_format 20 | ); 21 | 22 | 23 | #endif /* IMXVPUAPI2_IMX6_CODA_IPU_H */ 24 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_imx8m_hantro_dummy_encoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "imxvpuapi2.h" 3 | #include "imxvpuapi2_priv.h" 4 | 5 | 6 | static ImxVpuApiEncGlobalInfo const enc_global_info = { 7 | .flags = 0, 8 | .hardware_type = IMX_VPU_API_HARDWARE_TYPE_HANTRO, 9 | .min_required_stream_buffer_size = 0, 10 | .required_stream_buffer_physaddr_alignment = 0, 11 | .required_stream_buffer_size_alignment = 0, 12 | .supported_compression_formats = NULL, 13 | .num_supported_compression_formats = 0, 14 | }; 15 | 16 | 17 | ImxVpuApiEncGlobalInfo const * imx_vpu_api_enc_get_global_info(void) 18 | { 19 | return &enc_global_info; 20 | } 21 | 22 | 23 | ImxVpuApiCompressionFormatSupportDetails const * imx_vpu_api_enc_get_compression_format_support_details(ImxVpuApiCompressionFormat compression_format) 24 | { 25 | IMX_VPU_API_UNUSED_PARAM(compression_format); 26 | return NULL; 27 | } 28 | 29 | 30 | void imx_vpu_api_enc_set_default_open_params(ImxVpuApiCompressionFormat compression_format, ImxVpuApiColorFormat color_format, size_t frame_width, size_t frame_height, ImxVpuApiEncOpenParams *open_params) 31 | { 32 | IMX_VPU_API_UNUSED_PARAM(compression_format); 33 | IMX_VPU_API_UNUSED_PARAM(color_format); 34 | IMX_VPU_API_UNUSED_PARAM(frame_width); 35 | IMX_VPU_API_UNUSED_PARAM(frame_height); 36 | IMX_VPU_API_UNUSED_PARAM(open_params); 37 | } 38 | 39 | 40 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_open(ImxVpuApiEncoder **encoder, ImxVpuApiEncOpenParams *open_params, ImxDmaBuffer *stream_buffer) 41 | { 42 | IMX_VPU_API_UNUSED_PARAM(encoder); 43 | IMX_VPU_API_UNUSED_PARAM(open_params); 44 | IMX_VPU_API_UNUSED_PARAM(stream_buffer); 45 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 46 | } 47 | 48 | 49 | void imx_vpu_api_enc_close(ImxVpuApiEncoder *encoder) 50 | { 51 | IMX_VPU_API_UNUSED_PARAM(encoder); 52 | } 53 | 54 | 55 | ImxVpuApiEncStreamInfo const * imx_vpu_api_enc_get_stream_info(ImxVpuApiEncoder *encoder) 56 | { 57 | IMX_VPU_API_UNUSED_PARAM(encoder); 58 | return NULL; 59 | } 60 | 61 | 62 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_add_framebuffers_to_pool(ImxVpuApiEncoder *encoder, ImxDmaBuffer **fb_dma_buffers, size_t num_framebuffers) 63 | { 64 | IMX_VPU_API_UNUSED_PARAM(encoder); 65 | IMX_VPU_API_UNUSED_PARAM(fb_dma_buffers); 66 | IMX_VPU_API_UNUSED_PARAM(num_framebuffers); 67 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 68 | } 69 | 70 | 71 | void imx_vpu_api_enc_enable_drain_mode(ImxVpuApiEncoder *encoder) 72 | { 73 | IMX_VPU_API_UNUSED_PARAM(encoder); 74 | } 75 | 76 | 77 | int imx_vpu_api_enc_is_drain_mode_enabled(ImxVpuApiEncoder *encoder) 78 | { 79 | IMX_VPU_API_UNUSED_PARAM(encoder); 80 | return 0; 81 | } 82 | 83 | 84 | void imx_vpu_api_enc_flush(ImxVpuApiEncoder *encoder) 85 | { 86 | IMX_VPU_API_UNUSED_PARAM(encoder); 87 | } 88 | 89 | 90 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_set_bitrate(ImxVpuApiEncoder *encoder, unsigned int bitrate) 91 | { 92 | IMX_VPU_API_UNUSED_PARAM(encoder); 93 | IMX_VPU_API_UNUSED_PARAM(bitrate); 94 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 95 | } 96 | 97 | 98 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_set_quantization(ImxVpuApiEncoder *encoder, unsigned int quantization) 99 | { 100 | IMX_VPU_API_UNUSED_PARAM(encoder); 101 | IMX_VPU_API_UNUSED_PARAM(quantization); 102 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 103 | } 104 | 105 | 106 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_push_raw_frame(ImxVpuApiEncoder *encoder, ImxVpuApiRawFrame const *raw_frame) 107 | { 108 | IMX_VPU_API_UNUSED_PARAM(encoder); 109 | IMX_VPU_API_UNUSED_PARAM(raw_frame); 110 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 111 | } 112 | 113 | 114 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_encode(ImxVpuApiEncoder *encoder, size_t *encoded_frame_size, ImxVpuApiEncOutputCodes *output_code) 115 | { 116 | IMX_VPU_API_UNUSED_PARAM(encoder); 117 | IMX_VPU_API_UNUSED_PARAM(encoded_frame_size); 118 | IMX_VPU_API_UNUSED_PARAM(output_code); 119 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 120 | } 121 | 122 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_get_encoded_frame(ImxVpuApiEncoder *encoder, ImxVpuApiEncodedFrame *encoded_frame) 123 | { 124 | return imx_vpu_api_enc_get_encoded_frame_ext(encoder, encoded_frame, NULL); 125 | } 126 | 127 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_get_encoded_frame_ext(ImxVpuApiEncoder *encoder, ImxVpuApiEncodedFrame *encoded_frame, int *is_sync_point) 128 | { 129 | IMX_VPU_API_UNUSED_PARAM(encoder); 130 | IMX_VPU_API_UNUSED_PARAM(encoded_frame); 131 | IMX_VPU_API_UNUSED_PARAM(is_sync_point); 132 | return IMX_VPU_API_ENC_RETURN_CODE_OK; 133 | } 134 | 135 | ImxVpuApiEncReturnCodes imx_vpu_api_enc_get_skipped_frame_info(ImxVpuApiEncoder *encoder, void **context, uint64_t *pts, uint64_t *dts) 136 | { 137 | IMX_VPU_API_UNUSED_PARAM(encoder); 138 | IMX_VPU_API_UNUSED_PARAM(context); 139 | IMX_VPU_API_UNUSED_PARAM(pts); 140 | IMX_VPU_API_UNUSED_PARAM(dts); 141 | 142 | return IMX_VPU_API_ENC_RETURN_CODE_INVALID_CALL; 143 | } 144 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_jpeg.c: -------------------------------------------------------------------------------- 1 | /* Simplified API for JPEG en- and decoding with the NXP i.MX SoC 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include "imxvpuapi2_priv.h" 25 | #include "imxvpuapi2_jpeg.h" 26 | 27 | 28 | 29 | 30 | /******************************************************/ 31 | /******* MISCELLANEOUS STRUCTURES AND FUNCTIONS *******/ 32 | /******************************************************/ 33 | 34 | 35 | static void deallocate_dma_buffers(ImxDmaBuffer **dma_buffers, size_t num_dma_buffers) 36 | { 37 | size_t i; 38 | for (i = 0; i < num_dma_buffers; ++i) 39 | { 40 | if (dma_buffers[i] == NULL) 41 | continue; 42 | 43 | imx_dma_buffer_deallocate(dma_buffers[i]); 44 | dma_buffers[i] = NULL; 45 | } 46 | } 47 | 48 | 49 | static BOOL add_framebuffers_to_array(ImxDmaBuffer ***fb_dma_buffers, size_t *num_framebuffers, ImxDmaBufferAllocator *dma_buffer_allocator, size_t framebuffer_size, size_t framebuffer_alignment, size_t num_framebuffers_to_add) 50 | { 51 | BOOL ret = TRUE; 52 | size_t old_num_framebuffers; 53 | size_t new_num_framebuffers; 54 | ImxDmaBuffer **new_fb_pool_dmabuffers_array; 55 | int err; 56 | size_t i; 57 | 58 | assert(fb_dma_buffers != NULL); 59 | assert(num_framebuffers != NULL); 60 | 61 | old_num_framebuffers = *num_framebuffers; 62 | new_num_framebuffers = old_num_framebuffers + num_framebuffers_to_add; 63 | 64 | new_fb_pool_dmabuffers_array = realloc(*fb_dma_buffers, new_num_framebuffers * sizeof(ImxDmaBuffer *)); 65 | assert(new_fb_pool_dmabuffers_array != NULL); 66 | memset(new_fb_pool_dmabuffers_array + old_num_framebuffers, 0, num_framebuffers_to_add * sizeof(ImxDmaBuffer *)); 67 | 68 | *fb_dma_buffers = new_fb_pool_dmabuffers_array; 69 | *num_framebuffers = new_num_framebuffers; 70 | 71 | for (i = old_num_framebuffers; i < new_num_framebuffers; ++i) 72 | { 73 | (*fb_dma_buffers)[i] = imx_dma_buffer_allocate( 74 | dma_buffer_allocator, 75 | framebuffer_size, 76 | framebuffer_alignment, 77 | &err 78 | ); 79 | if ((*fb_dma_buffers)[i] == NULL) 80 | { 81 | IMX_VPU_API_ERROR("could not allocate DMA buffer for FB pool framebuffer: %s (%d)", strerror(err), err); 82 | ret = FALSE; 83 | goto finish; 84 | } 85 | } 86 | 87 | finish: 88 | return ret; 89 | } 90 | 91 | 92 | 93 | 94 | /****************************/ 95 | /******* JPEG DECODER *******/ 96 | /****************************/ 97 | 98 | 99 | struct _ImxVpuApiJpegDecoder 100 | { 101 | ImxVpuApiDecoder *decoder; 102 | 103 | ImxDmaBufferAllocator *dma_buffer_allocator; 104 | 105 | ImxDmaBuffer *stream_buffer; 106 | 107 | ImxVpuApiDecGlobalInfo const *global_info; 108 | ImxVpuApiDecOpenParams open_params; 109 | ImxVpuApiDecStreamInfo stream_info; 110 | 111 | ImxDmaBuffer **fb_dma_buffers; 112 | size_t num_framebuffers; 113 | 114 | ImxDmaBuffer *output_dma_buffer; 115 | ImxDmaBuffer *fb_dma_buffer_to_return; 116 | 117 | ImxVpuApiJpegDecInfo jpeg_dec_info; 118 | }; 119 | 120 | 121 | static BOOL imx_vpu_api_jpeg_dec_add_framebuffers(ImxVpuApiJpegDecoder *jpeg_decoder, size_t num_framebuffers_to_add); 122 | static void imx_vpu_api_jpeg_dec_deallocate_fb_dma_buffers(ImxVpuApiJpegDecoder *jpeg_decoder); 123 | 124 | 125 | static BOOL imx_vpu_api_jpeg_dec_add_framebuffers(ImxVpuApiJpegDecoder *jpeg_decoder, size_t num_framebuffers_to_add) 126 | { 127 | ImxVpuApiDecReturnCodes dec_ret; 128 | size_t old_num_framebuffers; 129 | 130 | if (num_framebuffers_to_add == 0) 131 | return TRUE; 132 | 133 | old_num_framebuffers = jpeg_decoder->num_framebuffers; 134 | 135 | if (!add_framebuffers_to_array( 136 | &(jpeg_decoder->fb_dma_buffers), 137 | &(jpeg_decoder->num_framebuffers), 138 | jpeg_decoder->dma_buffer_allocator, 139 | jpeg_decoder->stream_info.min_fb_pool_framebuffer_size, 140 | jpeg_decoder->stream_info.fb_pool_framebuffer_alignment, 141 | num_framebuffers_to_add 142 | )) 143 | return FALSE; 144 | 145 | dec_ret = imx_vpu_api_dec_add_framebuffers_to_pool(jpeg_decoder->decoder, &(jpeg_decoder->fb_dma_buffers[old_num_framebuffers]), NULL, num_framebuffers_to_add); 146 | if (dec_ret != IMX_VPU_API_DEC_RETURN_CODE_OK) 147 | { 148 | IMX_VPU_API_LOG("could not add framebuffers to VPU pool: %s", imx_vpu_api_dec_return_code_string(dec_ret)); 149 | return FALSE; 150 | } 151 | 152 | return TRUE; 153 | } 154 | 155 | 156 | static void imx_vpu_api_jpeg_dec_deallocate_fb_dma_buffers(ImxVpuApiJpegDecoder *jpeg_decoder) 157 | { 158 | if (jpeg_decoder->fb_dma_buffers != NULL) 159 | { 160 | deallocate_dma_buffers(jpeg_decoder->fb_dma_buffers, jpeg_decoder->num_framebuffers); 161 | jpeg_decoder->fb_dma_buffers = NULL; 162 | jpeg_decoder->num_framebuffers = 0; 163 | } 164 | } 165 | 166 | 167 | int imx_vpu_api_jpeg_dec_open(ImxVpuApiJpegDecoder **jpeg_decoder, ImxDmaBufferAllocator *dma_buffer_allocator) 168 | { 169 | int ret = TRUE; 170 | ImxVpuApiDecReturnCodes dec_ret; 171 | ImxVpuApiDecOpenParams *open_params; 172 | 173 | assert(jpeg_decoder != NULL); 174 | assert(dma_buffer_allocator != NULL); 175 | 176 | *jpeg_decoder = malloc(sizeof(ImxVpuApiJpegDecoder)); 177 | assert((*jpeg_decoder) != NULL); 178 | 179 | memset(*jpeg_decoder, 0, sizeof(ImxVpuApiJpegDecoder)); 180 | (*jpeg_decoder)->dma_buffer_allocator = dma_buffer_allocator; 181 | (*jpeg_decoder)->global_info = imx_vpu_api_dec_get_global_info(); 182 | assert((*jpeg_decoder)->global_info != NULL); 183 | 184 | (*jpeg_decoder)->stream_buffer = imx_dma_buffer_allocate( 185 | dma_buffer_allocator, 186 | (*jpeg_decoder)->global_info->min_required_stream_buffer_size, 187 | (*jpeg_decoder)->global_info->required_stream_buffer_physaddr_alignment, 188 | 0 189 | ); 190 | if ((*jpeg_decoder)->stream_buffer == NULL) 191 | goto error; 192 | 193 | open_params = &((*jpeg_decoder)->open_params); 194 | memset(open_params, 0, sizeof(ImxVpuApiDecOpenParams)); 195 | open_params->compression_format = IMX_VPU_API_COMPRESSION_FORMAT_JPEG; 196 | open_params->flags = 0; 197 | 198 | if ((dec_ret = imx_vpu_api_dec_open(&((*jpeg_decoder)->decoder), open_params, (*jpeg_decoder)->stream_buffer)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 199 | { 200 | IMX_VPU_API_ERROR("could not open JPEG decoder: %s", imx_vpu_api_dec_return_code_string(dec_ret)); 201 | goto error; 202 | } 203 | 204 | finish: 205 | return ret; 206 | 207 | error: 208 | imx_vpu_api_jpeg_dec_close(*jpeg_decoder); 209 | ret = FALSE; 210 | goto finish; 211 | } 212 | 213 | 214 | void imx_vpu_api_jpeg_dec_close(ImxVpuApiJpegDecoder *jpeg_decoder) 215 | { 216 | if (jpeg_decoder == NULL) 217 | return; 218 | 219 | if (jpeg_decoder->decoder != NULL) 220 | imx_vpu_api_dec_close(jpeg_decoder->decoder); 221 | 222 | imx_vpu_api_jpeg_dec_deallocate_fb_dma_buffers(jpeg_decoder); 223 | 224 | if (jpeg_decoder->output_dma_buffer != NULL) 225 | imx_dma_buffer_deallocate(jpeg_decoder->output_dma_buffer); 226 | 227 | if (jpeg_decoder->stream_buffer != NULL) 228 | imx_dma_buffer_deallocate(jpeg_decoder->stream_buffer); 229 | 230 | free(jpeg_decoder); 231 | } 232 | 233 | 234 | ImxVpuApiJpegDecInfo const * imx_vpu_api_jpeg_dec_decode(ImxVpuApiJpegDecoder *jpeg_decoder, uint8_t const *jpeg_data, size_t const jpeg_data_size) 235 | { 236 | ImxVpuApiDecOutputCodes output_code; 237 | ImxVpuApiDecReturnCodes dec_ret; 238 | ImxVpuApiEncodedFrame encoded_frame; 239 | BOOL do_loop = TRUE; 240 | 241 | assert(jpeg_decoder != NULL); 242 | 243 | if (jpeg_decoder->fb_dma_buffer_to_return != NULL) 244 | { 245 | imx_vpu_api_dec_return_framebuffer_to_decoder(jpeg_decoder->decoder, jpeg_decoder->fb_dma_buffer_to_return); 246 | jpeg_decoder->fb_dma_buffer_to_return = NULL; 247 | } 248 | 249 | encoded_frame.data = (uint8_t *)jpeg_data; 250 | encoded_frame.data_size = jpeg_data_size; 251 | 252 | if ((dec_ret = imx_vpu_api_dec_push_encoded_frame(jpeg_decoder->decoder, &encoded_frame)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 253 | { 254 | IMX_VPU_API_ERROR("could not push JPEG data into decoder: %s", imx_vpu_api_dec_return_code_string(dec_ret)); 255 | goto error; 256 | } 257 | 258 | jpeg_decoder->jpeg_dec_info.fb_dma_buffer = NULL; 259 | 260 | do 261 | { 262 | if ((dec_ret = imx_vpu_api_dec_decode(jpeg_decoder->decoder, &output_code)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 263 | { 264 | IMX_VPU_API_ERROR("could not decode JPEG: %s", imx_vpu_api_dec_return_code_string(dec_ret)); 265 | goto error; 266 | } 267 | 268 | switch (output_code) 269 | { 270 | case IMX_VPU_API_DEC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: 271 | break; 272 | 273 | case IMX_VPU_API_DEC_OUTPUT_CODE_EOS: 274 | do_loop = FALSE; 275 | break; 276 | 277 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEW_STREAM_INFO_AVAILABLE: 278 | { 279 | ImxVpuApiFramebufferMetrics const *fb_metrics = &(jpeg_decoder->stream_info.decoded_frame_framebuffer_metrics); 280 | ImxVpuApiDecStreamInfo const *stream_info = imx_vpu_api_dec_get_stream_info(jpeg_decoder->decoder); 281 | 282 | imx_vpu_api_jpeg_dec_deallocate_fb_dma_buffers(jpeg_decoder); 283 | 284 | jpeg_decoder->stream_info = *stream_info; 285 | jpeg_decoder->jpeg_dec_info.framebuffer_metrics = fb_metrics; 286 | jpeg_decoder->jpeg_dec_info.color_format = stream_info->color_format; 287 | jpeg_decoder->jpeg_dec_info.total_frame_size = (imx_vpu_api_is_color_format_semi_planar(stream_info->color_format) ? fb_metrics->u_offset : fb_metrics->v_offset) + fb_metrics->uv_size; 288 | 289 | if (!imx_vpu_api_jpeg_dec_add_framebuffers(jpeg_decoder, stream_info->min_num_required_framebuffers)) 290 | { 291 | IMX_VPU_API_ERROR("could not add %zu framebuffer(s) to decoder", stream_info->min_num_required_framebuffers); 292 | goto error; 293 | } 294 | 295 | if (!(jpeg_decoder->global_info->flags & IMX_VPU_API_DEC_GLOBAL_INFO_FLAG_DECODED_FRAMES_ARE_FROM_BUFFER_POOL)) 296 | { 297 | int err; 298 | 299 | if (jpeg_decoder->output_dma_buffer != NULL) 300 | { 301 | imx_dma_buffer_deallocate(jpeg_decoder->output_dma_buffer); 302 | jpeg_decoder->output_dma_buffer = NULL; 303 | } 304 | 305 | jpeg_decoder->output_dma_buffer = imx_dma_buffer_allocate( 306 | jpeg_decoder->dma_buffer_allocator, 307 | jpeg_decoder->stream_info.min_output_framebuffer_size, 308 | jpeg_decoder->stream_info.output_framebuffer_alignment, 309 | &err 310 | ); 311 | if (jpeg_decoder->output_dma_buffer == NULL) 312 | { 313 | IMX_VPU_API_ERROR("could not allocate DMA buffer for FB pool framebuffer: %s (%d)", strerror(err), err); 314 | goto error; 315 | } 316 | 317 | imx_vpu_api_dec_set_output_frame_dma_buffer(jpeg_decoder->decoder, jpeg_decoder->output_dma_buffer, NULL); 318 | } 319 | 320 | break; 321 | } 322 | 323 | case IMX_VPU_API_DEC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: 324 | { 325 | if (!imx_vpu_api_jpeg_dec_add_framebuffers(jpeg_decoder, 1)) 326 | { 327 | IMX_VPU_API_ERROR("could not add framebuffer to decoder"); 328 | goto error; 329 | } 330 | 331 | break; 332 | } 333 | 334 | case IMX_VPU_API_DEC_OUTPUT_CODE_DECODED_FRAME_AVAILABLE: 335 | { 336 | ImxVpuApiRawFrame decoded_frame; 337 | 338 | if ((dec_ret = imx_vpu_api_dec_get_decoded_frame(jpeg_decoder->decoder, &decoded_frame)) != IMX_VPU_API_DEC_RETURN_CODE_OK) 339 | { 340 | IMX_VPU_API_ERROR("imx_vpu_api_dec_get_decoded_frame() failed: %s", imx_vpu_api_dec_return_code_string(dec_ret)); 341 | goto error; 342 | } 343 | 344 | jpeg_decoder->jpeg_dec_info.fb_dma_buffer = decoded_frame.fb_dma_buffer; 345 | jpeg_decoder->fb_dma_buffer_to_return = decoded_frame.fb_dma_buffer; 346 | 347 | break; 348 | } 349 | 350 | case IMX_VPU_API_DEC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: 351 | do_loop = FALSE; 352 | break; 353 | 354 | case IMX_VPU_API_DEC_OUTPUT_CODE_FRAME_SKIPPED: 355 | break; 356 | 357 | default: 358 | IMX_VPU_API_ERROR("unknown/unhandled output code %s (%d)", imx_vpu_api_dec_output_code_string(output_code)); 359 | goto error; 360 | } 361 | } 362 | while (do_loop); 363 | 364 | return &(jpeg_decoder->jpeg_dec_info); 365 | 366 | error: 367 | return NULL; 368 | } 369 | 370 | 371 | 372 | 373 | /****************************/ 374 | /******* JPEG DECODER *******/ 375 | /****************************/ 376 | 377 | 378 | struct _ImxVpuApiJpegEncoder 379 | { 380 | ImxVpuApiEncoder *encoder; 381 | 382 | ImxDmaBufferAllocator *dma_buffer_allocator; 383 | 384 | ImxDmaBuffer *stream_buffer; 385 | 386 | ImxVpuApiEncGlobalInfo const *global_info; 387 | ImxVpuApiEncOpenParams open_params; 388 | ImxVpuApiEncStreamInfo stream_info; 389 | 390 | ImxDmaBuffer **fb_dma_buffers; 391 | size_t num_framebuffers; 392 | 393 | ImxDmaBuffer *input_dmabuffer; 394 | 395 | BOOL has_encoded_frame; 396 | }; 397 | 398 | 399 | static BOOL imx_vpu_api_jpeg_enc_open_internal(ImxVpuApiJpegEncoder *jpeg_encoder); 400 | static void imx_vpu_api_jpeg_enc_close_internal(ImxVpuApiJpegEncoder *jpeg_encoder); 401 | static BOOL imx_vpu_api_jpeg_enc_add_framebuffers(ImxVpuApiJpegEncoder *jpeg_encoder, size_t num_framebuffers_to_add); 402 | 403 | 404 | 405 | static BOOL imx_vpu_api_jpeg_enc_open_internal(ImxVpuApiJpegEncoder *jpeg_encoder) 406 | { 407 | int err; 408 | ImxVpuApiEncReturnCodes enc_ret; 409 | ImxVpuApiEncStreamInfo const *stream_info; 410 | 411 | assert(jpeg_encoder != NULL); 412 | 413 | enc_ret = imx_vpu_api_enc_open(&(jpeg_encoder->encoder), &(jpeg_encoder->open_params), jpeg_encoder->stream_buffer); 414 | if (enc_ret != IMX_VPU_API_ENC_RETURN_CODE_OK) 415 | { 416 | IMX_VPU_API_ERROR("imx_vpu_api_enc_open() failed: %s", imx_vpu_api_enc_return_code_string(enc_ret)); 417 | return FALSE; 418 | } 419 | 420 | stream_info = imx_vpu_api_enc_get_stream_info(jpeg_encoder->encoder); 421 | assert(stream_info != NULL); 422 | jpeg_encoder->stream_info = *stream_info; 423 | 424 | if (!imx_vpu_api_jpeg_enc_add_framebuffers(jpeg_encoder, stream_info->min_num_required_framebuffers)) 425 | return FALSE; 426 | 427 | jpeg_encoder->input_dmabuffer = imx_dma_buffer_allocate(jpeg_encoder->dma_buffer_allocator, stream_info->min_framebuffer_size, stream_info->framebuffer_alignment, &err); 428 | if (jpeg_encoder->input_dmabuffer == NULL) 429 | { 430 | IMX_VPU_API_ERROR("could not allocate DMA buffer for input framebuffer: %s (%d)", strerror(err), err); 431 | return FALSE; 432 | } 433 | 434 | return TRUE; 435 | } 436 | 437 | 438 | static void imx_vpu_api_jpeg_enc_close_internal(ImxVpuApiJpegEncoder *jpeg_encoder) 439 | { 440 | assert(jpeg_encoder != NULL); 441 | 442 | if (jpeg_encoder->encoder != NULL) 443 | { 444 | imx_vpu_api_enc_close(jpeg_encoder->encoder); 445 | jpeg_encoder->encoder = NULL; 446 | } 447 | 448 | if (jpeg_encoder->fb_dma_buffers != NULL) 449 | { 450 | deallocate_dma_buffers(jpeg_encoder->fb_dma_buffers, jpeg_encoder->num_framebuffers); 451 | jpeg_encoder->fb_dma_buffers = NULL; 452 | jpeg_encoder->num_framebuffers = 0; 453 | } 454 | 455 | if (jpeg_encoder->input_dmabuffer != NULL) 456 | { 457 | imx_dma_buffer_deallocate(jpeg_encoder->input_dmabuffer); 458 | jpeg_encoder->input_dmabuffer = NULL; 459 | } 460 | } 461 | 462 | 463 | static BOOL imx_vpu_api_jpeg_enc_add_framebuffers(ImxVpuApiJpegEncoder *jpeg_encoder, size_t num_framebuffers_to_add) 464 | { 465 | ImxVpuApiEncReturnCodes enc_ret; 466 | size_t old_num_framebuffers; 467 | 468 | if (num_framebuffers_to_add == 0) 469 | return TRUE; 470 | 471 | old_num_framebuffers = jpeg_encoder->num_framebuffers; 472 | 473 | if (!add_framebuffers_to_array( 474 | &(jpeg_encoder->fb_dma_buffers), 475 | &(jpeg_encoder->num_framebuffers), 476 | jpeg_encoder->dma_buffer_allocator, 477 | jpeg_encoder->stream_info.min_framebuffer_size, 478 | jpeg_encoder->stream_info.framebuffer_alignment, 479 | num_framebuffers_to_add 480 | )) 481 | return FALSE; 482 | 483 | enc_ret = imx_vpu_api_enc_add_framebuffers_to_pool(jpeg_encoder->encoder, jpeg_encoder->fb_dma_buffers + old_num_framebuffers, num_framebuffers_to_add); 484 | if (enc_ret != IMX_VPU_API_ENC_RETURN_CODE_OK) 485 | { 486 | IMX_VPU_API_LOG("could not add framebuffers to VPU pool: %s", imx_vpu_api_enc_return_code_string(enc_ret)); 487 | return FALSE; 488 | } 489 | 490 | return TRUE; 491 | } 492 | 493 | 494 | int imx_vpu_api_jpeg_enc_open(ImxVpuApiJpegEncoder **jpeg_encoder, ImxDmaBufferAllocator *dma_buffer_allocator) 495 | { 496 | int ret = TRUE; 497 | 498 | assert(jpeg_encoder != NULL); 499 | 500 | *jpeg_encoder = malloc(sizeof(ImxVpuApiJpegEncoder)); 501 | assert((*jpeg_encoder) != NULL); 502 | 503 | memset(*jpeg_encoder, 0, sizeof(ImxVpuApiJpegEncoder)); 504 | (*jpeg_encoder)->dma_buffer_allocator = dma_buffer_allocator; 505 | (*jpeg_encoder)->global_info = imx_vpu_api_enc_get_global_info(); 506 | assert((*jpeg_encoder)->global_info != NULL); 507 | 508 | (*jpeg_encoder)->stream_buffer = imx_dma_buffer_allocate( 509 | dma_buffer_allocator, 510 | (*jpeg_encoder)->global_info->min_required_stream_buffer_size, 511 | (*jpeg_encoder)->global_info->required_stream_buffer_physaddr_alignment, 512 | 0 513 | ); 514 | if ((*jpeg_encoder)->stream_buffer == NULL) 515 | goto error; 516 | 517 | finish: 518 | return ret; 519 | 520 | error: 521 | imx_vpu_api_jpeg_enc_close(*jpeg_encoder); 522 | ret = FALSE; 523 | goto finish; 524 | } 525 | 526 | 527 | void imx_vpu_api_jpeg_enc_close(ImxVpuApiJpegEncoder *jpeg_encoder) 528 | { 529 | if (jpeg_encoder == NULL) 530 | return; 531 | 532 | imx_vpu_api_jpeg_enc_close_internal(jpeg_encoder); 533 | 534 | if (jpeg_encoder->stream_buffer != NULL) 535 | imx_dma_buffer_deallocate(jpeg_encoder->stream_buffer); 536 | 537 | free(jpeg_encoder); 538 | } 539 | 540 | 541 | int imx_vpu_api_jpeg_enc_set_params(ImxVpuApiJpegEncoder *jpeg_encoder, ImxVpuApiJpegEncParams const *params) 542 | { 543 | int ret = TRUE; 544 | ImxVpuApiEncOpenParams *open_params; 545 | 546 | assert(jpeg_encoder != NULL); 547 | assert(params != NULL); 548 | 549 | open_params = &(jpeg_encoder->open_params); 550 | memset(open_params, 0, sizeof(ImxVpuApiEncOpenParams)); 551 | imx_vpu_api_enc_set_default_open_params(IMX_VPU_API_COMPRESSION_FORMAT_JPEG, params->color_format, params->frame_width, params->frame_height, open_params); 552 | /* See the ImxVpuApiCompressionFormatSupportDetails documentation for 553 | * an explanation of this calculation. */ 554 | open_params->quantization = 100 - params->quality_factor; 555 | 556 | imx_vpu_api_jpeg_enc_close_internal(jpeg_encoder); 557 | ret = imx_vpu_api_jpeg_enc_open_internal(jpeg_encoder); 558 | 559 | return ret; 560 | } 561 | 562 | 563 | ImxVpuApiFramebufferMetrics const * imx_vpu_api_jpeg_enc_get_framebuffer_metrics(ImxVpuApiJpegEncoder *jpeg_encoder) 564 | { 565 | return &(jpeg_encoder->stream_info.frame_encoding_framebuffer_metrics); 566 | } 567 | 568 | 569 | int imx_vpu_api_jpeg_enc_encode(ImxVpuApiJpegEncoder *jpeg_encoder, ImxDmaBuffer *frame_dma_buffer, size_t *encoded_data_size) 570 | { 571 | BOOL ret = TRUE; 572 | ImxVpuApiEncReturnCodes enc_ret; 573 | ImxVpuApiEncOutputCodes output_code; 574 | BOOL do_loop = TRUE; 575 | ImxVpuApiRawFrame raw_frame = { 576 | .fb_dma_buffer = frame_dma_buffer, 577 | .frame_types = { IMX_VPU_API_FRAME_TYPE_UNKNOWN, IMX_VPU_API_FRAME_TYPE_UNKNOWN } 578 | }; 579 | 580 | assert(jpeg_encoder != NULL); 581 | assert(frame_dma_buffer != NULL); 582 | assert(encoded_data_size != NULL); 583 | 584 | 585 | if ((enc_ret = imx_vpu_api_enc_push_raw_frame(jpeg_encoder->encoder, &raw_frame)) != IMX_VPU_API_ENC_RETURN_CODE_OK) 586 | { 587 | IMX_VPU_API_ERROR("could not push raw input data into encoder: %s", imx_vpu_api_enc_return_code_string(enc_ret)); 588 | goto error; 589 | } 590 | 591 | 592 | do 593 | { 594 | /* Perform an encoding step. */ 595 | if ((enc_ret = imx_vpu_api_enc_encode(jpeg_encoder->encoder, encoded_data_size, &output_code)) != IMX_VPU_API_ENC_RETURN_CODE_OK) 596 | { 597 | IMX_VPU_API_ERROR("could not encode JPEG: %s", imx_vpu_api_enc_return_code_string(enc_ret)); 598 | goto error; 599 | } 600 | 601 | IMX_VPU_API_LOG("encode step finished, output code: %s", imx_vpu_api_enc_output_code_string(output_code)); 602 | 603 | switch (output_code) 604 | { 605 | case IMX_VPU_API_ENC_OUTPUT_CODE_NO_OUTPUT_YET_AVAILABLE: 606 | break; 607 | 608 | case IMX_VPU_API_ENC_OUTPUT_CODE_NEED_ADDITIONAL_FRAMEBUFFER: 609 | { 610 | if (!imx_vpu_api_jpeg_enc_add_framebuffers(jpeg_encoder, 1)) 611 | { 612 | IMX_VPU_API_ERROR("could not add framebuffer to encoder"); 613 | goto error; 614 | } 615 | 616 | break; 617 | } 618 | 619 | case IMX_VPU_API_ENC_OUTPUT_CODE_ENCODED_FRAME_AVAILABLE: 620 | { 621 | if (jpeg_encoder->has_encoded_frame) 622 | { 623 | IMX_VPU_API_ERROR("internal error: there is already an encoded frame"); 624 | goto error; 625 | } 626 | 627 | jpeg_encoder->has_encoded_frame = TRUE; 628 | do_loop = FALSE; 629 | 630 | break; 631 | } 632 | 633 | case IMX_VPU_API_ENC_OUTPUT_CODE_MORE_INPUT_DATA_NEEDED: 634 | { 635 | if (!jpeg_encoder->has_encoded_frame) 636 | { 637 | IMX_VPU_API_ERROR("internal error: no frame encoded yet, and encoder needs more data"); 638 | goto error; 639 | } 640 | 641 | do_loop = FALSE; 642 | break; 643 | } 644 | 645 | case IMX_VPU_API_ENC_OUTPUT_CODE_EOS: 646 | { 647 | if (!jpeg_encoder->has_encoded_frame) 648 | { 649 | IMX_VPU_API_ERROR("internal error: no frame encoded yet, and encoder reported EOS"); 650 | goto error; 651 | } 652 | 653 | do_loop = FALSE; 654 | break; 655 | } 656 | 657 | default: 658 | IMX_VPU_API_ERROR("unknown/unhandled output code %s (%d)", imx_vpu_api_enc_output_code_string(output_code)); 659 | goto error; 660 | } 661 | } 662 | while (do_loop); 663 | 664 | 665 | finish: 666 | return ret; 667 | 668 | error: 669 | ret = FALSE; 670 | goto finish; 671 | } 672 | 673 | 674 | int imx_vpu_api_jpeg_enc_get_encoded_data(ImxVpuApiJpegEncoder *jpeg_encoder, void *encoded_data_dest) 675 | { 676 | ImxVpuApiEncReturnCodes enc_ret; 677 | ImxVpuApiEncodedFrame encoded_frame; 678 | 679 | assert(jpeg_encoder != NULL); 680 | assert(encoded_data_dest != NULL); 681 | 682 | encoded_frame.data = encoded_data_dest; 683 | 684 | enc_ret = imx_vpu_api_enc_get_encoded_frame(jpeg_encoder->encoder, &encoded_frame); 685 | if (enc_ret != IMX_VPU_API_ENC_RETURN_CODE_OK) 686 | { 687 | IMX_VPU_API_ERROR("could not get encoded frame: %s", imx_vpu_api_enc_return_code_string(enc_ret)); 688 | return FALSE; 689 | } 690 | 691 | jpeg_encoder->has_encoded_frame = FALSE; 692 | 693 | return TRUE; 694 | } 695 | 696 | 697 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_jpeg.h: -------------------------------------------------------------------------------- 1 | /* Simplified API for JPEG en- and decoding with the NXP i.MX SoC 2 | * Copyright (C) 2019 Carlos Rafael Giani 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 17 | * USA 18 | */ 19 | 20 | 21 | /* This is a convenience interface for simple en- and decoding of JPEG data. 22 | * For merely en/decoding JPEGs, having to set up a VPU en/decoder involves 23 | * a considerable amount of boilerplate code. This interface takes care of 24 | * these details, and presents a much simpler interface focused on this one 25 | * task: to en/decode JPEGs. */ 26 | 27 | #ifndef IMXVPUAPI2_JPEG_H 28 | #define IMXVPUAPI2_JPEG_H 29 | 30 | #include "imxvpuapi2.h" 31 | 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | 38 | /*****************************************************/ 39 | /******* JPEG DECODER STRUCTURES AND FUNCTIONS *******/ 40 | /*****************************************************/ 41 | 42 | 43 | /* Information about the result of a successful JPEG decoding. */ 44 | typedef struct 45 | { 46 | /* Metrics for the decoded frame. */ 47 | ImxVpuApiFramebufferMetrics const *framebuffer_metrics; 48 | 49 | /* DMA buffer containing the pixels of the decoded frame. */ 50 | ImxDmaBuffer *fb_dma_buffer; 51 | 52 | /* Color format of the decoded frame. */ 53 | ImxVpuApiColorFormat color_format; 54 | 55 | /* Total size of a decoded frame, in bytes. This is less than or equal to 56 | * the size of fb_dma_buffer. It is recommended to use this value instead 57 | * of the fb_dma_buffer size to determine how many bytes make up the frame, 58 | * since fb_dma_buffer may have additional internal data appended to the 59 | * frame's pixels. */ 60 | size_t total_frame_size; 61 | } 62 | ImxVpuApiJpegDecInfo; 63 | 64 | 65 | /* Opaque JPEG decoder structure. */ 66 | typedef struct _ImxVpuApiJpegDecoder ImxVpuApiJpegDecoder; 67 | 68 | 69 | /* Opens a new VPU JPEG decoder instance. 70 | * 71 | * @param jpeg_decoder Pointer to a ImxVpuApiJpegDecoder pointer that will be 72 | * set to point to the new decoder instance. Must not be NULL. 73 | * @param dma_buffer_allocator DMA buffer allocator to use for internal DMA 74 | * buffer allocations. Must not be NULL. The allocator must exist at 75 | * least until after the decoder instance was closed. 76 | * @return Nonzero if the decoder instance was set up successfully. 77 | * Zero otherwise. 78 | */ 79 | int imx_vpu_api_jpeg_dec_open(ImxVpuApiJpegDecoder **jpeg_decoder, ImxDmaBufferAllocator *dma_buffer_allocator); 80 | 81 | /* Closes a VPU JPEG decoder instance. 82 | * 83 | * After an instance was closed, it is gone and cannot be used anymore. Trying to 84 | * close the same instance multiple times results in undefined behavior. 85 | * 86 | * @param jpeg_decoder JPEG decoder instance. Must not be NULL. 87 | */ 88 | void imx_vpu_api_jpeg_dec_close(ImxVpuApiJpegDecoder *jpeg_decoder); 89 | 90 | /* Decodes JPEG data. 91 | * 92 | * Information about the decoded image is returned as a pointer to an internal 93 | * structure. This structure is read-only; do not attempt to modify its contents, 94 | * or to free() the returned pointer. The pointer is no longer to be used after 95 | * either the decoder was closed or another frame was decoded. 96 | * 97 | * If decoding failed, the decoder is not to be used anymore, and must be closed. 98 | * 99 | * @param jpeg_decoder JPEG decoder instance. Must not be NULL. 100 | * @param jpeg_data The data to decode. Must not be NULL. 101 | * @param jpeg_data_size Size of the data to decode. Must be nonzero. 102 | * @return Pointer to an ImxVpuApiJpegDecInfo struct containing information 103 | * about the decoded frame, or NULL if decoding failed. 104 | */ 105 | ImxVpuApiJpegDecInfo const * imx_vpu_api_jpeg_dec_decode(ImxVpuApiJpegDecoder *jpeg_decoder, uint8_t const *jpeg_data, size_t const jpeg_data_size); 106 | 107 | 108 | 109 | 110 | /*****************************************************/ 111 | /******* JPEG ENCODER STRUCTURES AND FUNCTIONS *******/ 112 | /*****************************************************/ 113 | 114 | 115 | typedef struct 116 | { 117 | /* Width and height of the input frame. These are the actual sizes; 118 | * they will be aligned internally if necessary. These sizes must 119 | * not be zero. */ 120 | size_t frame_width, frame_height; 121 | 122 | /* Color format of the input frame. */ 123 | ImxVpuApiColorFormat color_format; 124 | 125 | /* Quality factor for JPEG encoding. 1 = best compression, 100 = best quality. 126 | * This is the exact same quality factor as used by libjpeg. */ 127 | unsigned int quality_factor; 128 | } 129 | ImxVpuApiJpegEncParams; 130 | 131 | 132 | /* Opaque JPEG encoder structure. */ 133 | typedef struct _ImxVpuApiJpegEncoder ImxVpuApiJpegEncoder; 134 | 135 | 136 | /* Opens a new VPU JPEG encoder instance. 137 | * 138 | * @param jpeg_encoder Pointer to a ImxVpuApiJpegEncoder pointer that will be 139 | * set to point to the new encoder instance. Must not be NULL. 140 | * @param dma_buffer_allocator DMA buffer allocator to use for internal DMA 141 | * buffer allocations. Must not be NULL. The allocator must exist at 142 | * least until after the encoder instance was closed. 143 | * @return Nonzero if the encoder instance was set up successfully. 144 | * Zero otherwise. 145 | */ 146 | int imx_vpu_api_jpeg_enc_open(ImxVpuApiJpegEncoder **jpeg_encoder, ImxDmaBufferAllocator *dma_buffer_allocator); 147 | 148 | /* Closes a VPU JPEG encoder instance. 149 | * 150 | * After an instance was closed, it is gone and cannot be used anymore. Trying to 151 | * close the same instance multiple times results in undefined behavior. 152 | * 153 | * @param jpeg_decoder JPEG encoder instance. Must not be NULL. 154 | */ 155 | void imx_vpu_api_jpeg_enc_close(ImxVpuApiJpegEncoder *jpeg_encoder); 156 | 157 | /* Sets the encoding parameters. 158 | * 159 | * This needs to be called at least once (right after opening a JPEG encoder 160 | * instance). It also needs to be called whenever at least one of the parameters 161 | * change. 162 | * 163 | * Internally, this reopens the encoder. If this fails, the return value is zero. 164 | * 165 | * @param jpeg_encoder JPEG encoder instance. Must not be NULL. 166 | * @param params Pointer to a structure with encoding parameters. 167 | * Must not be NULL. 168 | * @return Nonzero if the call was successful, zero otherwise. 169 | * If it is zero, then the encoder instance cannot be used anymore, 170 | * and must be closed. 171 | */ 172 | int imx_vpu_api_jpeg_enc_set_params(ImxVpuApiJpegEncoder *jpeg_encoder, ImxVpuApiJpegEncParams const *params); 173 | 174 | /* Retrieves the required metrics for input framebuffers. 175 | * 176 | * The framebuffers that hold the frames to be encoded must be structured according 177 | * to these metrics, since the encoder expects plane offsets etc. to be just as 178 | * specified by the metrics. 179 | * 180 | * @param jpeg_encoder JPEG encoder instance. Must not be NULL. 181 | * @return Framebuffer metrics for the frames to be encoded. 182 | */ 183 | ImxVpuApiFramebufferMetrics const * imx_vpu_api_jpeg_enc_get_framebuffer_metrics(ImxVpuApiJpegEncoder *jpeg_encoder); 184 | 185 | /* Encodes a frame. 186 | * 187 | * Prior to this call, imx_vpu_api_jpeg_enc_set_params() must be called to set the 188 | * encoding parameters. It is also recommended to get the framebuffer metrics for 189 | * the input frames by calling imx_vpu_api_jpeg_enc_get_framebuffer_metrics(). 190 | * 191 | * This function does not immediately return the encoded frame. Instead, it returns 192 | * the size of the encoded frame data in bytes. This gives the caller the chance to 193 | * prepare a suitably sized buffer where the encoded data is written to by calling 194 | * imx_vpu_api_jpeg_enc_get_encoded_data() afterwards. 195 | * 196 | * This must not be called again until imx_vpu_api_jpeg_enc_get_encoded_data() was 197 | * called to retrieve the previously encoded frame. In other words, after successfully 198 | * encoding a frame, imx_vpu_api_jpeg_enc_get_encoded_data() must be called before 199 | * another frame can be encoded. 200 | * 201 | * While imx_vpu_api_jpeg_enc_get_encoded_data() must be called at least once 202 | * before calling this, it only needs to be called again if the parameters change, 203 | * that is, it does not need to be called before every frame encoding. 204 | * 205 | * @param jpeg_encoder JPEG encoder instance. Must not be NULL. 206 | * @param frame_dma_buffer DMA buffer of the framebuffer with the frame to be 207 | * encoded. Must not be NULL. 208 | * @param encoded_data_size Pointer to a variable to write the encoded data size 209 | * in bytes to. Must not be NULL. 210 | * @return Nonzero if the call was successful, zero otherwise. 211 | * If it is zero, then the encoder instance cannot be used anymore, 212 | * and must be closed. 213 | */ 214 | int imx_vpu_api_jpeg_enc_encode(ImxVpuApiJpegEncoder *jpeg_encoder, ImxDmaBuffer *frame_dma_buffer, size_t *encoded_data_size); 215 | 216 | /* Retrieves the encoded data. 217 | * 218 | * This must not be called until a frame was encoded by imx_vpu_api_jpeg_enc_encode(). 219 | * Once a frame was encoded, this function can be called to write the encoded data 220 | * to a system memory buffer pointed to by encoded_data_dest. Said buffer must be at 221 | * least as large as the value of encoded_data_size that imx_vpu_api_jpeg_enc_encode() 222 | * outputs (in bytes) to avoid buffer overflows. 223 | * 224 | * Once this was called, it must not be called again until another frame was encoded. 225 | * 226 | * @param jpeg_encoder JPEG encoder instance. Must not be NULL. 227 | * @param encoded_data_dest Pointer to the memory buffer the encoded data shall be 228 | * written to. Must not be NULL. 229 | * @return Nonzero if the call was successful, zero otherwise. 230 | * If it is zero, then the encoder instance cannot be used anymore, 231 | * and must be closed. 232 | */ 233 | int imx_vpu_api_jpeg_enc_get_encoded_data(ImxVpuApiJpegEncoder *jpeg_encoder, void *encoded_data_dest); 234 | 235 | 236 | #ifdef __cplusplus 237 | } 238 | #endif 239 | 240 | 241 | #endif 242 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_priv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "imxvpuapi2_priv.h" 5 | 6 | 7 | /* h.264 access unit delimiter data */ 8 | uint8_t const h264_aud[] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0xF0 }; 9 | size_t const h264_aud_size = sizeof(h264_aud); 10 | 11 | 12 | /* These quantization tables are from the JPEG specification, section K.1 */ 13 | 14 | uint8_t const jpeg_quantization_table_luma[64] = 15 | { 16 | 16, 11, 10, 16, 24, 40, 51, 61, 17 | 12, 12, 14, 19, 26, 58, 60, 55, 18 | 14, 13, 16, 24, 40, 57, 69, 56, 19 | 14, 17, 22, 29, 51, 87, 80, 62, 20 | 18, 22, 37, 56, 68, 109, 103, 77, 21 | 24, 35, 55, 64, 81, 104, 113, 92, 22 | 49, 64, 78, 87, 103, 121, 120, 101, 23 | 72, 92, 95, 98, 112, 100, 103, 99 24 | }; 25 | 26 | uint8_t const jpeg_quantization_table_chroma[64] = 27 | { 28 | 17, 18, 24, 47, 99, 99, 99, 99, 29 | 18, 21, 26, 66, 99, 99, 99, 99, 30 | 24, 26, 56, 99, 99, 99, 99, 99, 31 | 47, 66, 99, 99, 99, 99, 99, 99, 32 | 99, 99, 99, 99, 99, 99, 99, 99, 33 | 99, 99, 99, 99, 99, 99, 99, 99, 34 | 99, 99, 99, 99, 99, 99, 99, 99, 35 | 99, 99, 99, 99, 99, 99, 99, 99 36 | }; 37 | 38 | /* Natural order -> zig zag order 39 | * The quantization tables above are in natural order 40 | * but should be applied in zig zag order. 41 | */ 42 | uint8_t const jpeg_zigzag_pattern[64] = 43 | { 44 | 0, 1, 8, 16, 9, 2, 3, 10, 45 | 17, 24, 32, 25, 18, 11, 4, 5, 46 | 12, 19, 26, 33, 40, 48, 41, 34, 47 | 27, 20, 13, 6, 7, 14, 21, 28, 48 | 35, 42, 49, 56, 57, 50, 43, 36, 49 | 29, 22, 15, 23, 30, 37, 44, 51, 50 | 58, 59, 52, 45, 38, 31, 39, 46, 51 | 53, 60, 61, 54, 47, 55, 62, 63 52 | }; 53 | 54 | 55 | /* NOTE: The header uses big endian byte ordering. */ 56 | uint8_t const jpeg_jfif_app0_segment[JPEG_JFIF_APP0_SEGMENT_SIZE] = 57 | { 58 | /* APP0 marker. */ 59 | 0xFF, 0xE0, 60 | /* 16-bit integer containing length of segment, including the 2 bytes of this 61 | * length integer itself, but excluding the 2 bytes of the APP0 marker. */ 62 | 0x00, 63 | (JPEG_JFIF_APP0_SEGMENT_SIZE - 2), 64 | /* JFIF identifier with terminating nullbyte. */ 65 | 'J', 'F', 'I', 'F', '\0', 66 | /* JFIF version (1.01); first byte = major version, second byte = minor version. */ 67 | 0x01, 0x01, 68 | /* Byte specifying what units the pixel density values below use. 0 means that 69 | * they use _no_ units; instead, horizontal/vertical pixel density specify the 70 | * pixel aspect ratio (horizontal:vertical). */ 71 | 0x00, 72 | /* Two 16-bit integers containing horizontal and vertical pixel density, 73 | * respectively. Since the unit byte above is set to 0, these two denstity 74 | * values actually specify the pixel aspect ratio instead of a physical density. 75 | * We set both to 1 for a 1:1 density. No actual pixel aspect ratio is available 76 | * anywhere, so we use 1:1 as a default. */ 77 | 0x00, 0x01, 78 | 0x00, 0x01, 79 | /* Two bytes containing width and height of the embedded RGB thumbnail, respectively. 80 | * We don't store a thumbnail in the APP0 segment, so these two are both set to 0. */ 81 | 0x00, 82 | 0x00 83 | }; 84 | 85 | 86 | /* JPEG marker definitions, needed for JPEG header parsing */ 87 | 88 | /* Start Of Frame markers, non-differential, Huffman coding */ 89 | #define JPEG_MARKER_SOF0 0xc0 /* Baseline DCT */ 90 | #define JPEG_MARKER_SOF1 0xc1 /* Extended sequential DCT */ 91 | #define JPEG_MARKER_SOF2 0xc2 /* Progressive DCT */ 92 | #define JPEG_MARKER_SOF3 0xc3 /* Lossless */ 93 | 94 | /* Start Of Frame markers, differential, Huffman coding */ 95 | #define JPEG_MARKER_SOF5 0xc5 96 | #define JPEG_MARKER_SOF6 0xc6 97 | #define JPEG_MARKER_SOF7 0xc7 98 | 99 | /* Start Of Frame markers, non-differential, arithmetic coding */ 100 | #define JPEG_MARKER_JPG 0xc8 /* Reserved */ 101 | #define JPEG_MARKER_SOF9 0xc9 102 | #define JPEG_MARKER_SOF10 0xca 103 | #define JPEG_MARKER_SOF11 0xcb 104 | 105 | /* Start Of Frame markers, differential, arithmetic coding */ 106 | #define JPEG_MARKER_SOF13 0xcd 107 | #define JPEG_MARKER_SOF14 0xce 108 | #define JPEG_MARKER_SOF15 0xcf 109 | 110 | /* Restart interval termination */ 111 | #define JPEG_MARKER_RST0 0xd0 /* Restart ... */ 112 | #define JPEG_MARKER_RST1 0xd1 113 | #define JPEG_MARKER_RST2 0xd2 114 | #define JPEG_MARKER_RST3 0xd3 115 | #define JPEG_MARKER_RST4 0xd4 116 | #define JPEG_MARKER_RST5 0xd5 117 | #define JPEG_MARKER_RST6 0xd6 118 | #define JPEG_MARKER_RST7 0xd7 119 | 120 | #define JPEG_MARKER_SOI 0xd8 /* Start of image */ 121 | #define JPEG_MARKER_EOI 0xd9 /* End Of Image */ 122 | #define JPEG_MARKER_SOS 0xda /* Start Of Scan */ 123 | 124 | #define JPEG_MARKER_DHT 0xc4 /* Huffman Table(s) */ 125 | #define JPEG_MARKER_DAC 0xcc /* Algorithmic Coding Table */ 126 | #define JPEG_MARKER_DQT 0xdb /* Quantisation Table(s) */ 127 | #define JPEG_MARKER_DNL 0xdc /* Number of lines */ 128 | #define JPEG_MARKER_DRI 0xdd /* Restart Interval */ 129 | #define JPEG_MARKER_DHP 0xde /* Hierarchical progression */ 130 | #define JPEG_MARKER_EXP 0xdf 131 | 132 | #define JPEG_MARKER_APP0 0xe0 /* Application marker */ 133 | #define JPEG_MARKER_APP1 0xe1 134 | #define JPEG_MARKER_APP2 0xe2 135 | #define JPEG_MARKER_APP13 0xed 136 | #define JPEG_MARKER_APP14 0xee 137 | #define JPEG_MARKER_APP15 0xef 138 | 139 | #define JPEG_MARKER_JPG0 0xf0 /* Reserved ... */ 140 | #define JPEG_MARKER_JPG13 0xfd 141 | #define JPEG_MARKER_COM 0xfe /* Comment */ 142 | 143 | #define JPEG_MARKER_TEM 0x01 144 | 145 | 146 | void imx_vpu_api_insert_vp8_ivf_sequence_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height) 147 | { 148 | int i = 0; 149 | /* At this point in time, these values are unknown, so just use defaults */ 150 | uint32_t const fps_numerator = 1, fps_denominator = 1, num_frames = 0; 151 | 152 | /* DKIF signature */ 153 | header[i++] = 'D'; 154 | header[i++] = 'K'; 155 | header[i++] = 'I'; 156 | header[i++] = 'F'; 157 | 158 | /* Version number (has to be 0) */ 159 | WRITE_16BIT_LE_AND_INCR_IDX(header, i, 0); 160 | 161 | /* Size of the header, in bytes */ 162 | WRITE_16BIT_LE_AND_INCR_IDX(header, i, VP8_SEQUENCE_HEADER_SIZE); 163 | 164 | /* Codec FourCC ("VP80") */ 165 | header[i++] = 'V'; 166 | header[i++] = 'P'; 167 | header[i++] = '8'; 168 | header[i++] = '0'; 169 | 170 | /* Frame width and height, in pixels */ 171 | WRITE_16BIT_LE_AND_INCR_IDX(header, i, frame_width); 172 | WRITE_16BIT_LE_AND_INCR_IDX(header, i, frame_height); 173 | 174 | /* Frame rate numerator and denominator */ 175 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, fps_numerator); 176 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, fps_denominator); 177 | 178 | /* Number of frames */ 179 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, num_frames); 180 | 181 | /* Unused bytes */ 182 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, 0); 183 | } 184 | 185 | 186 | void imx_vpu_api_insert_vp8_ivf_frame_header(uint8_t *header, size_t main_data_size, uint64_t pts) 187 | { 188 | int i = 0; 189 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, main_data_size); 190 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, (pts >> 0) & 0xFFFFFFFF); 191 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, (pts >> 32) & 0xFFFFFFFF); 192 | } 193 | 194 | 195 | void imx_vpu_api_insert_wmv3_sequence_layer_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height, size_t main_data_size, uint8_t const *codec_data) 196 | { 197 | /* Header as specified in the VC-1 specification, Annex J and L, 198 | * L.2 , Sequence Layer. */ 199 | 200 | /* We deviate a bit from the spec here. The spec mentions a 201 | * value 0xC5 for the constant byte. Bit #6 is set in 0xC5, 202 | * and this bit #6 specifies that RCV V2 is used. We however 203 | * use RCV V1, so we have to clear that bit, leaving us with 204 | * a byte value that is 0x85, not 0xC5. */ 205 | /* XXX: The other bits of this byte are less clear. The code 206 | * from imx-vpuwrap indicates that bit #7 enables an 207 | * "extended header", while at least bits 0-2 specify a 208 | * "codec version". Nothing more is known at this point. */ 209 | uint32_t const constant_byte = 0x85; 210 | /* 0xFFFFFF is special value denoting an infinite sequence. 211 | * Since the number of frames isn't known at this point, 212 | * use that to not have to require any frame count here. */ 213 | uint32_t const num_frames = 0xFFFFFF; 214 | uint32_t const struct_c_values = (constant_byte << 24) | num_frames; 215 | uint32_t const ext_header_length = 4; 216 | 217 | int i = 0; 218 | 219 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, struct_c_values); 220 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, ext_header_length); 221 | 222 | memcpy(&(header[i]), codec_data, 4); 223 | i += 4; 224 | 225 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, frame_height); 226 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, frame_width); 227 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, main_data_size); 228 | } 229 | 230 | 231 | void imx_vpu_api_insert_wmv3_frame_layer_header(uint8_t *header, size_t main_data_size) 232 | { 233 | /* Header as specified in the VC-1 specification, Annex J and L, 234 | * L.3 , Frame Layer. This is an RCV1 frame layer header however, 235 | * not an RCV2 one (which is what the VC-1 specification describes). 236 | * RCV1 frame layer headers only contain the framesize. RCV2 ones 237 | * contain the framesize, the key frame indicator, and a timestamp. 238 | * Since we generate RCV1 headers, just store the framesize. */ 239 | WRITE_32BIT_LE(header, 0, main_data_size); 240 | } 241 | 242 | 243 | void imx_vpu_api_insert_vc1_frame_layer_header(uint8_t *header, uint8_t *main_data, size_t *actual_header_length) 244 | { 245 | static uint8_t const start_code_prefix[3] = { 0x00, 0x00, 0x01 }; 246 | 247 | /* Detect if a start code is present; if not, insert one. 248 | * Detection works according to SMPTE 421M Annex E E.2.1: 249 | * If the first two bytes are 0x00, and the third byte is 250 | * 0x01, then this is a start code. Otherwise, it isn't 251 | * one, and a frame start code is inserted. */ 252 | if (memcmp(main_data, start_code_prefix, 3) != 0) 253 | { 254 | static uint8_t const frame_start_code[4] = { 0x00, 0x00, 0x01, 0x0D }; 255 | memcpy(header, frame_start_code, 4); 256 | *actual_header_length = 4; 257 | } 258 | else 259 | *actual_header_length = 0; 260 | } 261 | 262 | 263 | void imx_vpu_api_insert_divx3_frame_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height) 264 | { 265 | int i = 0; 266 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, frame_width); 267 | WRITE_32BIT_LE_AND_INCR_IDX(header, i, frame_height); 268 | } 269 | 270 | 271 | int imx_vpu_api_parse_jpeg_header(void *jpeg_data, size_t jpeg_data_size, BOOL semi_planar_output, unsigned int *width, unsigned int *height, ImxVpuApiColorFormat *color_format) 272 | { 273 | uint8_t *jpeg_data_start = jpeg_data; 274 | uint8_t *jpeg_data_end = jpeg_data_start + jpeg_data_size; 275 | uint8_t *jpeg_data_cur = jpeg_data_start; 276 | int found_info = 0; 277 | 278 | #define READ_UINT8(value) do \ 279 | { \ 280 | (value) = *jpeg_data_cur; \ 281 | ++jpeg_data_cur; \ 282 | } \ 283 | while (0) 284 | #define READ_UINT16(value) do \ 285 | { \ 286 | uint16_t w = *((uint16_t *)jpeg_data_cur); \ 287 | jpeg_data_cur += 2; \ 288 | (value) = ( ((w & 0xff) << 8) | ((w & 0xff00) >> 8) ); \ 289 | } \ 290 | while (0) 291 | 292 | while (jpeg_data_cur < jpeg_data_end) 293 | { 294 | uint8_t marker; 295 | 296 | /* Marker is preceded by byte 0xFF */ 297 | if (*(jpeg_data_cur++) != 0xff) 298 | break; 299 | 300 | READ_UINT8(marker); 301 | if (marker == JPEG_MARKER_SOS) 302 | break; 303 | 304 | switch (marker) 305 | { 306 | case JPEG_MARKER_SOI: 307 | break; 308 | case JPEG_MARKER_DRI: 309 | jpeg_data_cur += 4; 310 | break; 311 | 312 | case JPEG_MARKER_SOF2: 313 | { 314 | IMX_VPU_API_ERROR("progressive JPEGs are not supported"); 315 | return 0; 316 | } 317 | 318 | case JPEG_MARKER_SOF0: 319 | { 320 | uint16_t length; 321 | uint8_t num_components; 322 | uint8_t block_width[3], block_height[3]; 323 | 324 | READ_UINT16(length); 325 | length -= 2; 326 | IMX_VPU_API_LOG("marker: %#lx length: %u", (unsigned long)marker, length); 327 | 328 | jpeg_data_cur++; 329 | READ_UINT16(*height); 330 | READ_UINT16(*width); 331 | 332 | if ((*width) > 8192) 333 | { 334 | IMX_VPU_API_ERROR("width of %u pixels exceeds the maximum of 8192", *width); 335 | return 0; 336 | } 337 | 338 | if ((*height) > 8192) 339 | { 340 | IMX_VPU_API_ERROR("height of %u pixels exceeds the maximum of 8192", *height); 341 | return 0; 342 | } 343 | 344 | READ_UINT8(num_components); 345 | 346 | if (num_components <= 3) 347 | { 348 | for (int i = 0; i < num_components; ++i) 349 | { 350 | uint8_t b; 351 | ++jpeg_data_cur; 352 | READ_UINT8(b); 353 | block_width[i] = (b & 0xf0) >> 4; 354 | block_height[i] = (b & 0x0f); 355 | ++jpeg_data_cur; 356 | } 357 | } 358 | 359 | if (num_components > 3) 360 | { 361 | IMX_VPU_API_ERROR("JPEGs with %d components are not supported", (int)num_components); 362 | return 0; 363 | } 364 | if (num_components == 3) 365 | { 366 | int temp = (block_width[0] * block_height[0]) / (block_width[1] * block_height[1]); 367 | 368 | if ((temp == 4) && (block_height[0] == 2)) 369 | *color_format = semi_planar_output ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV420_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV420_8BIT; 370 | else if ((temp == 2) && (block_height[0] == 1)) 371 | *color_format = semi_planar_output ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_HORIZONTAL_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_HORIZONTAL_8BIT; 372 | else if ((temp == 2) && (block_height[0] == 2)) 373 | *color_format = semi_planar_output ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV422_VERTICAL_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV422_VERTICAL_8BIT; 374 | else if ((temp == 1) && (block_height[0] == 1)) 375 | *color_format = semi_planar_output ? IMX_VPU_API_COLOR_FORMAT_SEMI_PLANAR_YUV444_8BIT : IMX_VPU_API_COLOR_FORMAT_FULLY_PLANAR_YUV444_8BIT; 376 | else 377 | *color_format = IMX_VPU_API_COLOR_FORMAT_YUV400_8BIT; 378 | } 379 | else 380 | *color_format = IMX_VPU_API_COLOR_FORMAT_YUV400_8BIT; 381 | 382 | IMX_VPU_API_LOG("width: %u height: %u number of components: %d", *width, *height, (int)num_components); 383 | 384 | found_info = 1; 385 | 386 | break; 387 | } 388 | 389 | default: 390 | { 391 | uint16_t length; 392 | READ_UINT16(length); 393 | length -= 2; 394 | IMX_VPU_API_LOG("marker: %#lx length: %u", (unsigned long)marker, length); 395 | jpeg_data_cur += length; 396 | } 397 | } 398 | } 399 | 400 | return found_info; 401 | } 402 | 403 | 404 | typedef struct 405 | { 406 | ImxVpuApiH264Level level; 407 | int max_macroblocks_per_second; 408 | int max_num_macroblocks_per_frame; 409 | /* bitrates are given in kbps */ 410 | int max_bitrate_for_baseline_extended_main; 411 | int max_bitrate_for_high; 412 | int max_bitrate_for_high10; 413 | } 414 | H264LevelTableItem; 415 | 416 | /* The items in this table are found in the spec ISO/IEC 14496-10 Table A-1 */ 417 | static H264LevelTableItem const h264_level_table[] = { 418 | { IMX_VPU_API_H264_LEVEL_1, 1485, 99, 64, 80, 192 }, 419 | { IMX_VPU_API_H264_LEVEL_1B, 1485, 99, 128, 160, 384 }, 420 | { IMX_VPU_API_H264_LEVEL_1_1, 3000, 396, 192, 240, 576 }, 421 | { IMX_VPU_API_H264_LEVEL_1_2, 6000, 396, 384, 480, 1152 }, 422 | { IMX_VPU_API_H264_LEVEL_1_3, 11880, 396, 768, 960, 2304 }, 423 | { IMX_VPU_API_H264_LEVEL_2, 11880, 396, 2000, 2500, 6000 }, 424 | { IMX_VPU_API_H264_LEVEL_2_1, 19800, 792, 4000, 5000, 12000 }, 425 | { IMX_VPU_API_H264_LEVEL_2_2, 20250, 1620, 4000, 5000, 12000 }, 426 | { IMX_VPU_API_H264_LEVEL_3, 40500, 1620, 10000, 12500, 30000 }, 427 | { IMX_VPU_API_H264_LEVEL_3_1, 108000, 3600, 14000, 17500, 42000 }, 428 | { IMX_VPU_API_H264_LEVEL_3_2, 216000, 5120, 20000, 25000, 60000 }, 429 | { IMX_VPU_API_H264_LEVEL_4, 245760, 8192, 20000, 25000, 60000 }, 430 | { IMX_VPU_API_H264_LEVEL_4_1, 245760, 8192, 50000, 50000, 150000 }, 431 | { IMX_VPU_API_H264_LEVEL_4_2, 522240, 8704, 50000, 50000, 150000 }, 432 | { IMX_VPU_API_H264_LEVEL_5, 589824, 22080, 135000, 168750, 405000 }, 433 | { IMX_VPU_API_H264_LEVEL_5_1, 983040, 36864, 240000, 300000, 720000 }, 434 | { IMX_VPU_API_H264_LEVEL_6, 4177920, 139264, 240000, 240000, 240000 }, 435 | { IMX_VPU_API_H264_LEVEL_6_1, 8355840, 139264, 480000, 480000, 480000 }, 436 | { IMX_VPU_API_H264_LEVEL_6_2, 16711680, 139264, 800000, 800000, 800000 }, 437 | }; 438 | static int const h264_level_table_size = sizeof(h264_level_table) / sizeof(H264LevelTableItem); 439 | 440 | 441 | ImxVpuApiH264Level imx_vpu_api_estimate_max_h264_level(int width, int height, int bitrate, int fps_num, int fps_denom, ImxVpuApiH264Profile profile) 442 | { 443 | int num_mb_per_frame, num_mb_per_second; 444 | 445 | /* One macroblock consists of 16 x 16 pixels */ 446 | num_mb_per_frame = (width * height) / (16 * 16); 447 | num_mb_per_second = num_mb_per_frame * fps_num / fps_denom; 448 | 449 | for (int i = 0; i < h264_level_table_size; ++i) 450 | { 451 | H264LevelTableItem const *item = &(h264_level_table[i]); 452 | int max_bitrate = 0; 453 | 454 | switch (profile) 455 | { 456 | case IMX_VPU_API_H264_PROFILE_CONSTRAINED_BASELINE: 457 | case IMX_VPU_API_H264_PROFILE_BASELINE: 458 | case IMX_VPU_API_H264_PROFILE_MAIN: 459 | max_bitrate = item->max_bitrate_for_baseline_extended_main; 460 | break; 461 | case IMX_VPU_API_H264_PROFILE_HIGH: 462 | max_bitrate = item->max_bitrate_for_high; 463 | break; 464 | case IMX_VPU_API_H264_PROFILE_HIGH10: 465 | max_bitrate = item->max_bitrate_for_high10; 466 | break; 467 | default: 468 | /* we should never reach this */ 469 | assert(FALSE); 470 | } 471 | 472 | if ((num_mb_per_frame <= item->max_num_macroblocks_per_frame) && 473 | (num_mb_per_second <= item->max_macroblocks_per_second) && 474 | (bitrate <= max_bitrate)) 475 | return item->level; 476 | } 477 | 478 | return IMX_VPU_API_H264_LEVEL_UNDEFINED; 479 | } 480 | 481 | 482 | ImxVpuApiH265Level imx_vpu_api_estimate_max_h265_level(int width, int height, int bitrate, int fps_num, int fps_denom, ImxVpuApiH265Profile profile) 483 | { 484 | // TODO 485 | } 486 | -------------------------------------------------------------------------------- /imxvpuapi2/imxvpuapi2_priv.h: -------------------------------------------------------------------------------- 1 | #ifndef IMXVPUAPI2_PRIV_H 2 | #define IMXVPUAPI2_PRIV_H 3 | 4 | #include 5 | #include "imxvpuapi2.h" 6 | 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | 13 | #ifndef TRUE 14 | #define TRUE (1) 15 | #endif 16 | 17 | 18 | #ifndef FALSE 19 | #define FALSE (0) 20 | #endif 21 | 22 | 23 | #ifndef BOOL 24 | #define BOOL int 25 | #endif 26 | 27 | 28 | #define IMX_VPU_API_UNUSED_PARAM(x) ((void)(x)) 29 | 30 | 31 | #define IMX_VPU_API_ALIGN_VAL_TO(LENGTH, ALIGN_SIZE) ( ((uintptr_t)(((uint8_t*)(LENGTH)) + (ALIGN_SIZE) - 1) / (ALIGN_SIZE)) * (ALIGN_SIZE) ) 32 | 33 | 34 | #define IMX_VPU_API_ERROR_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_ERROR) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_ERROR, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 35 | #define IMX_VPU_API_WARNING_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_WARNING) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_WARNING, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 36 | #define IMX_VPU_API_INFO_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_INFO) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_INFO, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 37 | #define IMX_VPU_API_DEBUG_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_DEBUG) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_DEBUG, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 38 | #define IMX_VPU_API_LOG_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_LOG) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_LOG, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 39 | #define IMX_VPU_API_TRACE_FULL(FILE_, LINE_, FUNCTION_, ...) do { if (imx_vpu_api_cur_log_level_threshold >= IMX_VPU_API_LOG_LEVEL_TRACE) { imx_vpu_api_cur_logging_fn(IMX_VPU_API_LOG_LEVEL_TRACE, FILE_, LINE_, FUNCTION_, __VA_ARGS__); } } while(0) 40 | 41 | 42 | #define IMX_VPU_API_ERROR(...) IMX_VPU_API_ERROR_FULL (__FILE__, __LINE__, __func__, __VA_ARGS__) 43 | #define IMX_VPU_API_WARNING(...) IMX_VPU_API_WARNING_FULL(__FILE__, __LINE__, __func__, __VA_ARGS__) 44 | #define IMX_VPU_API_INFO(...) IMX_VPU_API_INFO_FULL (__FILE__, __LINE__, __func__, __VA_ARGS__) 45 | #define IMX_VPU_API_DEBUG(...) IMX_VPU_API_DEBUG_FULL (__FILE__, __LINE__, __func__, __VA_ARGS__) 46 | #define IMX_VPU_API_LOG(...) IMX_VPU_API_LOG_FULL (__FILE__, __LINE__, __func__, __VA_ARGS__) 47 | #define IMX_VPU_API_TRACE(...) IMX_VPU_API_TRACE_FULL (__FILE__, __LINE__, __func__, __VA_ARGS__) 48 | 49 | 50 | extern ImxVpuApiLogLevel imx_vpu_api_cur_log_level_threshold; 51 | extern ImxVpuApiLoggingFunc imx_vpu_api_cur_logging_fn; 52 | 53 | 54 | #define VP8_SEQUENCE_HEADER_SIZE 32 55 | #define VP8_FRAME_HEADER_SIZE 12 56 | 57 | #define WMV3_RCV_SEQUENCE_LAYER_HEADER_SIZE (6 * 4) 58 | #define WMV3_RCV_FRAME_LAYER_HEADER_SIZE 4 59 | 60 | #define VC1_NAL_FRAME_LAYER_HEADER_MAX_SIZE 4 61 | 62 | #define DIVX3_FRAME_HEADER_SIZE (4 + 4) 63 | 64 | #define WEBP_FRAME_HEADER_SIZE 20 65 | 66 | /* JFIF APP0 segment size (16 bytes), including the size (2 bytes) of its marker */ 67 | #define JPEG_JFIF_APP0_SEGMENT_SIZE (16+2) 68 | 69 | 70 | extern uint8_t const h264_aud[]; 71 | extern size_t const h264_aud_size; 72 | 73 | 74 | extern uint8_t const jpeg_quantization_table_luma[64]; 75 | extern uint8_t const jpeg_quantization_table_chroma[64]; 76 | extern uint8_t const jpeg_zigzag_pattern[64]; 77 | extern uint8_t const jpeg_jfif_app0_segment[JPEG_JFIF_APP0_SEGMENT_SIZE]; 78 | 79 | 80 | #define READ_16BIT_BE(BUF, OFS) \ 81 | ( (((uint16_t)((BUF)[(OFS) + 0])) << 8) \ 82 | | (((uint16_t)((BUF)[(OFS) + 1])) << 0) ) 83 | 84 | #define READ_32BIT_BE(BUF, OFS) \ 85 | ( (((uint32_t)((BUF)[(OFS) + 0])) << 24) \ 86 | | (((uint32_t)((BUF)[(OFS) + 1])) << 16) \ 87 | | (((uint32_t)((BUF)[(OFS) + 2])) << 8) \ 88 | | (((uint32_t)((BUF)[(OFS) + 3])) << 0) ) 89 | 90 | #define READ_32BIT_LE(BUF, OFS) \ 91 | ( (((uint32_t)((BUF)[(OFS) + 0])) << 0) \ 92 | | (((uint32_t)((BUF)[(OFS) + 1])) << 8) \ 93 | | (((uint32_t)((BUF)[(OFS) + 2])) << 16) \ 94 | | (((uint32_t)((BUF)[(OFS) + 3])) << 24) ) 95 | 96 | 97 | #define WRITE_16BIT_LE(BUF, OFS, VALUE) \ 98 | do \ 99 | { \ 100 | (BUF)[(OFS) + 0] = ((VALUE) >> 0) & 0xFF; \ 101 | (BUF)[(OFS) + 1] = ((VALUE) >> 8) & 0xFF; \ 102 | } \ 103 | while (0) 104 | 105 | 106 | #define WRITE_16BIT_LE_AND_INCR_IDX(BUF, IDX, VALUE) \ 107 | do \ 108 | { \ 109 | (BUF)[(IDX)++] = ((VALUE) >> 0) & 0xFF; \ 110 | (BUF)[(IDX)++] = ((VALUE) >> 8) & 0xFF; \ 111 | } \ 112 | while (0) 113 | 114 | 115 | #define WRITE_32BIT_LE(BUF, OFS, VALUE) \ 116 | do \ 117 | { \ 118 | (BUF)[(OFS) + 0] = ((VALUE) >> 0) & 0xFF; \ 119 | (BUF)[(OFS) + 1] = ((VALUE) >> 8) & 0xFF; \ 120 | (BUF)[(OFS) + 2] = ((VALUE) >> 16) & 0xFF; \ 121 | (BUF)[(OFS) + 3] = ((VALUE) >> 24) & 0xFF; \ 122 | } \ 123 | while (0) 124 | 125 | 126 | #define WRITE_32BIT_LE_AND_INCR_IDX(BUF, IDX, VALUE) \ 127 | do \ 128 | { \ 129 | (BUF)[(IDX)++] = ((VALUE) >> 0) & 0xFF; \ 130 | (BUF)[(IDX)++] = ((VALUE) >> 8) & 0xFF; \ 131 | (BUF)[(IDX)++] = ((VALUE) >> 16) & 0xFF; \ 132 | (BUF)[(IDX)++] = ((VALUE) >> 24) & 0xFF; \ 133 | } \ 134 | while (0) 135 | 136 | 137 | #define FOURCC_FORMAT "c%c%c%c" 138 | 139 | #define FOURCC_PTR_ARGS(FOURCC) \ 140 | ((char)(((uint8_t const *)(FOURCC))[0])) \ 141 | , ((char)(((uint8_t const *)(FOURCC))[1])) \ 142 | , ((char)(((uint8_t const *)(FOURCC))[2])) \ 143 | , ((char)(((uint8_t const *)(FOURCC))[3])) 144 | 145 | 146 | void imx_vpu_api_insert_vp8_ivf_sequence_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height); 147 | void imx_vpu_api_insert_vp8_ivf_frame_header(uint8_t *header, size_t main_data_size, uint64_t pts); 148 | 149 | void imx_vpu_api_insert_wmv3_sequence_layer_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height, size_t main_data_size, uint8_t const *codec_data); 150 | void imx_vpu_api_insert_wmv3_frame_layer_header(uint8_t *header, size_t main_data_size); 151 | 152 | void imx_vpu_api_insert_vc1_frame_layer_header(uint8_t *header, uint8_t *main_data, size_t *actual_header_length); 153 | 154 | void imx_vpu_api_insert_divx3_frame_header(uint8_t *header, unsigned int frame_width, unsigned int frame_height); 155 | 156 | int imx_vpu_api_parse_jpeg_header(void *jpeg_data, size_t jpeg_data_size, BOOL semi_planar_output, unsigned int *width, unsigned int *height, ImxVpuApiColorFormat *color_format); 157 | 158 | ImxVpuApiH264Level imx_vpu_api_estimate_max_h264_level(int width, int height, int bitrate, int fps_num, int fps_denom, ImxVpuApiH264Profile profile); 159 | ImxVpuApiH265Level imx_vpu_api_estimate_max_h265_level(int width, int height, int bitrate, int fps_num, int fps_denom, ImxVpuApiH265Profile profile); 160 | 161 | 162 | #ifdef __cplusplus 163 | } 164 | #endif 165 | 166 | 167 | #endif /* IMXVPUAPI2_PRIV_H */ 168 | -------------------------------------------------------------------------------- /libimxvpuapi2.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libimxvpuapi2 7 | Description: interface library for i.MX VPU devices 8 | Version: @IMXVPUAPI2_VERSION@ 9 | Libs: -L${libdir} -limxvpuapi2 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freescale/libimxvpuapi/37095a854aa176bb763a25ce98ceb6a787501271/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext, Logs 5 | import os 6 | 7 | top = '.' 8 | out = 'build' 9 | 10 | 11 | # the code inside fragment deliberately does an unsafe implicit cast float->char to trigger a 12 | # compiler warning; sometimes, gcc does not tell about an unsupported parameter *unless* the 13 | # code being compiled causes a warning 14 | c_cflag_check_code = """ 15 | int main() 16 | { 17 | float f = 4.0; 18 | char c = f; 19 | return c - 4; 20 | } 21 | """ 22 | def check_compiler_flag(conf, flag, mandatory = 0): 23 | return conf.check(fragment = c_cflag_check_code, mandatory = mandatory, execute = 0, define_ret = 0, msg = 'Checking for compiler switch %s' % flag, cflags = [flag], okmsg = 'yes', errmsg = 'no') 24 | 25 | def check_combined_build_flags(conf, cflags, ldflags, mandatory = 0): 26 | if ldflags: 27 | Logs.pprint('NORMAL', 'Now checking the combination of CFLAGS %s and LDFLAGS %s' % (' '.join(cflags), ' '.join(ldflags))) 28 | else: 29 | Logs.pprint('NORMAL', 'Now checking the combination of CFLAGS %s' % ' '.join(cflags)) 30 | return conf.check(fragment = c_cflag_check_code, mandatory = mandatory, execute = 0, define_ret = 0, msg = 'Checking if this combination works', cflags = cflags, ldflags = ldflags, okmsg = 'yes', errmsg = 'no') 31 | 32 | 33 | class PlatformIMX6: 34 | description = 'i.MX6 with Chips&Media CODA960 codec as VPU' 35 | 36 | def configure(self, conf): 37 | conf.check_cc(lib = 'vpu', uselib_store = 'CODA960', define_name = '', mandatory = 1) 38 | with_sof_stuff = conf.check_cc(fragment = ''' 39 | #include 40 | int main() { 41 | return ENC_ENABLE_SOF_STUFF * 0; 42 | } 43 | ''', 44 | uselib = 'CODA960', 45 | mandatory = False, 46 | execute = False, 47 | define_name = '', 48 | msg = 'checking if ENC_ENABLE_SOF_STUFF exists' 49 | ) 50 | if with_sof_stuff: 51 | conf.define('HAVE_IMXVPUENC_ENABLE_SOF_STUFF', 1) 52 | 53 | imx_linux_headers_path = conf.options.imx_headers 54 | if not imx_linux_headers_path: 55 | imx_linux_headers_path = os.path.join(conf.options.sysroot_path, 'usr/include/imx') 56 | Logs.pprint('NORMAL', 'i.MX linux headers path: %s' % imx_linux_headers_path) 57 | conf.env['INCLUDES_IMXHEADERS'] = [imx_linux_headers_path] 58 | 59 | if not conf.check_cc(fragment = ''' 60 | #include 61 | #include 62 | #include 63 | 64 | int main() { return 0; } 65 | ''', 66 | uselib_store = 'CODA960', 67 | uselib = ['IMXHEADERS', 'GNU99'], 68 | mandatory = False, 69 | execute = False, 70 | define_name = '', 71 | msg = 'checking for the IPU header linux/ipu.h' 72 | ): 73 | conf.fatal('Cannot build CODA960 backend: IPU header linux/ipu.h not found (needed for combined frame copying / de-tiling)') 74 | 75 | def build(self, bld): 76 | bld( 77 | features = ['c'], 78 | includes = ['.'], 79 | cflags = ['-Wno-pedantic'], 80 | uselib = ['IMXDMABUFFER', 'GNU99', 'IMXHEADERS'], 81 | source = ['imxvpuapi2/imxvpuapi2_imx6_coda_ipu.c'], 82 | name = 'imx6_coda_ipu' 83 | ) 84 | bld( 85 | features = ['c'], 86 | includes = ['.'], 87 | uselib = ['IMXDMABUFFER', 'C99', 'CODA960'], 88 | source = ['imxvpuapi2/imxvpuapi2_imx6_coda.c'], 89 | name = 'imx6_coda' 90 | ) 91 | 92 | return { 93 | 'uselib': ['CODA960'], 94 | 'use': ['imx6_coda_ipu', 'imx6_coda'] 95 | } 96 | 97 | 98 | class PlatformIMX8M: 99 | description = 'i.MX8 M / M mini / M plus with Hantro G1/G2 decoder (optionally also a Hantro H1 or Hantro VC8000E encoder)' 100 | 101 | def __init__(self, soc_type): 102 | self.soc_type = soc_type 103 | 104 | def configure(self, conf): 105 | sysroot_path = conf.env['SYSROOT'] 106 | conf.env['CFLAGS_HANTRO'] += ['-pthread'] 107 | conf.env['LINKFLAGS_HANTRO'] += ['-pthread'] 108 | conf.check_cc(uselib_store = 'HANTRO', uselib = 'HANTRO', define_name = '', mandatory = 1, lib = 'hantro') 109 | conf.check_cc(uselib_store = 'HANTRO', uselib = 'HANTRO', define_name = '', mandatory = 1, lib = 'codec') 110 | conf.env['DEFINES_HANTRO'] += ['SET_OUTPUT_CROP_RECT', 'USE_EXTERNAL_BUFFER', 'VSI_API', 'ENABLE_CODEC_VP8'] 111 | 112 | conf.check_cc(uselib_store = 'HANTRO_DEC', uselib = 'HANTRO', define_name = '', mandatory = 1, includes = [os.path.join(sysroot_path, 'usr/include/hantro_dec')], header_name = 'dwl.h') 113 | conf.check_cc(uselib_store = 'HANTRO_DEC', uselib = 'HANTRO', define_name = '', mandatory = 1, includes = [os.path.join(sysroot_path, 'usr/include/hantro_dec')], header_name = 'codec.h') 114 | 115 | if self.soc_type == 'MX8MM': 116 | # i.MX8m mini has the Hantro H1 encoder 117 | conf.define('IMXVPUAPI2_VPU_HAS_H1_ENCODER', 1) 118 | conf.check_cc(uselib_store = 'HANTRO', uselib = 'HANTRO', define_name = '', mandatory = 1, lib = 'hantro_h1') 119 | conf.check_cc(uselib_store = 'HANTRO', uselib = 'HANTRO', define_name = '', mandatory = 1, lib = 'codec_enc') 120 | conf.env['DEFINES_HANTRO_ENC'] += ['ENCH1', 'OMX_ENCODER_VIDEO_DOMAIN', 'ENABLE_HANTRO_ENC'] 121 | conf.check_cc(uselib_store = 'HANTRO_ENC', uselib = 'HANTRO', define_name = '', mandatory = 1, includes = [os.path.join(sysroot_path, 'usr/include/hantro_enc'), os.path.join(sysroot_path, 'usr/include/hantro_enc/headers')], header_name = 'encoder/codec.h') 122 | elif self.soc_type == 'MX8MP': 123 | # i.MX8m plus has the Hantro VC8000E encoder 124 | conf.define('IMXVPUAPI2_VPU_HAS_VC8000E_ENCODER', 1) 125 | conf.check_cc(uselib_store = 'HANTRO', uselib = 'HANTRO', define_name = '', mandatory = 1, lib = ['hantro_vc8000e', 'm']) 126 | conf.check_cc(uselib_store = 'HANTRO_ENC', uselib = 'HANTRO', define_name = '', mandatory = 1, includes = [os.path.join(sysroot_path, 'usr/include')], header_name = 'hantro_VC8000E_enc/hevcencapi.h') 127 | 128 | with_hantro_codec_error_frame_retval = conf.check_cc(fragment = ''' 129 | #include "dwl.h" 130 | #include "codec.h" 131 | int main() { 132 | return CODEC_ERROR_FRAME * 0; 133 | } 134 | ''', 135 | uselib = ['C99', 'HANTRO', 'HANTRO_DEC'], 136 | mandatory = False, 137 | execute = False, 138 | define_name = '', 139 | msg = 'checking if CODEC_ERROR_FRAME exists' 140 | ) 141 | if with_hantro_codec_error_frame_retval: 142 | conf.define('HAVE_IMXVPUDEC_HANTRO_CODEC_ERROR_FRAME', 1) 143 | 144 | conf.define('IMXVPUAPI_IMX8_SOC_TYPE_' + self.soc_type, 1) 145 | 146 | def build(self, bld): 147 | bld( 148 | features = ['c'], 149 | includes = ['.'], 150 | uselib = ['IMXDMABUFFER', 'C99', 'HANTRO', 'HANTRO_DEC'], 151 | source = ['imxvpuapi2/imxvpuapi2_imx8m_hantro_decoder.c'], 152 | name = 'imx8_decoder' 153 | ) 154 | if self.soc_type == 'MX8MM': 155 | bld( 156 | features = ['c'], 157 | includes = ['.'], 158 | uselib = ['IMXDMABUFFER', 'C99', 'HANTRO', 'HANTRO_ENC'], 159 | source = ['imxvpuapi2/imxvpuapi2_imx8m_hantro_h1_encoder.c'], 160 | name = 'imx8_encoder' 161 | ) 162 | elif self.soc_type == 'MX8MP': 163 | bld( 164 | features = ['c'], 165 | includes = ['.'], 166 | uselib = ['IMXDMABUFFER', 'C99', 'HANTRO', 'HANTRO_ENC'], 167 | source = ['imxvpuapi2/imxvpuapi2_imx8m_hantro_vc8000_encoder.c'], 168 | name = 'imx8_encoder' 169 | ) 170 | else: 171 | bld( 172 | features = ['c'], 173 | includes = ['.'], 174 | uselib = ['IMXDMABUFFER', 'C99'], 175 | source = ['imxvpuapi2/imxvpuapi2_imx8m_hantro_dummy_encoder.c'], 176 | name = 'imx8_encoder' 177 | ) 178 | 179 | return { 180 | 'uselib': ['HANTRO', 'HANTRO_DEC', 'HANTRO_ENC'], 181 | 'use': ['imx8_decoder', 'imx8_encoder'] 182 | } 183 | 184 | 185 | imx_platforms = { 186 | 'imx6': PlatformIMX6(), 187 | 'imx8m': PlatformIMX8M(soc_type = 'MX8M'), 188 | 'imx8mm': PlatformIMX8M(soc_type = 'MX8MM'), 189 | 'imx8mp': PlatformIMX8M(soc_type = 'MX8MP') 190 | } 191 | 192 | 193 | def options(opt): 194 | opt.add_option('--enable-debug', action = 'store_true', default = False, help = 'enable debug build [default: disabled]') 195 | opt.add_option('--enable-static', action = 'store_true', default = False, help = 'build static library [default: build shared library]') 196 | opt.add_option('--imx-platform', action='store', default='', help='i.MX platform to build for (valid platforms: ' + ' '.join(imx_platforms.keys()) + ')') 197 | opt.add_option('--imx-headers', action='store', default='', help='path to where linux/ipu.h etc. can be found [default: /usr/include/imx]') 198 | opt.add_option('--sysroot-path', action='store', default='', help='path to the sysroot') 199 | opt.add_option('--disable-examples', action = 'store_true', default = False, help = 'do not compile examples [default: build examples]') 200 | opt.load('compiler_c') 201 | opt.load('gnu_dirs') 202 | 203 | 204 | def configure(conf): 205 | conf.load('compiler_c') 206 | conf.load('gnu_dirs') 207 | 208 | # check and add compiler flags 209 | 210 | basic_cflags = conf.env['CFLAGS'] or [] 211 | basic_ldflags = conf.env['LINKFLAGS'] or [] 212 | 213 | basic_cflags += ['-Wextra', '-Wall', '-pedantic', '-fPIC', '-DPIC'] 214 | if conf.options.enable_debug: 215 | basic_cflags += ['-O0', '-g3', '-ggdb'] 216 | else: 217 | basic_cflags += ['-O2'] 218 | 219 | for cflag in basic_cflags: 220 | check_compiler_flag(conf, cflag, 1) 221 | for std in ['gnu99', 'c99']: 222 | check_compiler_flag(conf, '-std=' + std) 223 | check_combined_build_flags(conf, basic_cflags + ['-std=' + std], basic_ldflags, 1) 224 | conf.env['CFLAGS_' + std.upper()] = ['-std=' + std] 225 | 226 | 227 | conf.env['CFLAGS'] = basic_cflags 228 | conf.env['LINKFLAGS'] = basic_ldflags 229 | conf.env['BUILD_STATIC'] = conf.options.enable_static 230 | conf.env['DISABLE_EXAMPLES'] = conf.options.disable_examples 231 | 232 | 233 | # check libimxdmabuffer dependency 234 | conf.check_cfg(package = 'libimxdmabuffer >= 1.1.1', uselib_store = 'IMXDMABUFFER', define_name = '', args = '--cflags --libs', mandatory = 1) 235 | 236 | 237 | # check sysroot path 238 | if not conf.options.sysroot_path: 239 | conf.fatal('Sysroot path not set; add --sysroot-path switch to configure command line') 240 | sysroot_path = os.path.abspath(os.path.expanduser(conf.options.sysroot_path)) 241 | if os.path.isdir(sysroot_path): 242 | Logs.pprint('NORMAL', 'Using "%s" as sysroot path' % sysroot_path) 243 | else: 244 | conf.fatal('Path "%s" does not exist or is not a valid directory; cannot use as sysroot path' % sysroot_path) 245 | conf.env['SYSROOT'] = sysroot_path 246 | 247 | 248 | # check i.MX platform 249 | if not conf.options.imx_platform: 250 | conf.fatal('i.MX platform not defined; add --imx-platform switch to configure command line') 251 | imx_platform_id = conf.options.imx_platform 252 | try: 253 | imx_platform = imx_platforms[imx_platform_id] 254 | except KeyError: 255 | conf.fatal('Invalid i.MX platform "%s" specified; valid platforms: %s' % (imx_platform_id, ' '.join(imx_platforms.keys()))) 256 | conf.env['IMX_PLATFORM'] = imx_platform_id 257 | 258 | 259 | # configure platform 260 | imx_platform.configure(conf) 261 | 262 | 263 | # process the library version number 264 | version_node = conf.srcnode.find_node('VERSION') 265 | if not version_node: 266 | conf.fatal('Could not open VERSION file') 267 | with open(version_node.abspath()) as x: 268 | version = x.readline().splitlines()[0] 269 | conf.env['IMXVPUAPI2_VERSION'] = version 270 | conf.define('IMXVPUAPI2_VERSION', version) 271 | Logs.pprint('NORMAL', 'libimxvpuapi version %s' % version) 272 | 273 | 274 | # write the config header 275 | conf.write_config_header('config.h') 276 | 277 | 278 | def build(bld): 279 | imx_platform_id = bld.env['IMX_PLATFORM'] 280 | imx_platform = imx_platforms[imx_platform_id] 281 | 282 | use_lists = imx_platform.build(bld) 283 | 284 | bld( 285 | features = ['c', 'cstlib' if bld.env['BUILD_STATIC'] else 'cshlib'], 286 | includes = ['.'], 287 | uselib = ['IMXDMABUFFER', 'C99'] + use_lists['uselib'], 288 | use = use_lists['use'], 289 | source = ['imxvpuapi2/imxvpuapi2.c', 'imxvpuapi2/imxvpuapi2_priv.c', 'imxvpuapi2/imxvpuapi2_jpeg.c'], 290 | name = 'imxvpuapi2', 291 | target = 'imxvpuapi2', 292 | install_path="${LIBDIR}", 293 | vnum = bld.env['IMXVPUAPI2_VERSION'] 294 | ) 295 | 296 | bld.install_files('${PREFIX}/include/imxvpuapi2/', ['imxvpuapi2/imxvpuapi2.h', 'imxvpuapi2/imxvpuapi2_jpeg.h']) 297 | 298 | bld( 299 | features = ['subst'], 300 | source = "libimxvpuapi2.pc.in", 301 | target="libimxvpuapi2.pc", 302 | install_path="${LIBDIR}/pkgconfig" 303 | ) 304 | 305 | if not bld.env['DISABLE_EXAMPLES']: 306 | examples = [ \ 307 | { 'name': 'decode-example', 'source': ['example/decode-example.c'] }, \ 308 | { 'name': 'encode-example', 'source': ['example/encode-example.c'] }, \ 309 | { 'name': 'jpeg-dec-example', 'source': ['example/jpeg-dec-example.c'] }, \ 310 | { 'name': 'jpeg-enc-example', 'source': ['example/jpeg-enc-example.c'] }, \ 311 | ] 312 | 313 | bld( 314 | features = ['c'], 315 | includes = ['.', 'example'], 316 | uselib = ['IMXDMABUFFER', 'C99'], 317 | use = 'imxvpuapi2', 318 | source = ['example/main.c', 'example/y4m_io.c', 'example/h264_utils.c'], 319 | name = 'examples-common' 320 | ) 321 | 322 | for example in examples: 323 | bld( 324 | features = ['c', 'cprogram'], 325 | includes = ['.', 'example'], 326 | uselib = ['IMXDMABUFFER', 'C99'], 327 | use = 'imxvpuapi2 examples-common', 328 | source = example['source'], 329 | target = 'example/' + example['name'], 330 | install_path = None # makes sure the example is not installed 331 | ) 332 | --------------------------------------------------------------------------------