Repository: fpv-wtf/msp-osd
Branch: main
Commit: ef9d906cf43a
Files: 80
Total size: 859.0 KB
Directory structure:
gitextract_wyjnxqch/
├── .github/
│ └── workflows/
│ └── build.yml
├── .gitignore
├── FAKEHD.md
├── LICENSE
├── Makefile.dji
├── Makefile.unix
├── README.md
├── config/
│ ├── airunit/
│ │ ├── config.json
│ │ ├── schema.json
│ │ ├── schemaV2.json
│ │ └── uiSchemaV2.json
│ └── goggles/
│ ├── config.json
│ ├── schema.json
│ ├── schemaV2.json
│ └── uiSchemaV2.json
├── docs/
│ └── fonts/
│ └── gen.sh
├── ipk/
│ ├── airunit/
│ │ ├── control/
│ │ │ ├── conffiles
│ │ │ ├── control
│ │ │ ├── postinst
│ │ │ └── prerm
│ │ └── data/
│ │ └── opt/
│ │ ├── bin/
│ │ │ └── airunit-osd-start.sh
│ │ └── etc/
│ │ └── dinit.d/
│ │ └── msp-osd-airside
│ └── goggle/
│ ├── control/
│ │ ├── conffiles
│ │ ├── control
│ │ ├── postinst
│ │ ├── preinst
│ │ └── prerm
│ └── data/
│ └── opt/
│ └── etc/
│ └── dinit.d/
│ └── msp-osd-goggles
├── jni/
│ ├── Android.mk
│ ├── Application.mk
│ ├── displayport_osd_shim.c
│ ├── fakehd/
│ │ ├── fakehd.c
│ │ └── fakehd.h
│ ├── font/
│ │ ├── font.c
│ │ └── font.h
│ ├── hw/
│ │ ├── dji_display.c
│ │ ├── dji_display.h
│ │ ├── dji_radio_shm.c
│ │ ├── dji_radio_shm.h
│ │ ├── dji_services.c
│ │ ├── dji_services.h
│ │ └── duml_hal.h
│ ├── json/
│ │ ├── osd_config.c
│ │ ├── osd_config.h
│ │ ├── parson.c
│ │ └── parson.h
│ ├── libspng/
│ │ ├── spng.c
│ │ └── spng.h
│ ├── lz4/
│ │ ├── lz4.c
│ │ └── lz4.h
│ ├── msp/
│ │ ├── msp.c
│ │ ├── msp.h
│ │ ├── msp_displayport.c
│ │ └── msp_displayport.h
│ ├── msp_displayport_mux.c
│ ├── net/
│ │ ├── data_protocol.h
│ │ ├── network.c
│ │ ├── network.h
│ │ ├── serial.c
│ │ └── serial.h
│ ├── osd.h
│ ├── osd_dji_overlay_udp.c
│ ├── osd_dji_udp.c
│ ├── osd_sfml_udp.c
│ ├── rec/
│ │ ├── rec.c
│ │ ├── rec.h
│ │ ├── rec_pb.c
│ │ ├── rec_pb.h
│ │ ├── rec_shim.c
│ │ ├── rec_shim.h
│ │ ├── rec_util.c
│ │ └── rec_util.h
│ ├── toast/
│ │ ├── toast.c
│ │ └── toast.h
│ └── util/
│ ├── debug.h
│ ├── display_info.h
│ ├── fs_util.c
│ ├── fs_util.h
│ └── time_util.h
└── libshims/
└── duml_hal.c
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build.yml
================================================
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
tags:
- '*'
# pull_request:
# branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
release:
name: Create new releas and attach IPKs
runs-on: ubuntu-latest
permissions:
contents: write
needs: build
steps:
- name: Branch name
id: branch_name
run: |
echo ::set-output name=IS_TAG::${{ startsWith(github.ref, 'refs/tags/') }}
echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
echo ::set-output name=RELEASE_VERSION::${GITHUB_REF#refs/*/}
- name: Download artifact
env:
IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}
SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}
if: "${{ env.IS_TAG == 'true' }}"
uses: actions/download-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
- name: View content
env:
IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}
SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}
if: "${{ env.IS_TAG == 'true' }}"
run: ls -R
- name: Create tagged release
id: create_release
env:
IS_TAG: ${{ steps.branch_name.outputs.IS_TAG }}
SOURCE_TAG: ${{ steps.branch_name.outputs.SOURCE_TAG }}
if: "${{ env.IS_TAG == 'true' }}"
uses: ncipollo/release-action@v1
with:
tag: ${{ env.SOURCE_TAG }}
name: ${{ env.SOURCE_TAG }}
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: false
artifacts: "./*/*.ipk"
build:
name: Build .ipk packages
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Get GitHub Build Number (ENV)
id: get_buildno
run: echo "GITHUBBUILDNUMBER=${{ github.run_number }}" >> $GITHUB_ENV
continue-on-error: true
- name: Get repository name
run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | cut -d '/' -f2)" >> $GITHUB_ENV
shell: bash
- name: Make artifact name
id: make_artifactname
run: |
ARTIFACT_NAME="${{ env.REPOSITORY_NAME }}-${{ github.run_number }}"
echo "${ARTIFACT_NAME}"
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r23b
- name: Build .ipk
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/:$PATH
make ipk
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
path: |
./ipk/*.ipk
================================================
FILE: .gitignore
================================================
*.o
osd_dji
msp_displayport_mux
ipk/goggle/build
ipk/airunit/build
*.ipk
repo
obj
libs
================================================
FILE: FAKEHD.md
================================================
# FakeHD
Betaflight's (before 4.4) OSD supports a 30 * 16 Grid, which looks large/blocky when displayed in the DJI Goggles.
For versions of Betaflight before 4.4 (or other FC firmwares without HD support), as a workaround, we have a mode called "FakeHD". It chops up the OSD into sections and positions them evenly around an HD grid (with gaps between) - the way this is done is configurable, explained below. This then allows the use of smaller fonts - so it looks nicer/more in keeping with the built in Goggles OSD (but you still only have 30 columns / 16 rows to configure.... and you have the gaps to contend with).
A diagram to help...
| Before (in Configurator) | After (in Goggles) |
| ------------------------------------------------------ | --------------------------------------------------- |
|  |  |
##### To configure/enable:
Visit https://fpv.wtf/package/fpv-wtf/msp-osd with your goggles connected, and check "Fake HD"
Optionally, place custom fonts in the root of your sd card, using the names `font_bf_hd.png`
Configuration of the grid is also possible; see below.
No air unit/vista config is required.
##### Menu Switching - Getting rid of gaps when displaying Menu / Post Flight Stats + displaying centered:
In order to have menus (accessible in Betaflight using stick commands) and post-flight stats appear in the center of the screen while using FakeHD, rather than having gaps + looking broken, you should set up menu switching.
FakeHD can use the presence/absence of a character in the OSD as a switch to indicate when you are in regular OSD mode or in the menu/stats and switch to centering temporarily when needed.
By default, the `Throttle Position` icon is used (character 4) - but you can set any character you want. It needs to be something that doesn't flash or change in the regular OSD, and ideally (but not essential) something that is never in the menu/post flight stats. The icons next to various elements are obvious choices here. You can set this using the `fakehd_menu_switch` configuration parameter.
Betaflight has a list here: https://github.com/betaflight/betaflight/blob/master/docs/osd.md
If you want to use FakeHD with some other Flight Controller, you will need to find an appropriate icon. (Let us know - we can include the information here).
Finally, if you don't have anything in your OSD that works for menu switching, you can hide the menu switching character and the subsequent 5 characters, allowing you to add the `Throttle Position` element but not have to see it on screen. This is enabled by setting `fakehd_hide_menu_switch` to true.
Notes:
- Because of this switching feature, if you change to a different quad or OSD config (specifically the switch element is in a different place), FakeHD will center - you will need to reboot your Goggles to get it to recognise the switch element in a different location.
- Also because of this switching, if you are editing OSD in the configurator with the goggles on to preview and you move the switching element around, it will cause the gaps to be disabled and everything to center. The new location of the switching element will be found next time you reboot the goggles and it'll work as normal.
##### I don't want gaps at all...
Set config `fakehd_lock_center` to true and the center locking used for the menu / post flight stats will be enabled permanently (ie: you fly with it as well) - basically it places your 30 x 16 SD grid into the middle of an HD grid, there's never any gaps - see diagram below + compare to diagrams above.
| After/Centered (in Goggles) `fakehd_lock_center` |
| ------------------------------------------------------------------------------ |
|
|
##### Customising the default FakeHD grid.
By default, FakeHD positions your SD grid into the HD grid as per the before/after diagram above.
If this doesn't work for you for whatever reason, some customisation is available. It's necessarily a little complicated.
Each row can be set to one of:
| Code | Description |
| ---- | ------------------------------------------------------------------------------------------------------------------------------ |
| L | Left aligned, the SD row occupies cell 1-30, no gaps |
| C | Center aligned, the SD row occupies cell 16-45, no gaps |
| R | Right aligned, , the SD row occupies cell 31-60, no gaps |
| W | Split A - Row is split in 3, the FakeHD default, filling cells 1-10, 26-35, 51-60 |
| T | Split B - Row is split in 2, touching the sides - filling cells 1-15 + 46-60 |
| F | Split C - Row is split in 2 and away from the sides - filling cells 11-25 + 36-50 |
| D | DJI Special - Row is centered but pushed a little left; used to posiution the bottom row between the existing DJI OSD elements |
And then the columns as a whole can be set to one of:
| Code | Description |
| ---- | --------------------------------------------------------------------------- |
| T | Top aligned, OSD occupies rows 1-16 |
| M | Center aligned, OSD occupies cells 4-19, no gaps |
| B | Bottom aligned, , the OSD occupies rows 7-22 |
| S | Split - FakeHD default - split in 3, OSD occupies rows 1 - 5, 9 - 13, 17-22 |
Using the default row config; here's what they all look like:
| T | M | B | S |
| ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------ |
|
|
|
|
|
###### To configure rows
Rows config accepts a 16 character long string; each character configuring it's corresponding row. The default FakeHD config would be set like this:
`fakehd_rows = WWWWWWCCWWWWWWWD`
The characters are case sensitive, but the configurator will reject invalid characters.
###### To configure columns
Columns accepts a single character configuring how the whole grid is aligned. The default would be set like this:
`fakehd_columns = S`
The characters are case sensitive, but the configurator will reject invalid characters.
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: Makefile.dji
================================================
CC=armv7a-linux-androideabi19-clang
CFLAGS=-I. -O2
LIB_SHIMS = libshims/libduml_hal.so
.PHONY: repo
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
#this doesn't work by default
#an extra duss_result_t(frame_pop_handler)
#is generated that the compiler doesn't like
libshims/%.c: %.h
stubgen -g -e .c -l -N -t libshims -p "../" -n $<
libshims/lib%.so: libshims/%.c
$(CC) -Wno-c2x-extensions -O2 -shared -Wl,-soname,libduml_hal.so -o $@ $< $(CFLAGS)
libshims: $(LIB_SHIMS)
all: jni/*
ndk-build
install: all
install -d ipk/goggle/build/data/opt/fonts/
install fonts/*.png ipk/goggle/build/data/opt/fonts/
install -d ipk/goggle/build/data/opt/mspdictionaries/
install dictionaries/*.bin ipk/goggle/build/data/opt/mspdictionaries/
install -d ipk/goggle/build/data/opt/etc/preload.d/
install libs/armeabi-v7a/libdisplayport_osd_shim.so ipk/goggle/build/data/opt/etc/preload.d/
install -d ipk/airunit/build/data/opt/bin/
install libs/armeabi-v7a/msp_displayport_mux ipk/airunit/build/data/opt/bin/
install -d ipk/airunit/build/data/opt/mspdictionaries/
install dictionaries/*.bin ipk/airunit/build/data/opt/mspdictionaries/
install -d ipk/airunit/build/data/opt/etc/package-config/msp-osd/
install config/airunit/* ipk/airunit/build/data/opt/etc/package-config/msp-osd/
install -d ipk/goggle/build/data/opt/etc/package-config/msp-osd/
install config/goggles/* ipk/goggle/build/data/opt/etc/package-config/msp-osd/
goggle_ipk: install
$(eval PKG_NAME := $(shell cat ./ipk/goggle/control/control | grep Package | cut -d" " -f2))
$(eval ARCH := $(shell cat ./ipk/goggle/control/control | grep Architecture | cut -d" " -f2))
$(eval VERSION :=$(shell cat ./ipk/goggle/control/control | grep Version | cut -d" " -f2))
$(eval IPK_NAME := "${PKG_NAME}_${VERSION}_${ARCH}.ipk")
cp -r ipk/goggle/data ipk/goggle/build/
echo "2.0" > ipk/goggle/build/debian-binary
cp -r ipk/goggle/control ipk/goggle/build/
cd ipk/goggle/build/control && tar czvf ../control.tar.gz .
cd ipk/goggle/build/data && tar czvf ../data.tar.gz .
cd ipk/goggle/build && tar czvf "../../${IPK_NAME}" ./control.tar.gz ./data.tar.gz ./debian-binary
airunit_ipk: install
$(eval PKG_NAME := $(shell cat ./ipk/airunit/control/control | grep Package | cut -d" " -f2))
$(eval ARCH := $(shell cat ./ipk/airunit/control/control | grep Architecture | cut -d" " -f2))
$(eval VERSION :=$(shell cat ./ipk/airunit/control/control | grep Version | cut -d" " -f2))
$(eval IPK_NAME := "${PKG_NAME}_${VERSION}_${ARCH}.ipk")
mkdir -p ipk/airunit/build
echo "2.0" > ipk/airunit/build/debian-binary
cp -r ipk/airunit/data ipk/airunit/build/
cp -r ipk/airunit/control ipk/airunit/build/
cd ipk/airunit/build/control && tar czvf ../control.tar.gz .
cd ipk/airunit/build/data && tar czvf ../data.tar.gz .
cd ipk/airunit/build && tar czvf "../../${IPK_NAME}" ./control.tar.gz ./data.tar.gz ./debian-binary
ipk: install goggle_ipk airunit_ipk
repo: ipk
mkdir -p repo
cp ipk/*.ipk repo/
../opkg-utils-0.5.0/opkg-make-index ./repo/ > repo/Packages
http-server -p 8042 ./repo/
clean:
rm -rf **/*.o
rm -rf *.o
rm -rf repo
rm -rf libshims/*.so
rm -rf msp_displayport_mux
rm -rf osd_dji
rm -rf ipk/goggle/build
rm -rf ipk/airunit/build
rm -rf ipk/*.ipk
rm -rf obj
rm -rf libs
================================================
FILE: Makefile.unix
================================================
CC=gcc
CFLAGS=-I. -O2
SRCDIR = jni/
DEPS = $(addprefix $(SRCDIR), msp/msp.h msp/msp_displayport.h net/network.h net/serial.h)
OSD_OBJ = $(addprefix $(SRCDIR), osd_sfml_udp.o net/network.o msp/msp.o msp/msp_displayport.o)
DISPLAYPORT_MUX_OBJ = $(addprefix $(SRCDIR), msp_displayport_mux.o net/serial.o net/network.o msp/msp.o)
OSD_LIBS=-lcsfml-graphics
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
osd_sfml: $(OSD_OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(OSD_LIBS)
msp_displayport_mux: $(DISPLAYPORT_MUX_OBJ)
$(CC) -o $@ $^ $(CFLAGS)
clean:
rm -rf *.o
rm -rf **/*.o
rm -f msp_displayport_mux
rm -f osd_sfml
================================================
FILE: README.md
================================================
# IMPORTANT
As of msp-osd v0.12+, the required font format has now changed for the goggles and the OSD Overlay tool. Support for .bin font file format has been removed in favour of .png font file format.
See the 'Fonts' section and the 'Overlaying OSD on DVR' sections further below for clarification.
# MSP-OSD - Full OSD / Displayport OSD
This package gives you support for full flight controller driven OSD in line with those on analog + other HD systems.
Technically - it takes MSP DisplayPort (so-called "canvas" although this is a misnomer as there is another Betaflight "canvas" mode for Pixel OSDs) messages through UDP and renders them to a framebuffer overlaid under the DJI 'dji_glasses' menu system.
SFML (PC/Mac development) and DJI Goggles viewports are available, as well as a *mux* for the Air Unit / Vista, which creates a *pty* and provides filtered MSP access, and reroutes DisplayPort messages to UDP.
# Setup and Installation
## Easy Installation
* Install WTFOS from https://fpv.wtf. WTFOS must be installed on both the goggles and each AU/Vista.
* Install the msp-osd package on each device using WTFOS.
* Reboot.
## Flight Controller Setup
* Ensure that the correct UART is set to use MSP
* Enable MSP DisplayPort
### Betaflight - Since 4.5
Betaflight 4.5 has added support for coloured warning messages/OSD elements.
This added feature requires a Betaflight font that includes the extra colour elements. The extra colour elements are for green, amber and red indicators and respectively reside in additional font pages.
This means that to support the extra 3 colours, a 4 page font file is required.
MSP-OSD 0.12+ now supports this feature and includes the required 4 page coloured font for Betaflight bundled by default.
A Betaflight CLI command may needed to take advantage of the this feature:
`set displayport_msp_fonts = 0,1,2,3`
This tells Betaflight to use a specific [colour] page when displaying the warning.
##### *Important*
If you are **not** using a Betaflight 4 page font, and this value is set to `0,1,2,3`, some OSD elements may not display. In this case, set the value to `0,0,0,0`. Meaning every warning will use the first page of the font.
### Betaflight - Since 4.4
Betaflight 4.4 has added native support for OSD in HD aspect ratio.
#### Configure With Preset
There is a preset available: "OSD for Fpv.wtf, DJI O3, Avatar HD", once applied you can then skip ahead to configuring in the OSD tab.
#### Configure Manually
Or to configure manually, first enter the following commands in the CLI tab:
```
set osd_displayport_device = MSP
set vcd_video_system = HD
save
```
And then in the Ports tab, select the peripheral "VTX (MSP + DisplayPort)" for the UART your Vista/Air unit is connected to.

Afterwards, you can configure the OSD elements as normal in the OSD tab.
#### Troubleshooting wrong grid size in BF 4.4 Configurator
It is recommended to enable [compressed transmission](#compressed-transmission) with BF 4.4; (now default). It removes/avoids ordering issues between FC/AU/Goggles bootup - the AU has to tell the FC the grid size it supports.
If you don't want to / can't do this - try rebooting your goggles, then reboot your AU.
### Betaflight - 4.3 or Before
We have a configurator preset available - "FPV.WTF MSP-OSD", just be sure to pick the UART your Vista/Air unit is connected to.
#### Or to configure manually
On *Betaflight*, this is done using the following commands in the CLI tab:
```
set osd_displayport_device = MSP
set displayport_msp_serial =
set vcd_video_system = PAL
save
```
Eg.: If the Betaflight Configurator says your DJI VTx is attached to UART2, the value for **** is **1** - so you would use ```set displayport_msp_serial = 1```.
Test if the value is correct by typing `save` and after the reboot `get displayport_msp_serial` This command should return the value you set it to. If it returns -1 (and that was not the value you set) then the value was not correct.
For Betaflight - ensure you set the Video Format to PAL or Auto in the OSD tab - otherwise you don't get access to the whole OSD area. Note that currently BF Configurator hides these options once you switch to MSP for OSD; the last command above should have done this for you.
#### Softserial
We don't recommend connecting via soft serial; results have been poor - it gives slow/laggy/inconsistent behaviour. But some users have reported it being usable, so if for whatever reason this is your only option, read on.
If you have connected the Vista/Airunit to a softserial port run the `serial` command to list serial ports
Use the value after _serial_ with set `displayport_msp_serial` but do **not** subtract 1 from the value. E.g.:
```
# serial
serial 20 1 115200 57600 0 115200
serial 0 64 115200 57600 0 115200
serial 1 0 115200 57600 0 115200
serial 30 1 115200 57600 0 115200
# set displayport_msp_serial = 30
```
#### FakeHD
With the introduction of HD FPV and its associated transmission and display resolutions, a significantly larger area (canvas*) became a possibility to use for flight controller OSD informaton.
The first generation of the DJI FPV system supports a display resolution of 810p (1440px width x 810px height). This allowed for a potential for a 60 * 22 grid of (mono-spaced) characters of OSD.
Prior to Betaflight(BF) 4.4, the BF OSD supported a 30 * 16 Grid, which looks large/blocky when displayed in the DJI Goggles. This 30 * 16 grid was due to the analogue roots of the video and OSD system.
To present the OSD in a visually better way on HD FPV systems, MSP-OSD introduced a workaround called FakeHD that divided up the OSD canvas area into sections that could be configured.
[FakeHD information is available here.](FAKEHD.md)
### INAV
Select "MSP Display Port" (or "HDZero VTx" on older INAV versions) as the Peripheral. Next, select "HD" (or the "WTFOS" variant) in the OSD tab if you'd like to use the HD Canvas.
If the iNav OSD appears garbled at first, try entering the iNav menus using the RC sticks, and then exiting the menus. This will force INAV to switch into HD mode a second time.
It is recommended (now enabled by default in recent msp-osd releases) to enable [compressed transmission](#compressed-transmission) with INAV to avoid issues with the display corrupting - the artifical horizon is the most common element to show this.
### Ardupilot
Please install a recent Ardupilot version for full functionality. There was one critical bug fix for MSP telemetry not passing through a DisplayPort serial port. Additionally, there were several feature additions including HD support after the last 4.2 release.
Settings:
```
SERIALx_PROTOCOL = 42
OSD_TYPE = 5
```
Recent versions of MSP-OSD fully support Ardupilot with a specific HD FPV font. This font is bundled with msp-osd 0.12
If you wish to use a Betaflight font instead of an Ardupilot font, you can also set `MSP_OPTIONS = 4` to allow the use of a Betaflight font.
More info: https://ardupilot.org/plane/docs/common-msp-osd-overview-4.2.html#dji-goggles-with-wtf-osd-firmware
### KISS Ultra
Select MSP on serial and select DJI WTF as canvas dialect. That's it.
### QUICKSILVER
Configure the UART under Digital VTX - see https://docs.bosshobby.com/Configuring-Quicksilver/#setup
# Fonts
We bundle in default fonts for Betaflight, Ardupilot, INAV, Quicksilver, and KISS ULTRA (INAV/Betaflight/Ardupilot fonts are SNEAKY_FPV's Unify Europa design - thanks to SNEAKYFPV for allowing us to use these - https://sites.google.com/view/sneaky-fpv/home). Since 0.12 we now use a PNG font format, the same as Walksnail. [Default fonts can be viewed here](fonts). You may also upload your own fonts to the SD card.
For the naming convention of the font file in png format see the *FC Specific Font File Names* section below.
***It is important to note the following in regards to when a font file is used from where***
When the goggles determines what font file to use in presenting the OSD, it will look in the following 3 different locations in preferential order:
1. SD Card (root) `/storage/sdcard0`
2. Goggles user font location `/blackbox` (see the *Moving fonts to the goggles file system* section below)
3. Goggles bundled fallback location `/blackbox/wtfos/opt/fonts`
### HD vs SD fonts
Due to historical support for FC firmware that did not have the full HD canvas grid size, both SD and HD font files are bundled.
SD font file/s (naming without the suffix *_hd*) are used when the FC configuration is not set to, or does not support, HD. i.e. BF 4.3 and below.
HD font file/s are used with most modern FC firmware when the equivalent HD selection is made in the FC's OSD configuration tab.
##### *Note*
You do not need to use/copy/install the non-hd font file if you only use a HD OSD canvas.
### Attaining/using fonts
* Download a font package. See below for known community fonts.
* Place these two PNG files on the root of your Goggles SD card.
* Insert the SD card and reboot the goggles.
### Using Walksnail Fonts
As of msp-osd 0.12, the format of font files are now compatible with Walksnail fonts, with the following caveats.
For INAV specific Walksnail fonts, the format of the font pages matter as historical fonts typically were formatted with pages stacked vertically. The new requirement is for the pages to be stacked side by side. The newer format of side by side pages is supported by both msp-osd and Walksnail. Side by Side specifially formatted Walksnail fonts for INAV will be denoted 'sbs' in the zipfile and or image names. This does not affect Betaflight as historical fonts were single page, and newer 4 page fonts are side by side formatted pages. Ardu is a single page font file so all historically created fonts will work without issue.
A Walksnail font package will typically contain 3 files. An ini file and 2 png files. The 2 png files will typically follow a naming convention that contains `_24` or `_36` in the file name. The '24' file is the msp-osd `_hd` equivalent, and the '36' file is the non-hd equivalent. e.g.
```
font_update.ini
WS_BFx4_Sphere_24.png
WS_BFx4_Sphere_36.png
```
From the above example, to use them for msp-osd, copy the 2 png files to the SD card root and rename them as:
|Walksnail Name|msp-osd Name|
|--------------|------------|
|`WS_BFx4_Sphere_24.png`|`font_btfl_hd.png`|
|`WS_BFx4_Sphere_36.png`|`font_btfl.png`|
### .bin Fonts
Due to the changes in 0.12, the .bin font format was superseded in favour of a wider accepted format that makes it easier for the community to create their own.
With 0.12, any fonts stored on the SD card in the .bin format will be ignored.
If you wish to retain the font from the .bin file, this open source tool will assist in conversion to png from bin.
https://github.com/shellixyz/hd_fpv_osd_font_tool/tree/main/src/bin/hd_fpv_osd_font_tool
### FC Specific Font File Names
| Flight controller | SD | HD |
| ----------------- | -- | -- |
| Betaflight | `font_btfl.png` | `font_btfl_hd.png` |
| INAV | `font_inav.png` | `font_inav_hd.png`|
| Ardupilot | `font_ardu.png` | `font_ardu_hd.png`|
| KISS Ultra | `font_ultr.png` | `font_ultr_hd.png`|
| QUICKSILVER | `font_quic.png` | `font_quic_hd.png`|
| Generic/Fallback* | `font.png` | `font_hd.png`|
*This uses the Betaflight font layout
Airside VTx (AU/Vista) which have a very old version of msp-osd on, as well as flight controllers which do not respond to the Variant request, like old Ardupilot versions, will use to the Generic/Fallback font.
##### *Note*
You can also add fonts for firmwares not in this list; using the generic filename, or put the MSP identifier in (lower case it) the filename - ```font_.png / font__hd.png```
### Moving fonts to the goggles file system
If you wish to use a specific font different than the font stored on the goggles, for when you do not have the SD card inserted in the goggles, you can follow the below steps to do this.
1. Copy the font files you wish to be stored on the goggles firstly to your SD card
2. Connect your goggles via USB if you didn't do this for step 1.
3. Insert the SD card in the goggles then power them on
4. Navigate to https://fpv.wtf
5. After the goggles are found and connected, go to the fpv.wtf CLI and type the following
6. `cp /storage/sdcard0/font*.png /blackbox`
7. Power off the goggles, remove the SD card and confirm the newly copied font is used.
##### *Note*
To remove copied fonts repeat steps 1 to 6 and replace step 7 with `rm /blackbox/font*.png`. Then power off and on the goggles.
### Suggested Third Party Fonts
- [KNIFA's Material](https://github.com/Knifa/material-osd/releases) - use the Walksnail version for MSP-OSD <= 0.12
- [SNEAKY_FPV's colour fonts for INAV, ARDU and BF](https://sites.google.com/view/sneaky-fpv/home)
- [VICEWIZE Italic](https://github.com/vicewize/vicewizeosdfontset)
- [Kw0ngk4n's Neue OSD](https://github.com/Kw0ngk4n/WTF-Neue-OSD)
- [EVilm1's OSD Font](https://github.com/EVilm1/EVilm1-OSD-Font)
## Overlaying OSD on DVR
**Note the change in font file format requirements (png format) for the OSD Overlay tool. If no font file is provided by the user, a built in font (png format) will be used.**
The overlay process uses DVR, recorded osd data and a font file, to overlay/render the OSD data onto DVR footage on your computer.
A default font will be used if no font file is supplied however. The font look and feel will be the bundled font you would see if using this during flight.
https://fpv.wtf/osd-overlay provides a tool that will overlay captured osd information onto DVR footage.
### Pre-requisites
Unless the osd information is captured during DVR recording on the goggles you will be unable to overlay your osd onto DVR on your computer.
The following fpv.wtf CLI commands (goggles) will ensure this is enabled.
```
package-config set msp-osd rec_enabled true
package-config apply msp-osd
```
##### *Notes*
You only need to supply a font file for the canvas the DVR was recorded with. i.e. If your FC firmware configuration was HD or a HD variant you only need to supply the '_hd.png' font file.
The 'Chroma Key' will replace the DVR with a solid colour for use within video editing software. Be aware however that fonts have an amount of transparency around elements that will include the 'Chroma Key' bleed that will be difficult to avoid in video editing software.
See the 'Using Walksnail Fonts Section' for specific requirements that may apply to font file formats.
# Configuration options
Configuration options can be set using the WTFOS Configurator CLI.
Prefix option with `package-config set msp-osd`
e.g. `package-config set msp-osd compress_osd true`
Once desired setting changes are made then: `package-config apply msp-osd`, otherwise settings will be lost when power is off.
Visit https://fpv.wtf/package/fpv-wtf/msp-osd with your Goggles or Air Unit plugged in to edit options.
### Current available options (Goggles):
| Option | Description | Type | Default|
| ------ | ----------- | ---- |--------|
|`show_waiting`| enables or disables WAITING FOR OSD message | true/false | true |
|`show_au_data`| enables AU data (temp/voltage) overlay on the right | true/false | false |
|`rec_enabled`| enable OSD recording to .msp files alongside video | true/false | true |
|`rec_pb_enabled`| enable OSD playback if .msp file is stored alongside video | true/false | true |
|`hide_diagnostics`| hide the diagnostic information in the bottom right | true/false | false |
|`fakehd_enable`| enables [FakeHD](#FakeHD); the other FakeHD options don't do anything if this is disabled. FakeHD is force disabled if the Flight Controller supports proper HD / RealHD | true/false| false |
|`fakehd_lock_center`| Lock FakeHD in centered mode all the time; no gaps/spreading out even when you are flying. | true/false | false |
|`fakehd_menu_switch`| FakeHD will use this character as the menu switch to detect when you are in menus/postflight and triggger centering. | integer/number | 4 (Betaflight Throttle) |
|`fakehd_hide_menu_switch`| FakeHD will hide the menu switch set above; and the next 5 characters | true / false | false |
| `fakehd_columns` | FakeHD column alignment config | Single character, one of T M B S | S |
| `fakehd_rows` | FakeHD row alignment config, each character configures the alignment for one row | 16 characters, each one of L C R W T F D | WWWWWWCCWWWWWWWD |
### Current available options (Air Unit/Vista):
| Option | Description | Type | Default|
| ------ | ----------- | ---- |--------|
|`compress_osd`| Enable sending full frames of compressed data. Disable to send raw MSP data [Read more](#Compressed-Transmission) | true/false| true |
| `osd_update_rate_hz` | Configure the update rate in hz for the OSD when using compressed transmission | integer | 10 |
| `disable_betaflight_hd` | Disable HD Mode, which is otherwise set by default in Betaflight 4.4 | true/false | false |
| `fast_serial` | Change serial baud rate to 230400 baud, which can improve OSD performance in some situations - FC UART config must be changed to match. | true/false | false |
| `cache_serial` | Cache unimportant MSP messages for seldom-used features (like PID tuning in the DJI Goggles Settings Menu) to reduce serial pressure | true/false | false |
#### Compressed Transmission
As of 0.7.0, a new option, `compress_osd`, was added to the **air side** process.
If this is set to "true", then the entire character buffer will be sent using LZ4 compression at the rate defined in `osd_update_rate_hz`, instead of sending raw MSP messages over the air.
When enabled, this should fix INAV delta update related issues as well as provide better link stability.
To enable:
Visit https://fpv.wtf/package/fpv-wtf/msp-osd with your **Air Unit / Vista** plugged in to edit package options.
This option is enabled by default as of 0.10.0, however, if you upgraded from an older version, your configuration will need to be updated using the configurator.
If you continue to have issues with especially INAV character corruption, it is likely your serial link is saturated. Check that the "Custom OSD" option in your DJI goggles menus is set to _disabled_ , and also try out the cache_serial option.
## FAQ / Suggestions
### Modify / Move original DJI OSD elements
You can now modify the elements present in the original DJI OSD. These include for example : transmission speed, latency, channel used, googles battery, sd card icon and default timer.
Elements position, visibility, font and icons can be modified by editing the internal googles files.
This is possible by connecting to the googles using ADB. You can even preview changes using a Python script!
This is not a trivial thing for everyone to do, the full tutorial can be found [here](https://github.com/EVilm1/WIKI-HACK-DJI-OSD#6-advanced-setup-modify-the-dji-hud-elements).
### How do I create a new font (for INAV, Ardupilot, etc.)?
Use `mcm2img` , specifically Knifa's branch to allow you to draw using a PNG template.
https://github.com/Knifa/mcm2img/tree/templates
### Why is everything so big / can I make the text smaller (betaflight)?
For Betaflight prior to 4.4, look into FakeHD.
For Betaflight after 4.4, you should see "HD" fonts by default. Make sure your VTx (AU/Vista) is powered up and visit the Betaflight Configurator to move OSD items to the edge of the screen.
### How can I get my INAV/ArduPilot/Kiss Ultra OSD closer to the edge of the screen / Why is FakeHD closer to the edges?
- The goggles need 60 characters to go edge to edge - so the 50 in the hd grid doesn't quite fill it
- So, depending on the Flight Controller's setup, the RealHD grid is displayed centered in the goggles - gaps on both edges.
- FakeHD had no compatibility constraints like this so we were able to use the full width of the screens.
- Consequently, FakeHD can get nearer the edges.
- Currently no solution to get RealHD closer to the edges.
### What is RealHD
Sometimes we refer to the proper MSP OSD HD grid supported by ArduPilot / Kiss Ultra / INAV / Betaflight (from 4.4) + others as RealHD, to distinguish from FakeHD.
# Compiling (development and debugging)
To build for DJI, install the [Android NDK](https://developer.android.com/ndk/downloads) and add the NDK toolchain to your PATH, then use `ndk-build` to build the targets.
To build for UNIXes, install CSFML and run:
```
make -f Makefile.unix
```
Provided targets and tools are:
* `msp_displayport_mux` - takes MSP DisplayPort messages, bundles each frame (all DisplayPort messages between Draw commands) into a single UDP Datagram, and then blasts it over UDP. Also creates a PTY that passes through all _other_ MSP messages, for `dji_hdvt_uav` to connect to.
* `libdisplayport_osd_shim.so` - Patches the `dji_glasses` process to listen for these MSP DisplayPort messages over UDP, and blits them to a DJI framebuffer screen using the DJI framebuffer HAL `libduml_hal` access library, and a converted Betaflight font stored in `font.bin`.
* `osd_sfml` - The same thing as `osd_dji`, but for a desktop PC using SFML and `bold.png`.
Additional debugging can be enabled using `-DDEBUG` as a CFLAG.
## Custom Build Installation (Goggles)
There's a slightly different process for V1 vs V2 Goggles, they renamed some bits between the two.
### FPV Goggles V1
```
ndk-build
adb shell setprop dji.dji_glasses_service 0
adb push libs/armeabi-v7a/libdisplayport_osd_shim.so /tmp
adb shell LD_PRELOAD=/tmp/libdisplayport_osd_shim.so dji_glasses_original -g
```
### FPV Goggles V2
```
ndk-build
adb shell setprop dji.glasses_wm150_service 0
adb push libs/armeabi-v7a/libdisplayport_osd_shim.so /tmp
adb shell LD_PRELOAD=/tmp/libdisplayport_osd_shim.so dji_gls_wm150_original -g
```
### Air Unit / Air Unit Lite (Vista)
```
ndk-build
adb push msp_displayport_mux /blackbox
adb shell setprop dji.hdvt_uav_service 0
adb shell mv /dev/ttyS1 /dev/ttyS1_moved
adb shell nohup /blackbox/msp_displayport_mux 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1 &
adb shell setprop dji.hdvt_uav_service 1
```
This tells the displayport mux to send data from /dev/ttyS1_moved to 192.168.41.2 (goggles) and to create a fake serial port at /dev/ttyS1 with the displayport messages filtered out.
Optionally, you can add `-f`, like `nohup /blackbox/msp_displayport_mux -f 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1` to put the serial port in a faster 230400 baud mode, and set the MSP serial port in your flight controller to 230400 to try to improve the framerate.
You can also omit `setprop dji.hdvt_uav_service 1` , which will improve your OSD framerate at the expense of disabling all Air Unit / Vista side coordination functionality (AU recording, channel changes, some RC features, etc.).
Enjoy.
## Additional Reading / Learning
https://github.com/fpv-wtf/margerine/wiki
## Shoutouts / Thank You
* http://github.com/fpv-wtf team, for making this all possible and very helpful random bits of advice
================================================
FILE: config/airunit/config.json
================================================
{
"fast_serial": false,
"cache_serial": false,
"osd_update_rate_hz": 10,
"compress_osd": true,
"disable_betaflight_hd": false
}
================================================
FILE: config/airunit/schema.json
================================================
{
"settings": {
"fast_serial": {
"name": "Fast Serial",
"widget": "checkbox"
},
"cache_serial": {
"name": "Cache Responses",
"widget": "checkbox"
},
"compress_osd": {
"name": "Compress OSD",
"widget": "checkbox"
},
"osd_update_rate_hz": {
"name": "OSD update rate",
"widget": "range",
"min": 2,
"max": 20,
"step":1
},
"disable_betaflight_hd": {
"name": "Compress OSD",
"widget": "checkbox"
}
},
"units": [
"msp-osd-airside"
]
}
================================================
FILE: config/airunit/schemaV2.json
================================================
{
"title": "MSP OSD",
"description": "Airside OSD config",
"type": "object",
"units": [
"msp-osd-airside"
],
"required": [],
"properties": {
"compress_osd": {
"title": "Compress OSD",
"type": "boolean",
"description": "",
"enum": [
true,
false
]
},
"osd_update_rate_hz": {
"title": "OSD update rate (for Compressed OSD)",
"type": "number",
"description": "",
"minimum": 1,
"maximum": 20
},
"disable_betaflight_hd": {
"title": "Disable Betaflight HD Mode",
"type": "boolean",
"description": "",
"enum": [
true,
false
]
},
"fast_serial": {
"type": "boolean",
"title": "Fast Serial",
"description": "",
"enum": [
true,
false
]
},
"cache_serial": {
"type": "boolean",
"title": "Cache Responses",
"description": "",
"enum": [
true,
false
]
}
}
}
================================================
FILE: config/airunit/uiSchemaV2.json
================================================
{
"compress_osd": {
"ui:help": "Enable sending full frames of compressed data. Disable to send raw MSP data - we strongly recommend leaving this on. [Read more](https://github.com/fpv-wtf/msp-osd#Compressed-Transmission)"
},
"osd_update_rate_hz": {
"ui:help": "Configure the update rate in hz for the OSD when using compressed transmission (default 10)"
},
"disable_betaflight_hd": {
"ui:help": "Disable HD Mode, which is otherwise set by default if configured in Betaflight 4.4"
},
"fast_serial": {
"ui:help": "Change serial baud rate to 230400 baud, which can improve OSD performance in some situations - FC UART config must be changed to match."
},
"cache_serial": {
"ui:help": "Cache unimportant MSP messages for seldom-used features (like PID tuning in the DJI Goggles Settings Menu) to reduce serial pressure"
}
}
================================================
FILE: config/goggles/config.json
================================================
{
"show_waiting": true,
"show_au_data": false,
"fakehd_enable": false,
"fakehd_hide_throttle_element": false,
"fakehd_lock_center": false,
"fakehd_menu_switch": 4,
"fakehd_hide_menu_switch": false,
"fakehd_layout_debug": false,
"fakehd_columns": "S",
"fakehd_rows": "WWWWWWCCWWWWWWWD",
"rec_enabled": true,
"rec_pb_enabled": true
}
================================================
FILE: config/goggles/schema.json
================================================
{
"settings": {
"show_waiting": {
"name": "Show Waiting Message",
"widget": "checkbox"
},
"show_au_data": {
"name": "Show VTx Temperature and Voltage",
"widget": "checkbox"
},
"fakehd_enable": {
"name": "Enable FakeHD Mode",
"widget": "checkbox"
},
"fakehd_hide_throttle_element": {
"name": "FakeHD hide throttle element",
"widget": "checkbox"
},
"fakehd_lock_center": {
"name": "Lock FakeHD to centered mode",
"widget": "checkbox"
},
"fakehd_menu_switch": {
"name": "FakeHD menu switch character",
"widget": "range",
"min": 1,
"max": 512,
"step":1
},
"fakehd_hide_menu_switch": {
"name": "FakeHD hide the menu switch",
"widget": "checkbox"
},
"fakehd_layout_debug": {
"name": "Undocumented feature to fill the grid with numbers for layout debugging",
"widget": "checkbox"
},
"fakehd_columns": {
"name": "FakeHD column config",
"widget": "text",
"pattern": "[TMBS]",
"minLength": 1,
"maxLength": 1
},
"fakehd_rows": {
"name": "FakeHD row config",
"widget": "text",
"pattern": "[LCRWTFD]{16}",
"minLength": 16,
"maxLength": 16
},
"rec_enabled": {
"name": "Enable OSD recording",
"widget": "checkbox"
},
"rec_pb_enabled": {
"name": "Enable OSD playback",
"widget": "checkbox"
},
"hide_diagnostics": {
"name": "Hide diagnostic information",
"widget": "checkbox"
}
},
"units": [
"msp-osd-goggles"
]
}
================================================
FILE: config/goggles/schemaV2.json
================================================
{
"title": "MSP OSD - Goggles",
"description": "",
"type": "object",
"units": [ "msp-osd-goggles" ],
"required": [],
"properties": {
"show_waiting": {
"type": "boolean",
"title": "Show Waiting Message",
"description": "",
"enum": [
true,
false
]
},
"show_au_data": {
"type": "boolean",
"title": "Show VTx Temperature and Voltage",
"description": "",
"enum": [
true,
false
]
},
"rec_enabled": {
"type": "boolean",
"title": "Enable OSD recording",
"description": "",
"enum": [
true,
false
]
},
"rec_pb_enabled": {
"type": "boolean",
"title": "Enable OSD playback",
"description": "",
"enum": [
true,
false
]
},
"hide_diagnostics": {
"type": "boolean",
"title": "Hide diagnostic information",
"description": "",
"enum": [
true,
false
]
},
"fakehd_enable": {
"type": "boolean",
"title": "Enable FakeHD Mode",
"description": "",
"enum": [
true,
false
]
}
},
"dependencies": {
"fakehd_enable": {
"oneOf": [
{
"properties": {
"fakehd_enable": {
"enum": [
false
]
}
}
},
{
"properties": {
"fakehd_enable": {
"enum": [
true
]
},
"fakehd_lock_center": {
"type": "boolean",
"title": "Lock FakeHD to centered mode",
"description": "",
"enum": [
true,
false
]
},
"fakehd_menu_switch": {
"type": "number",
"title": "FakeHD menu switch character",
"description": "",
"minimum": 1,
"maximum": 512
},
"fakehd_hide_menu_switch": {
"type": "boolean",
"title": "FakeHD hide the menu switch",
"description": "",
"enum": [
true,
false
]
},
"fakehd_columns": {
"type": "string",
"title": "FakeHD column config",
"description": "",
"oneOf": [
{
"const": "T",
"title": "Top"
},
{
"const": "M",
"title": "Middle"
},
{
"const": "B",
"title": "Bottom"
},
{
"const": "S",
"title": "Split"
}
]
},
"fakehd_rows": {
"type": "string",
"title": "FakeHD row config",
"description": "16 characters; each one of LCRWTFD",
"maxLength": 16,
"minLength": 16,
"pattern": "^[LCRWTFD]{16}$"
}
}
}
]
}
}
}
================================================
FILE: config/goggles/uiSchemaV2.json
================================================
{
"show_waiting": {
"ui:help": "Enables or disables WAITING FOR OSD message"
},
"show_au_data": {
"ui:help": "Enables AU data (temp/voltage) overlay on the right"
},
"rec_enabled": {
"ui:help": "Enable OSD recording to .msp files alongside video"
},
"rec_pb_enabled": {
"ui:help": "Enable OSD playback if .msp file is stored alongside video (ie: OSD recording is enabled)"
},
"hide_diagnostics": {
"ui:help": "Hide the diagnostic information in the bottom right"
},
"fakehd_enable": {
"ui:help": "Enables FakeHD. [Read more](https://github.com/fpv-wtf/msp-osd#fakehd)"
},
"fakehd_lock_center": {
"ui:help": "Lock FakeHD in centered mode all the time; no gaps/spreading out even when you are flying. [Read more](https://github.com/fpv-wtf/msp-osd#i-dont-want-gaps-at-all)"
},
"fakehd_menu_switch": {
"ui:help": "FakeHD will use this character as the menu switch to detect when you are in menus/postflight and triggger centering. [Read more](https://github.com/fpv-wtf/msp-osd#menu-switching---getting-rid-of-gaps-when-displaying-menu--post-flight-stats--displaying-centered)"
},
"fakehd_hide_menu_switch": {
"ui:help": "FakeHD will hide the menu switch set above; and the next 5 characters"
},
"fakehd_columns": {
"ui:help": "FakeHD column alignment config. [Read more](https://github.com/fpv-wtf/msp-osd#customising-the-default-fakehd-grid)"
},
"fakehd_rows": {
"ui:help": "FakeHD row alignment config, each character configures the alignment for one row. [Read more](https://github.com/fpv-wtf/msp-osd#customising-the-default-fakehd-grid)"
}
}
================================================
FILE: docs/fonts/gen.sh
================================================
#!/bin/bash
echo "You need https://github.com/shellixyz/hd_fpv_osd_font_tool";
echo "And you need montage from imagemagick";
echo "converting";
find ../../fonts -iname '*.bin' | cut -d '/' -f 4 | cut -d '.' -f 1 | xargs -I '%' ~/.cargo/bin/hd_fpv_osd_font_tool convert djibin:../../fonts/%.bin tiledir:dir_%;
echo "montaging";
find ../../fonts -iname '*.bin' | cut -d '/' -f 4 | cut -d '.' -f 1 | xargs -I '{}' montage 'dir_{}/*.png' -tile 16x -geometry 24x36+1+1 {}.png;
echo "cleaning";
rm -rf ./dir_font*
echo "Finished"
================================================
FILE: ipk/airunit/control/conffiles
================================================
/opt/etc/package-config/msp-osd/config.json
================================================
FILE: ipk/airunit/control/control
================================================
Package: msp-osd
Version: 0.10.1
Maintainer: bri3d
Description: MSP OSD service for the DJI HD FPV airunit.
Architecture: pigeon-airside
Depends: dinit, wtfos-opkg-config, wtfos-package-config
Homepage: https://github.com/fpv-wtf/msp-osd
================================================
FILE: ipk/airunit/control/postinst
================================================
#!/system/bin/sh
/opt/sbin/dinitctl -u enable msp-osd-airside || true
================================================
FILE: ipk/airunit/control/prerm
================================================
#!/system/bin/sh
/opt/sbin/dinitctl -u disable msp-osd-airside || true
================================================
FILE: ipk/airunit/data/opt/bin/airunit-osd-start.sh
================================================
#!/system/bin/sh
setprop dji.hdvt_uav_service 0
setprop dji.shuttle_service 0
if [ ! -e "/dev/ttyS1_moved" ]
then
mv /dev/ttyS1 /dev/ttyS1_moved
fi
/opt/bin/msp_displayport_mux 192.168.41.2 /dev/ttyS1_moved /dev/ttyS1 &
echo $! > /opt/var/run/airunit-osd-dji.pid
setprop dji.hdvt_uav_service 1
setprop dji.shuttle_service 1
================================================
FILE: ipk/airunit/data/opt/etc/dinit.d/msp-osd-airside
================================================
type = bgprocess
command = /opt/bin/airunit-osd-start.sh
pid-file = /opt/var/run/airunit-osd-dji.pid
restart = true
================================================
FILE: ipk/goggle/control/conffiles
================================================
/opt/etc/package-config/msp-osd/config.json
================================================
FILE: ipk/goggle/control/control
================================================
Package: msp-osd
Version: 0.12.4
Maintainer: bri3d
Description: MSP OSD service for the DJI HD FPV goggles.
Architecture: pigeon-glasses
Depends: wtfos-modloader, wtfos-package-config
Homepage: https://github.com/fpv-wtf/msp-osd
================================================
FILE: ipk/goggle/control/postinst
================================================
#!/system/bin/sh
/opt/sbin/dinitctl -u enable msp-osd-goggles || true
================================================
FILE: ipk/goggle/control/preinst
================================================
#!/system/bin/sh
if [[ -f /opt/fonts ]]; then
rm -f /opt/fonts
fi
/opt/sbin/dinitctl -u disable msp-osd-goggles || true
================================================
FILE: ipk/goggle/control/prerm
================================================
#!/system/bin/sh
/opt/sbin/dinitctl -u disable msp-osd-goggles || true
================================================
FILE: ipk/goggle/data/opt/etc/dinit.d/msp-osd-goggles
================================================
type = scripted
command = modmanager enable diy_glasses displayport_osd_shim
stop-command = modmanager disable diy_glasses displayport_osd_shim
================================================
FILE: jni/Android.mk
================================================
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := duml_hal
LOCAL_SRC_FILES := libduml_hal.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -fPIC -std=c99 -O3
LOCAL_LDFLAGS += -fPIC
LOCAL_LDLIBS := -llog -lz
LOCAL_ARM_NEON := true
LOCAL_MODULE := displayport_osd_shim
LOCAL_SHARED_LIBRARIES := duml_hal
LOCAL_SRC_FILES := \
displayport_osd_shim.c \
fakehd/fakehd.c \
font/font.c \
hw/dji_display.c \
hw/dji_radio_shm.c \
hw/dji_services.c \
json/osd_config.c \
json/parson.c \
lz4/lz4.c \
msp/msp_displayport.c \
msp/msp.c \
net/network.c \
osd_dji_overlay_udp.c \
rec/rec_pb.c \
rec/rec_shim.c \
rec/rec_util.c \
rec/rec.c \
toast/toast.c \
util/fs_util.c \
libspng/spng.c
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
hw/dji_radio_shm.c \
json/osd_config.c \
json/parson.c \
msp_displayport_mux.c \
msp/msp.c \
msp/msp_displayport.c \
net/network.c \
net/serial.c \
util/fs_util.c \
lz4/lz4.c
LOCAL_MODULE := msp_displayport_mux
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
================================================
FILE: jni/Application.mk
================================================
APP_PLATFORM=android-23
APP_ABI=armeabi-v7a
APP_CFLAGS += -DSTDC_HEADERS
APP_OPTIM := release
================================================
FILE: jni/displayport_osd_shim.c
================================================
#include
#include
#include
#include
#include
#include "osd.h"
#include "hw/dji_display.h"
#include "hw/dji_services.h"
// Which window in the creation order is the overlay / top menu. Usually 1.
#define MENU_WINDOW_ORDER 1
static duss_disp_instance_handle_t *disp_instance = NULL;
static duss_hal_obj_handle_t ion_handle = NULL;
static int started = 0;
static int are_v2 = 0;
static int window_count = 0;
static void *menu_window = NULL;
// Patch a seemingly unused window management thread in libtp1801_gui.so to inject our code into the dji_glasses process.
void _ZN24MMSFBWindowManagerThread10threadMainEv(void *this) {
printf("ENTERING MAIN \n");
are_v2 = dji_goggles_are_v2();
while(1) {
if(started == 0 && disp_instance != NULL && ion_handle != NULL) {
printf("ENTERING OSD! fbdev disp %x ion %x\n", disp_instance, ion_handle);
started = 1;
osd_directfb(disp_instance, ion_handle);
}
usleep(50000);
}
}
static void (*_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2)(void *this, uint32_t channel_id) = NULL;
static void *tp1801_gui_lib = NULL;
void _ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId(void *this, uint32_t channel_id) {
if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {
_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 = dlsym(RTLD_NEXT, "_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId");
if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {
tp1801_gui_lib = dlopen("/system/lib/libtp1801_gui.so", 1);
_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 = dlsym(tp1801_gui_lib, "_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId");
if (_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2 == NULL) {
printf("dlsym: %s\n", dlerror());
}
}
}
// RTOS video channels:
// 1 = Playback
// 2 = Unknown
// 3 = Live Video
// 4 = AV-IN
if(channel_id == 3) {
osd_enable();
} else {
osd_disable();
}
_ZN23GlassVideoChnlUIManager19setNextVideoChannelE19GlassVideoChannelId2(this, channel_id);
}
static void *hal_lib = NULL;
static duss_result_t (*duss_hal_mem_alloc2)(duss_hal_obj_handle_t handle, duss_hal_mem_handle_t *mem_handle, uint32_t size, uint32_t param1, uint32_t param2, uint32_t param3) = 0;
// Patch libduml_hal's duss_hal_mem_alloc.
// Use the first invocation of this to steal the duss_hal_obj_handle_t pointing to the ion shared memory service.
duss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t handle, duss_hal_mem_handle_t *mem_handle, uint32_t size, uint32_t param1, uint32_t param2, uint32_t param3) {
if (duss_hal_mem_alloc2 == NULL) {
duss_hal_mem_alloc2 = dlsym(RTLD_NEXT, "duss_hal_mem_alloc");
if (duss_hal_mem_alloc2 == 0){
if (hal_lib == NULL){
hal_lib = dlopen("/system/lib/libduml_hal.so", 1);
}
duss_hal_mem_alloc2 = dlsym(hal_lib, "duss_hal_mem_alloc");
if (duss_hal_mem_alloc2 == 0) {
printf("dlsym: %s\n", dlerror());
return -1;
}
}
}
if (ion_handle == NULL) {
ion_handle = handle;
}
return duss_hal_mem_alloc2(handle, mem_handle, size, param1, param2, param3);
}
static duss_result_t (*duss_hal_display_aquire_plane2)(duss_disp_instance_handle_t * , duss_disp_plane_type_t , duss_disp_plane_id_t * ) = 0;
// Patch libduml_hal's duss_hal_display_aquire_plane.
// Use the first invocation of this with plane == 5 to steal the duss_hal_instance_handle_t pointing to the display driver.
duss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t *disp, duss_disp_plane_type_t plane_type, duss_disp_plane_id_t *plane_id) {
if (duss_hal_display_aquire_plane2 == NULL) {
duss_hal_display_aquire_plane2 = dlsym(RTLD_NEXT, "duss_hal_display_aquire_plane");
if (duss_hal_display_aquire_plane2 == 0){
if(hal_lib == NULL) {
hal_lib = dlopen("/system/lib/libduml_hal.so", 1);
}
duss_hal_display_aquire_plane2 = dlsym(hal_lib, "duss_hal_display_aquire_plane");
if (duss_hal_display_aquire_plane2 == 0) {
printf("dlsym: %s\n", dlerror());
return -1;
}
}
}
if(disp_instance == NULL && *plane_id == 5) {
disp_instance = disp;
}
return duss_hal_display_aquire_plane2(disp, plane_type, plane_id);
}
duss_result_t (*duss_hal_display_plane_blending_set2)(duss_disp_instance_handle_t *disp, duss_disp_plane_id_t plane_id, duss_disp_plane_blending_t *blending) = NULL;
duss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t *disp, duss_disp_plane_id_t plane_id, duss_disp_plane_blending_t *blending) {
if (duss_hal_display_plane_blending_set2 == NULL) {
duss_hal_display_plane_blending_set2 = dlsym(RTLD_NEXT, "duss_hal_display_plane_blending_set");
if (duss_hal_display_plane_blending_set2 == NULL) {
if (hal_lib == NULL) {
hal_lib = dlopen("/system/lib/libduml_hal.so", 1);
}
duss_hal_display_plane_blending_set2 = dlsym(hal_lib, "duss_hal_display_plane_blending_set");
if (duss_hal_display_plane_blending_set2 == NULL) {
printf("dlsym: %s\n", dlerror());
return -1;
}
}
}
// Patch blending order for DJI UI on V1 Goggles to match V2, so we can draw under them.
if (blending->order == 1) {
blending->order = 4;
}
return duss_hal_display_plane_blending_set2(disp, plane_id, blending);
}
================================================
FILE: jni/fakehd/fakehd.c
================================================
#include
#include
#include
#include "json/osd_config.h"
#include "toast/toast.h"
#define FAKEHD_ENABLE_KEY "fakehd_enable"
#define FAKEHD_LOCK_CENTER_KEY "fakehd_lock_center"
#define FAKEHD_HIDE_THROTTLE_KEY "fakehd_hide_throttle_element" // for compat
#define FAKEHD_MENU_SWITCH_KEY "fakehd_menu_switch"
#define FAKEHD_HIDE_MENU_SWITCH_KEY "fakehd_hide_menu_switch" // for compat
#define FAKEHD_LAYOUT_DEBUG_KEY "fakehd_layout_debug"
#define FAKEHD_COLUMNS_KEY "fakehd_columns"
#define FAKEHD_ROWS_KEY "fakehd_rows"
#define INPUT_ROWS 16
#define INPUT_COLS 30
int fakehd_enabled = 0;
static int fakehd_hide_menu_switch = 0;
static int fakehd_lock_center = 0;
static int fakehd_layout_debug = 0;
static int fakehd_menu_switch_char = 4; // betaflight throttle icon
static int fakehd_trigger_x = 99;
static int fakehd_trigger_y = 99;
static char fakehd_columns = 'S';
static char fakehd_rows[INPUT_COLS] = "WWWWWWCCWWWWWWWD";
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) fprintf(stderr, fmt, ##args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
void load_fakehd_config()
{
DEBUG_PRINT("checking for fakehd enabled\n");
if (get_boolean_config_value(FAKEHD_ENABLE_KEY))
{
DEBUG_PRINT("fakehd enabled\n");
toast("FAKEHD ENABLED");
fakehd_enabled = 1;
}
else
{
DEBUG_PRINT("fakehd disabled\n");
}
DEBUG_PRINT("checking for fakehd layout debug\n");
if (get_boolean_config_value(FAKEHD_LAYOUT_DEBUG_KEY))
{
DEBUG_PRINT("fakehd layout debug\n");
fakehd_layout_debug = 1;
}
else
{
DEBUG_PRINT("fakehd layout debug off\n");
}
DEBUG_PRINT("checking for fakehd hide throttle \n");
if (get_boolean_config_value(FAKEHD_HIDE_MENU_SWITCH_KEY))
{
DEBUG_PRINT("fakehd hide throttle\n");
fakehd_hide_menu_switch = 1;
}
else
{
DEBUG_PRINT("fakehd no hide throttle\n");
}
DEBUG_PRINT("checking for fakehd lock center \n");
if (get_boolean_config_value(FAKEHD_LOCK_CENTER_KEY))
{
DEBUG_PRINT("fakehd lock center\n");
fakehd_lock_center = 1;
}
else
{
DEBUG_PRINT("fakehd no lock center\n");
}
int trigger = get_integer_config_value(FAKEHD_MENU_SWITCH_KEY);
if (trigger)
{
DEBUG_PRINT("fakehd found custom trigger\n");
fakehd_menu_switch_char = trigger;
toast("FHD MENU SWITCH %c", 4);
}
// trigger
// rows
const char * rows = get_string_config_value(FAKEHD_ROWS_KEY);
if (rows) {
DEBUG_PRINT("fakehd found custom row conf\n");
memcpy(fakehd_rows, rows, INPUT_COLS);
}
const char * cols = get_string_config_value(FAKEHD_COLUMNS_KEY);
if (cols)
{
DEBUG_PRINT("fakehd found col conf\n");
fakehd_columns = cols[0];
}
DEBUG_PRINT("fakehd finished config init\n");
}
static void fakehd_get_column_config(int cols[INPUT_ROWS])
{
switch (fakehd_columns)
{
case 'T':
memcpy(cols, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, sizeof(cols[0]) * INPUT_ROWS);
break;
case 'M':
memcpy(cols, (int[]){3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, sizeof(cols[0]) * INPUT_ROWS);
break;
case 'B':
memcpy(cols, (int[]){6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}, sizeof(cols[0]) * INPUT_ROWS);
break;
case 'S':
default:
memcpy(cols, (int[]){0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 21}, sizeof(cols[0]) * INPUT_ROWS);
break;
}
// If more flexibility needed / when config allows - I suggest the 'default' switch block is separated
// and used to lookup the mapping from the config file, letting the user define extras?
}
static void fakehd_get_row_config(int rownum, int row[INPUT_COLS])
{
char rowmode = fakehd_rows[rownum];
switch (rowmode)
{
case 'L':
memcpy(row, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}, sizeof(row[0]) * INPUT_COLS);
break;
case 'C':
memcpy(row, (int[]){15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}, sizeof(row[0]) * INPUT_COLS);
break;
case 'R':
memcpy(row, (int[]){30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, sizeof(row[0]) * INPUT_COLS);
break;
case 'T':
memcpy(row, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, sizeof(row[0]) * INPUT_COLS);
break;
case 'F':
memcpy(row, (int[]){10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}, sizeof(row[0]) * INPUT_COLS);
break;
case 'D':
memcpy(row, (int[]){12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41}, sizeof(row[0]) * INPUT_COLS);
break;
case 'W':
default:
memcpy(row, (int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, sizeof(row[0]) * INPUT_COLS);
break;
}
// If more flexibility needed / when config allows - I suggest the 'default' switch block is separated
// and used to lookup the mapping from the config file, letting the user define extras?
}
// when possible, this should be called on reconnect. it will do what's needed to put fakehd back
// into fresh booted state
void fakehd_reset() {
// clear saved centering trigger position
int fakehd_trigger_x = 99;
int fakehd_trigger_y = 99;
}
void fakehd_enable()
{
fakehd_enabled = 1;
}
void fakehd_disable()
{
fakehd_enabled = 0;
fakehd_reset();
}
int fakehd_is_enabled() {
return fakehd_enabled;
}
void fakehd_map_sd_character_map_to_hd(uint16_t sd_character_map[60][22], uint16_t hd_character_map[60][22])
{
int row[INPUT_COLS];
int col[INPUT_ROWS];
fakehd_get_column_config(col);
int render_x = 0;
int render_y = 0;
for (int y = INPUT_ROWS-1; y >= 0; y--)
{
fakehd_get_row_config(y, row);
for (int x = INPUT_COLS-1; x >= 0; x--)
{
// to visualise the layout better in dev
if (fakehd_layout_debug && sd_character_map[x][y] == 0) {
sd_character_map[x][y] = 48 + (x % 10);
}
// skip if it's not a character
if (sd_character_map[x][y] != 0)
{
// if current element is fly min or throttle icon
// record the current position as the 'trigger' position
if (fakehd_trigger_x == 99 && sd_character_map[x][y] == fakehd_menu_switch_char)
{
DEBUG_PRINT("found fakehd triggger \n");
fakehd_trigger_x = x;
fakehd_trigger_y = y;
}
// if we have seen a trigger (see above) - and it's now gone, switch to centering
// this is intented to center the menu + postflight stats, which don't contain
// timer/battery symbols
if (
fakehd_lock_center ||
(fakehd_trigger_x != 99 &&
sd_character_map[fakehd_trigger_x][fakehd_trigger_y] != fakehd_menu_switch_char))
{
render_x = x + 15;
render_y = y + 3;
}
else
{
render_y = col[y];
render_x = row[x];
}
// 0 out the throttle element if configured to do so
// and also the three adjacent positions where the thottle percent will be
if (fakehd_trigger_x != 99 &&
fakehd_hide_menu_switch &&
sd_character_map[x][y] == fakehd_menu_switch_char)
{
hd_character_map[render_x][render_y] = 0;
(render_x <= 57) && (hd_character_map[render_x + 1][render_y] = 0);
(render_x <= 56) && (hd_character_map[render_x + 2][render_y] = 0);
(render_x <= 55) && (hd_character_map[render_x + 3][render_y] = 0);
(render_x <= 54) && (hd_character_map[render_x + 4][render_y] = 0);
(render_x <= 53) && (hd_character_map[render_x + 5][render_y] = 0);
}
else
{
// otherwise, the normal path
hd_character_map[render_x][render_y] = sd_character_map[x][y];
}
}
}
}
}
================================================
FILE: jni/fakehd/fakehd.h
================================================
#pragma once
void load_fakehd_config();
void fakehd_disable();
void fakehd_enable();
int fakehd_is_enabled();
void fakehd_reset();
void fakehd_map_sd_character_map_to_hd(uint16_t sd_character_map[60][22], uint16_t hd_character_map[60][22]);
================================================
FILE: jni/font/font.c
================================================
#include
#include
#include
#include
#include
#include
// #include
#include "../libspng/spng.h"
#include "font.h"
#include "../util/debug.h"
#define BYTES_PER_PIXEL 4
#define HD_FONT_WIDTH 24
/* Font helper methods */
void get_font_path_with_extension(char *font_path_dest, const char *font_path, const char *extension, uint8_t len, uint8_t is_hd, const char *font_variant)
{
char name_buf[len];
char res_buf[len];
if (font_variant != NULL && strlen(font_variant) > 0)
{
snprintf(name_buf, len, "%s_%s", font_path, font_variant);
} else {
snprintf(name_buf, len, "%s", font_path);
}
if (is_hd)
{
// surely there's a better way...
snprintf(res_buf, len, "%s", "_hd");
} else {
snprintf(res_buf, len, "%s", "");
}
snprintf(font_path_dest, len, "%s%s%s", name_buf, res_buf, extension);
DEBUG_PRINT("Font path: %s\n", font_path_dest);
}
static int open_font(const char *filename, display_info_t *display_info, const char *font_variant)
{
char file_path[255];
int is_hd = (display_info->font_width == HD_FONT_WIDTH) ? 1 : 0;
get_font_path_with_extension(file_path, filename, ".png", 255, is_hd, font_variant);
DEBUG_PRINT("Opening font: %s\n", file_path);
struct stat st;
memset(&st, 0, sizeof(st));
stat(file_path, &st);
size_t filesize = st.st_size;
if(!(filesize > 0)) {
DEBUG_PRINT("Font file did not exist: %s\n", file_path);
return -1;
}
FILE *fd = fopen(file_path, "rb");
if (!fd) {
DEBUG_PRINT("Could not open file %s\n", file_path);
return -1;
}
spng_ctx *ctx = spng_ctx_new(0);
DEBUG_PRINT("Allocated PNG context\n");
// Set some kind of reasonable PNG limit so we don't get blown up
size_t limit = 1024 * 1024 * 64;
spng_set_chunk_limits(ctx, limit, limit);
DEBUG_PRINT("Set PNG chunk limits\n");
spng_set_png_file(ctx, fd);
DEBUG_PRINT("Set PNG file\n");
struct spng_ihdr ihdr;
int ret = spng_get_ihdr(ctx, &ihdr);
DEBUG_PRINT("Got PNG header\n");
if(ret)
{
printf("spng_get_ihdr() error: %s\n", spng_strerror(ret));
goto err;
}
if(ihdr.height != display_info->font_height * NUM_CHARS) {
printf("font invalid height, got %d wanted %d\n", ihdr.height, display_info->font_height * NUM_CHARS);
goto err;
}
if(ihdr.width % display_info->font_width != 0) {
printf("font invalid width, not a multiple of %d\n", display_info->font_width);
goto err;
}
DEBUG_PRINT("Image pixel size %d x %d\n", ihdr.width, ihdr.height);
int num_pages = ihdr.width / display_info->font_width;
DEBUG_PRINT("Font has %d pages\n", num_pages);
size_t image_size = 0;
int fmt = SPNG_FMT_RGBA8;
ret = spng_decoded_image_size(ctx, fmt, &image_size);
if(ret) {
goto err;
}
DEBUG_PRINT("Allocating image size %d\n", image_size);
void* font_data = malloc(image_size);
ret = spng_decode_image(ctx, font_data, image_size, SPNG_FMT_RGBA8, 0);
if(ret) {
printf("Failed to decode PNG!\n");
free(font_data);
goto err;
}
for(int page = 0; page < num_pages; page++) {
DEBUG_PRINT("Loading font page %d of %d, placing %x\n", page, num_pages, display_info->fonts);
display_info->fonts[page] = malloc(display_info->font_width * display_info->font_height * NUM_CHARS * BYTES_PER_PIXEL);
DEBUG_PRINT("Allocated %d bytes for font page buf at%x\n", display_info->font_width * display_info->font_height * NUM_CHARS * BYTES_PER_PIXEL, display_info->fonts[page]);
for(int char_num = 0; char_num < NUM_CHARS; char_num++) {
for(int y = 0; y < display_info->font_height; y++) {
// Copy each character line at a time into the correct font buffer
int char_width_bytes = display_info->font_width * BYTES_PER_PIXEL;
int char_size_bytes_dest = (display_info->font_width * display_info->font_height * BYTES_PER_PIXEL);
int char_size_bytes_src = (ihdr.width * display_info->font_height * BYTES_PER_PIXEL);
memcpy((uint8_t *)display_info->fonts[page] + (char_num * char_size_bytes_dest) + (y * char_width_bytes), (uint8_t *)font_data + (char_num * char_size_bytes_src) + (ihdr.width * y * BYTES_PER_PIXEL) + (page * char_width_bytes), char_width_bytes);
}
}
}
free(font_data);
spng_ctx_free(ctx);
fclose(fd);
return 0;
err:
spng_ctx_free(ctx);
fclose(fd);
return -1;
}
void load_font(display_info_t *display_info, const char *font_variant) {
// Note: load_font will not replace an existing font.
if(display_info->fonts[0] == NULL) {
int loaded_font = 0;
DEBUG_PRINT("IN LOAD_FONT\n");
// create a copy of font_variant
char font_variant_lower[5] = "";
if (font_variant != NULL)
{
DEBUG_PRINT("Lowercasing variant\n");
size_t length = strlen(font_variant);
for (size_t i = 0; i < length && i < 4; i++) // Ensure not to exceed array bounds
{
font_variant_lower[i] = tolower(font_variant[i]);
}
}
else
{
DEBUG_PRINT("Font variant is NULL\n");
}
DEBUG_PRINT("Loading font %s\n", font_variant_lower);
char *fallback_font_variant = "";
if (strcmp(font_variant_lower, "btfl") == 0)
{
DEBUG_PRINT("Setting fallback font variant to bf\n");
fallback_font_variant = "bf";
}
else if (strcmp(font_variant_lower, "ultr") == 0)
{
DEBUG_PRINT("Setting fallback font variant to ultra\n");
fallback_font_variant = "ultra";
}
// try the three paths for the current font
DEBUG_PRINT("Loading from: %s %s\n", SDCARD_FONT_PATH, font_variant_lower);
loaded_font = open_font(SDCARD_FONT_PATH, display_info, font_variant_lower);
if (loaded_font < 0 && strcmp(fallback_font_variant, "") != 0)
{
DEBUG_PRINT("Loading fallback variant from: %s %s\n", SDCARD_FONT_PATH, fallback_font_variant);
loaded_font = open_font(SDCARD_FONT_PATH, display_info, fallback_font_variant);
}
if (loaded_font < 0)
{
DEBUG_PRINT("Loading from: %s %s\n", FALLBACK_FONT_PATH, font_variant_lower);
loaded_font = open_font(FALLBACK_FONT_PATH, display_info, font_variant_lower);
}
if (loaded_font < 0 && strcmp(fallback_font_variant, "") != 0)
{
DEBUG_PRINT("Loading fallback variant from: %s %s\n", FALLBACK_FONT_PATH, fallback_font_variant);
loaded_font = open_font(FALLBACK_FONT_PATH, display_info, fallback_font_variant);
}
if (loaded_font < 0)
{
DEBUG_PRINT("Loading from: %s %s\n", ENTWARE_FONT_PATH, font_variant_lower);
loaded_font = open_font(ENTWARE_FONT_PATH, display_info, font_variant_lower);
}
// finally, if we have no fonts for this FC, fallback to the default font
if (loaded_font)
{
DEBUG_PRINT("Loading generic from: %s\n", SDCARD_FONT_PATH);
loaded_font = open_font(SDCARD_FONT_PATH, display_info, "");
if (loaded_font < 0)
{
DEBUG_PRINT("Loading generic from: %s\n", FALLBACK_FONT_PATH);
loaded_font = open_font(FALLBACK_FONT_PATH, display_info, "");
}
if (loaded_font < 0)
{
DEBUG_PRINT("Loading generic from: %s\n", ENTWARE_FONT_PATH);
loaded_font = open_font(ENTWARE_FONT_PATH, display_info, "");
}
}
}
}
void close_font(display_info_t *display_info) {
for(int i = 0; i < NUM_FONT_PAGES; i++) {
if(display_info->fonts[i] != NULL) {
free(display_info->fonts[i]);
display_info->fonts[i] = NULL;
}
}
}
================================================
FILE: jni/font/font.h
================================================
#pragma once
#include
#include "../util/display_info.h"
#define FALLBACK_FONT_PATH "/blackbox/font"
#define ENTWARE_FONT_PATH "/opt/fonts/font"
#define SDCARD_FONT_PATH "/storage/sdcard0/font"
#define FALLBACK_FONT NULL
typedef enum
{
FONT_VARIANT_GENERIC,
FONT_VARIANT_BETAFLIGHT,
FONT_VARIANT_INAV,
FONT_VARIANT_ARDUPILOT,
FONT_VARIANT_KISS_ULTRA,
FONT_VARIANT_QUICKSILVER,
FONT_VARIANT_COUNT
} font_variant_e;
void load_font(display_info_t *display_info, const char *font_variant);
void close_font(display_info_t *display_info);
void get_font_path_with_extension(char *font_path_dest, const char *font_path, const char *extension, uint8_t len, uint8_t is_hd, const char *font_variant);
================================================
FILE: jni/hw/dji_display.c
================================================
#include
#include "dji_display.h"
#include "util/debug.h"
#define GOGGLES_V1_VOFFSET 575
#define GOGGLES_V2_VOFFSET 215
dji_display_state_t *dji_display_state_alloc(uint8_t is_v2_goggles) {
dji_display_state_t *display_state = calloc(1, sizeof(dji_display_state_t));
display_state->disp_instance_handle = (duss_disp_instance_handle_t *)calloc(1, sizeof(duss_disp_instance_handle_t));
display_state->fb_0 = (duss_frame_buffer_t *)calloc(1,sizeof(duss_frame_buffer_t));
display_state->fb_1 = (duss_frame_buffer_t *)calloc(1,sizeof(duss_frame_buffer_t));
display_state->pb_0 = (duss_disp_plane_blending_t *)calloc(1, sizeof(duss_disp_plane_blending_t));
display_state->is_v2_goggles = is_v2_goggles;
display_state->frame_drawn = 0;
return display_state;
}
void dji_display_state_free(dji_display_state_t *display_state) {
free(display_state->disp_instance_handle);
free(display_state->fb_0);
free(display_state->fb_1);
free(display_state->pb_0);
free(display_state);
}
void dji_display_close_framebuffer(dji_display_state_t *display_state) {
duss_hal_display_port_enable(display_state->disp_instance_handle, 3, 0);
duss_hal_display_release_plane(display_state->disp_instance_handle, display_state->plane_id);
duss_hal_display_close(display_state->disp_handle, &display_state->disp_instance_handle);
duss_hal_mem_free(display_state->ion_buf_0);
duss_hal_mem_free(display_state->ion_buf_1);
duss_hal_device_close(display_state->disp_handle);
duss_hal_device_stop(display_state->ion_handle);
duss_hal_device_close(display_state->ion_handle);
duss_hal_deinitialize();
}
void dji_display_open_framebuffer(dji_display_state_t *display_state, duss_disp_plane_id_t plane_id) {
uint32_t hal_device_open_unk = 0;
duss_result_t res = 0;
display_state->plane_id = plane_id;
// PLANE BLENDING
display_state->pb_0->is_enable = 1;
display_state->pb_0->voffset = GOGGLES_V1_VOFFSET; // TODO just check hwid to figure this out
display_state->pb_0->hoffset = 0;
display_state->pb_0->order = 2;
// Global alpha - disable as we want per pixel alpha.
display_state->pb_0->glb_alpha_en = 0;
display_state->pb_0->glb_alpha_val = 0;
// Blending algorithm 1 seems to work.
display_state->pb_0->blending_alg = 1;
duss_hal_device_desc_t device_descs[3] = {
{"/dev/dji_display", &duss_hal_attach_disp, &duss_hal_detach_disp, 0x0},
{"/dev/ion", &duss_hal_attach_ion_mem, &duss_hal_detach_ion_mem, 0x0},
{0,0,0,0}
};
duss_hal_initialize(device_descs);
res = duss_hal_device_open("/dev/dji_display",&hal_device_open_unk,&display_state->disp_handle);
if (res != 0) {
printf("failed to open dji_display device");
exit(0);
}
res = duss_hal_display_open(display_state->disp_handle, &display_state->disp_instance_handle, 0);
if (res != 0) {
printf("failed to open display hal");
exit(0);
}
res = duss_hal_display_reset(display_state->disp_instance_handle);
if (res != 0) {
printf("failed to reset display");
exit(0);
}
// No idea what this "plane mode" actually does but it's different on V2
uint8_t acquire_plane_mode = display_state->is_v2_goggles ? 6 : 0;
res = duss_hal_display_aquire_plane(display_state->disp_instance_handle,acquire_plane_mode,&plane_id);
if (res != 0) {
printf("failed to acquire plane");
exit(0);
}
res = duss_hal_display_port_enable(display_state->disp_instance_handle, 3, 1);
if (res != 0) {
printf("failed to enable display port");
exit(0);
}
res = duss_hal_display_plane_blending_set(display_state->disp_instance_handle, plane_id, display_state->pb_0);
if (res != 0) {
printf("failed to set blending");
exit(0);
}
res = duss_hal_device_open("/dev/ion", &hal_device_open_unk, &display_state->ion_handle);
if (res != 0) {
printf("failed to open shared VRAM");
exit(0);
}
res = duss_hal_device_start(display_state->ion_handle,0);
if (res != 0) {
printf("failed to start VRAM device");
exit(0);
}
res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_0,0x473100,0x400,0,0x17);
if (res != 0) {
printf("failed to allocate VRAM");
exit(0);
}
res = duss_hal_mem_map(display_state->ion_buf_0, &display_state->fb0_virtual_addr);
if (res != 0) {
printf("failed to map VRAM");
exit(0);
}
res = duss_hal_mem_get_phys_addr(display_state->ion_buf_0, &display_state->fb0_physical_addr);
if (res != 0) {
printf("failed to get FB0 phys addr");
exit(0);
}
printf("first buffer VRAM mapped virtual memory is at %p : %p\n", display_state->fb0_virtual_addr, display_state->fb0_physical_addr);
res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_1,0x473100,0x400,0,0x17);
if (res != 0) {
printf("failed to allocate FB1 VRAM");
exit(0);
}
res = duss_hal_mem_map(display_state->ion_buf_1,&display_state->fb1_virtual_addr);
if (res != 0) {
printf("failed to map FB1 VRAM");
exit(0);
}
res = duss_hal_mem_get_phys_addr(display_state->ion_buf_1, &display_state->fb1_physical_addr);
if (res != 0) {
printf("failed to get FB1 phys addr");
exit(0);
}
printf("second buffer VRAM mapped virtual memory is at %p : %p\n", display_state->fb1_virtual_addr, display_state->fb1_physical_addr);
for(int i = 0; i < 2; i++) {
duss_frame_buffer_t *fb = i ? display_state->fb_1 : display_state->fb_0;
fb->buffer = i ? display_state->ion_buf_1 : display_state->ion_buf_0;
fb->pixel_format = display_state->is_v2_goggles ? DUSS_PIXFMT_RGBA8888_GOGGLES_V2 : DUSS_PIXFMT_RGBA8888; // 20012 instead on V2
fb->frame_id = i;
fb->planes[0].bytes_per_line = 0x1680;
fb->planes[0].offset = 0;
fb->planes[0].plane_height = 810;
fb->planes[0].bytes_written = 0x473100;
fb->width = 1440;
fb->height = 810;
fb->plane_count = 1;
}
}
void dji_display_open_framebuffer_injected(dji_display_state_t *display_state, duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle, duss_disp_plane_id_t plane_id) {
uint32_t hal_device_open_unk = 0;
duss_result_t res = 0;
display_state->disp_instance_handle = disp;
display_state->ion_handle = ion_handle;
display_state->plane_id = plane_id;
// PLANE BLENDING
display_state->pb_0->is_enable = 1;
// TODO just check hwid to figure this out. Not actually V1/V2 related but an HW version ID.
display_state->pb_0->voffset = GOGGLES_V1_VOFFSET;
display_state->pb_0->hoffset = 0;
// On Goggles V1, the UI and video are in Z-Order 1.
// On Goggles V2, they're in Z-Order 4, but we inline patch them to Z-Order 1 (see displayport_osd_shim.c)
display_state->pb_0->order = 2;
// Global alpha - disable as we want per pixel alpha.
display_state->pb_0->glb_alpha_en = 0;
display_state->pb_0->glb_alpha_val = 0;
// These aren't documented. Blending algorithm 0 is employed for menus and 1 for screensaver.
display_state->pb_0->blending_alg = 1;
// No idea what this "plane mode" actually does but it's different on V2
uint8_t acquire_plane_mode = display_state->is_v2_goggles ? 6 : 0;
DEBUG_PRINT("acquire plane\n");
res = duss_hal_display_aquire_plane(display_state->disp_instance_handle,acquire_plane_mode,&plane_id);
if (res != 0) {
DEBUG_PRINT("failed to acquire plane");
exit(0);
}
res = duss_hal_display_plane_blending_set(display_state->disp_instance_handle, plane_id, display_state->pb_0);
if (res != 0) {
DEBUG_PRINT("failed to set blending");
exit(0);
}
DEBUG_PRINT("alloc ion buf\n");
res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_0,0x473100,0x400,0,0x17);
if (res != 0) {
DEBUG_PRINT("failed to allocate VRAM");
exit(0);
}
res = duss_hal_mem_map(display_state->ion_buf_0, &display_state->fb0_virtual_addr);
if (res != 0) {
DEBUG_PRINT("failed to map VRAM");
exit(0);
}
res = duss_hal_mem_get_phys_addr(display_state->ion_buf_0, &display_state->fb0_physical_addr);
if (res != 0) {
DEBUG_PRINT("failed to get FB0 phys addr");
exit(0);
}
DEBUG_PRINT("first buffer VRAM mapped virtual memory is at %p : %p\n", display_state->fb0_virtual_addr, display_state->fb0_physical_addr);
res = duss_hal_mem_alloc(display_state->ion_handle,&display_state->ion_buf_1,0x473100,0x400,0,0x17);
if (res != 0) {
DEBUG_PRINT("failed to allocate FB1 VRAM");
exit(0);
}
res = duss_hal_mem_map(display_state->ion_buf_1,&display_state->fb1_virtual_addr);
if (res != 0) {
DEBUG_PRINT("failed to map FB1 VRAM");
exit(0);
}
res = duss_hal_mem_get_phys_addr(display_state->ion_buf_1, &display_state->fb1_physical_addr);
if (res != 0) {
DEBUG_PRINT("failed to get FB1 phys addr");
exit(0);
}
DEBUG_PRINT("second buffer VRAM mapped virtual memory is at %p : %p\n", display_state->fb1_virtual_addr, display_state->fb1_physical_addr);
for(int i = 0; i < 2; i++) {
duss_frame_buffer_t *fb = i ? display_state->fb_1 : display_state->fb_0;
fb->buffer = i ? display_state->ion_buf_1 : display_state->ion_buf_0;
fb->pixel_format = display_state->is_v2_goggles ? DUSS_PIXFMT_RGBA8888_GOGGLES_V2 : DUSS_PIXFMT_RGBA8888; // 20012 instead on V2
fb->frame_id = i;
fb->planes[0].bytes_per_line = 0x1680;
fb->planes[0].offset = 0;
fb->planes[0].plane_height = 810;
fb->planes[0].bytes_written = 0x473100;
fb->width = 1440;
fb->height = 810;
fb->plane_count = 1;
}
}
void dji_display_push_frame(dji_display_state_t *display_state) {
if (display_state->frame_drawn == 0) {
duss_frame_buffer_t *fb = display_state->fb_0;
duss_hal_mem_sync(fb->buffer, 1);
display_state->frame_drawn = 1;
printf("fbdebug pushing frame\n");
duss_hal_display_push_frame(display_state->disp_instance_handle, display_state->plane_id, fb);
} else {
DEBUG_PRINT("!!! Dropped frame due to pending frame push!\n");
}
memcpy(display_state->fb0_virtual_addr, display_state->fb1_virtual_addr, sizeof(uint32_t) * 1440 * 810);
}
void *dji_display_get_fb_address(dji_display_state_t *display_state) {
return display_state->fb1_virtual_addr;
}
================================================
FILE: jni/hw/dji_display.h
================================================
#pragma once
#include "duml_hal.h"
typedef struct dji_display_state_s {
duss_disp_plane_id_t plane_id;
duss_hal_obj_handle_t disp_handle;
duss_hal_obj_handle_t ion_handle;
duss_disp_vop_id_t vop_id;
duss_hal_mem_handle_t ion_buf_0;
duss_hal_mem_handle_t ion_buf_1;
void * fb0_virtual_addr;
void * fb0_physical_addr;
void * fb1_virtual_addr;
void * fb1_physical_addr;
duss_disp_instance_handle_t *disp_instance_handle;
duss_frame_buffer_t *fb_0;
duss_frame_buffer_t *fb_1;
duss_disp_plane_blending_t *pb_0;
uint8_t is_v2_goggles;
uint8_t frame_drawn;
} dji_display_state_t;
void dji_display_push_frame(dji_display_state_t *display_state);
void dji_display_open_framebuffer(dji_display_state_t *display_state, duss_disp_plane_id_t plane_id);
void dji_display_open_framebuffer_injected(dji_display_state_t *display_state, duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle, duss_disp_plane_id_t plane_id);
void dji_display_close_framebuffer(dji_display_state_t *display_state);
dji_display_state_t *dji_display_state_alloc(uint8_t is_v2_goggles);
void dji_display_state_free(dji_display_state_t *display_state);
void *dji_display_get_fb_address(dji_display_state_t *display_state);
================================================
FILE: jni/hw/dji_radio_shm.c
================================================
#include
#include
#include
#include
#include "dji_radio_shm.h"
void open_dji_radio_shm(dji_shm_state_t *shm) {
int fd = open("/dev/mem", O_RDWR);
assert(fd > 0);
shm->mapped_address = mmap64(NULL, RTOS_SHM_SIZE, PROT_READ, MAP_SHARED, fd, RTOS_SHM_ADDRESS);
assert(shm->mapped_address != MAP_FAILED);
close(fd);
shm->modem_info = (modem_shmem_info_t *)((uint8_t *)shm->mapped_address + 0x100);
shm->product_info = (product_shm_info_t *)((uint8_t *)shm->mapped_address + 0xC0);
}
void close_dji_radio_shm(dji_shm_state_t *shm) {
munmap(shm->mapped_address, RTOS_SHM_SIZE);
shm->mapped_address = NULL;
shm->modem_info = NULL;
shm->product_info = NULL;
}
uint16_t dji_radio_latency_ms(dji_shm_state_t *shm) {
return shm->product_info->frame_delay_e2e;
}
uint16_t dji_radio_mbits(dji_shm_state_t *shm) {
return shm->modem_info->channel_status;
}
================================================
FILE: jni/hw/dji_radio_shm.h
================================================
#pragma once
#include
#define RTOS_SHM_ADDRESS 0xfffc1000
#define RTOS_SHM_SIZE 0x1000
#define RTOS_PRODUCT_OFFSET 0xc0
#define RTOS_MODEM_OFFSET 0x100
typedef struct modem_shmem_info_s {
uint32_t frm_idx;
uint16_t frm_isI;
uint16_t frm_len;
uint16_t frm_dsti;
uint16_t frm_dstf;
uint16_t channel_status;
uint16_t dec_err_status;
uint16_t cur_time;
uint16_t delta_time;
uint16_t dbg_msc;
uint8_t dbg_ap_ready;
uint8_t dbg_cp_ready;
uint32_t local_id;
uint8_t cp_state;
uint8_t cp_report;
uint8_t cp_report_seq;
uint8_t client_type;
uint32_t cp_boot_status0;
uint32_t cp_boot_status1;
uint32_t board_version;
uint32_t board_sub_version;
uint8_t machine_role;
uint8_t is_reverse;
int8_t cp_tx_power;
uint8_t gnd_type;
uint32_t cp_sssfn;
uint32_t ulow_en;
uint8_t mipi_rx_response;
uint8_t liveview_broken_status;
uint8_t reserved01;
uint8_t reserved02;
uint8_t ap_reboot_flag;
uint8_t ap_reboot_ack_flag;
uint8_t secure_sync_flag;
uint8_t reserve00;
uint8_t area_state;
uint8_t area_substate;
uint8_t GsCtrl;
uint8_t GsSubState;
uint8_t WaterLevel[4];
uint16_t RxCntStastic[4];
uint16_t TxCntStastic[4];
uint8_t Reserved[8];
uint32_t com_uart_status;
uint32_t fcr_rx_status;
uint32_t fcr_tx_status;
uint32_t frm_idx_for_display;
uint32_t frm_delay_for_display;
uint16_t wifi_sdr_mode;
uint16_t frm_isI_for_display;
uint32_t country_code;
uint16_t frm_len_for_display;
uint16_t delta_time_for_display;
uint8_t uint8_t_reboot_reason;
uint8_t field_0x85;
uint8_t field_0x86;
uint8_t field_0x87;
uint64_t cpa7_version;
uint64_t dsp_version;
uint8_t u8_dual_band_capability;
} __attribute__((packed)) modem_shmem_info_t;
typedef struct product_shm_info_s {
uint16_t frm_width;
uint16_t frm_height;
uint8_t fps;
uint8_t enc_strategy;
uint16_t lcdc_underflow_cnt;
uint32_t enc_sto_frm_dropped;
uint32_t enc_lv_frm_dropped;
uint32_t mipi_csi_frm_dropped;
uint32_t display_frm_dropped;
uint64_t audio_pts;
uint32_t local_fps_num;
uint32_t local_fps_den;
uint16_t frame_delay_e2e;
uint16_t cam_frame_interval;
uint16_t outliner_frame_interval;
uint16_t outliner_frame_interval_cnt;
uint16_t max_frame_delay_e2e;
uint16_t min_frame_delay_e2e;
uint16_t avg_frame_delay_e2e;
uint16_t if_switch;
uint8_t if_change_pipe;
uint8_t if_pb_pause;
uint8_t liveview_pipeline_running;
uint8_t avIn_pipeline_running;
uint8_t avIn_stream_type;
uint8_t pb_flush;
uint8_t disp_pannel_need_reset;
uint8_t pad;
} __attribute__((packed)) product_shm_info_t;
typedef struct dji_shm_state_s {
void *mapped_address;
product_shm_info_t *product_info;
modem_shmem_info_t *modem_info;
} dji_shm_state_t;
uint16_t dji_radio_latency_ms(dji_shm_state_t *shm);
uint16_t dji_radio_mbits(dji_shm_state_t *shm);
void close_dji_radio_shm(dji_shm_state_t *shm);
void open_dji_radio_shm(dji_shm_state_t *shm);
================================================
FILE: jni/hw/dji_services.c
================================================
#include
#ifdef __ANDROID_API__
#include
#endif
#define V2_SERVICE_NAME "dji.glasses_wm150_service"
#define V1_SERVICE_NAME "dji.glasses_service"
#define DEVICE_PROPERTY_NAME "ro.product.device"
#define V2_GOGGLES_DEVICE "pigeon_wm170_gls"
void dji_stop_goggles(int is_v2) {
#ifdef __ANDROID_API__
__system_property_set(is_v2 ? V2_SERVICE_NAME : V1_SERVICE_NAME, "0");
#endif
}
void dji_start_goggles(int is_v2) {
#ifdef __ANDROID_API__
__system_property_set(is_v2 ? V2_SERVICE_NAME : V1_SERVICE_NAME, "1");
#endif
}
int dji_goggles_are_v2() {
#ifdef __ANDROID_API__
char goggles_version_response[255];
int len = __system_property_get(DEVICE_PROPERTY_NAME, &goggles_version_response);
return(strcmp(goggles_version_response, V2_GOGGLES_DEVICE) == 0);
#else
return 0;
#endif
}
================================================
FILE: jni/hw/dji_services.h
================================================
#pragma once
void dji_stop_goggles(int is_v2);
void dji_start_goggles(int is_v2);
int dji_goggles_are_v2();
================================================
FILE: jni/hw/duml_hal.h
================================================
#pragma once
#include
typedef int32_t duss_result_t;
typedef uint8_t duss_hal_state_t;
typedef uint8_t duss_hal_class_t;
struct sem_t {
uint32_t count;
};
struct duss_osal_mutex_attrib_t {
char * name;
};
struct duss_osal_mutex_handle_t {
struct duss_osal_mutex_attrib_t attrib;
struct sem_t sema;
};
typedef struct duss_osal_mutex_handle_t duss_osal_mutex_handle_t, *Pduss_osal_mutex_handle_t;
typedef struct duss_hal_obj_dev_t duss_hal_obj_dev_t, *Pduss_hal_obj_dev_t;
typedef struct duss_hal_obj * duss_hal_obj_handle_t;
struct duss_hal_obj_dev_t {
char * name;
duss_hal_state_t obj_state;
duss_hal_class_t obj_class;
uint16_t obj_index;
int32_t obj_refcnt;
struct duss_osal_mutex_handle_t * obj_lock;
struct duss_osal_mutex_handle_t * app_lock;
duss_result_t (* open)(duss_hal_obj_handle_t, void *);
duss_result_t (* close)(duss_hal_obj_handle_t);
duss_result_t (* set_cfg)(duss_hal_obj_handle_t, void *);
duss_result_t (* get_cfg)(duss_hal_obj_handle_t, void *);
duss_result_t (* start)(duss_hal_obj_handle_t, void *);
duss_result_t (* stop)(duss_hal_obj_handle_t, void *);
};
struct duss_hal_obj {
struct duss_hal_obj_dev_t dev;
void * dev_class;
void * dev_priv;
};
struct duss_hal_device_desc_t {
char * name;
duss_result_t (* attach)(char *, duss_hal_obj_handle_t *);
duss_result_t (* detach)(duss_hal_obj_handle_t);
duss_hal_obj_handle_t obj;
};
typedef enum duss_pixel_format {
DUSS_PIXFMT_COMPRESSED_END=30002,
DUSS_PIXFMT_DRAW_V1=30000,
DUSS_PIXFMT_DUAL_PLANE_16BIT=40003,
DUSS_PIXFMT_DUAL_PLANE_8BIT=40002,
DUSS_PIXFMT_GENERAL_END=40004,
DUSS_PIXFMT_JPEG_LS=30001,
DUSS_PIXFMT_OPAQUE=0,
DUSS_PIXFMT_RAW10_PACKED=10004,
DUSS_PIXFMT_RAW12_PACKED=10005,
DUSS_PIXFMT_RAW12_UNPACKED=10008,
DUSS_PIXFMT_RAW14_PACKED=10006,
DUSS_PIXFMT_RAW14_UNPACKED=10009,
DUSS_PIXFMT_RAW16_OPAQUE=10001,
DUSS_PIXFMT_RAW16_UNPACKED=10010,
DUSS_PIXFMT_RAW18_PACKED=10007,
DUSS_PIXFMT_RAW18_UNPACKED=10011,
DUSS_PIXFMT_RAW24_OPAQUE=10002,
DUSS_PIXFMT_RAW24_UNPACKED=10012,
DUSS_PIXFMT_RAW32_OPAQUE=10003,
DUSS_PIXFMT_RAW32_UNPACKED=10013,
DUSS_PIXFMT_RAW8_OPAQUE=10000,
DUSS_PIXFMT_RAW_END=10014,
DUSS_PIXFMT_RGB101010=20003,
DUSS_PIXFMT_RGB565=20001,
DUSS_PIXFMT_RGB888=20000,
DUSS_PIXFMT_RGBA8888=20002,
DUSS_PIXFMT_RGBA8888_GOGGLES_V2=20012,
DUSS_PIXFMT_RGB_END=20004,
DUSS_PIXFMT_SINGLE_PLANE_16BIT=40001,
DUSS_PIXFMT_SINGLE_PLANE_8BIT=40000,
DUSS_PIXFMT_YUV10_420_END=1005,
DUSS_PIXFMT_YUV10_422_END=1105,
DUSS_PIXFMT_YUV10_444_END=1203,
DUSS_PIXFMT_YUV10_P444_YUV=1202,
DUSS_PIXFMT_YUV10_SP420_YUV=1003,
DUSS_PIXFMT_YUV10_SP420_YUV16=1001,
DUSS_PIXFMT_YUV10_SP420_YVU=1004,
DUSS_PIXFMT_YUV10_SP420_YVU16=1002,
DUSS_PIXFMT_YUV10_SP422_YUV=1103,
DUSS_PIXFMT_YUV10_SP422_YUV16=1101,
DUSS_PIXFMT_YUV10_SP422_YVU=1104,
DUSS_PIXFMT_YUV10_Y422_YUYV16=1102,
DUSS_PIXFMT_YUV10_Y444_UYVA=1201,
DUSS_PIXFMT_YUV8_420_END=4,
DUSS_PIXFMT_YUV8_422_END=106,
DUSS_PIXFMT_YUV8_444_END=203,
DUSS_PIXFMT_YUV8_P420_YUV=1,
DUSS_PIXFMT_YUV8_P422_YUV=105,
DUSS_PIXFMT_YUV8_P444_YUV=202,
DUSS_PIXFMT_YUV8_SP420_YUV=2,
DUSS_PIXFMT_YUV8_SP420_YVU=3,
DUSS_PIXFMT_YUV8_SP422_YUV=103,
DUSS_PIXFMT_YUV8_SP422_YVU=104,
DUSS_PIXFMT_YUV8_Y422_UYVY=101,
DUSS_PIXFMT_YUV8_Y422_YUYV=102,
DUSS_PIXFMT_YUV8_Y444_UYV=201,
DUSS_PIXFMT_YUV_END=1204
} duss_pixel_format;
typedef enum duss_pixel_format duss_pixel_format_t;
struct duss_object {
int ref_count;
struct duss_osal_mutex_handle_t * lock;
void (* on_released)(struct duss_object *);
};
typedef struct duss_object duss_object_t;
typedef struct duss_hal_mem_buf * duss_hal_mem_handle_t;
typedef struct duss_frame_plane duss_frame_plane, *Pduss_frame_plane;
typedef struct duss_frame_plane duss_frame_plane_t;
struct duss_frame_plane {
int32_t bytes_per_line;
int32_t offset;
int32_t plane_height;
int32_t bytes_written;
};
struct __attribute__((__packed__)) duss_frame_buffer {
duss_object_t obj;
duss_hal_mem_handle_t buffer;
duss_pixel_format_t pixel_format;
uint32_t unknown;
int64_t time_stamp;
int32_t width;
int32_t height;
duss_frame_plane_t planes[4];
int32_t plane_count;
uint32_t frame_id;
};
typedef struct duss_frame_buffer duss_frame_buffer_t;
typedef struct duss_hal_device_desc_t duss_hal_device_desc_t, *Pduss_hal_device_desc_t;
typedef struct duss_hal_obj duss_hal_obj, *Pduss_hal_obj;
typedef struct duss_disp_instance_handle_t duss_disp_instance_handle_t, *Pduss_disp_instance_handle_t;
struct __attribute__((__packed__)) duss_disp_instance_handle_t {
uint8_t is_init;
uint8_t field_0x1;
uint8_t field_0x2;
uint8_t field_0x3;
uint32_t current_vop_index;
uint32_t ref_cnt;
duss_hal_obj_handle_t obj;
void * instance_private_data;
void * global_disp_info;
};
typedef uint8_t duss_disp_vop_id_t;
typedef uint8_t duss_disp_plane_type_t;
typedef uint8_t duss_disp_port_id_t;
typedef struct duss_disp_timing_detail_t duss_disp_timing_detail_t, *Pduss_disp_timing_detail_t;
typedef enum duss_disp_timing_bitdepth_t {
DUSS_DISP_BITDEPTH_10BIT=1,
DUSS_DISP_BITDEPTH_12BIT=2,
DUSS_DISP_BITDEPTH_8BIT=0
} duss_disp_timing_bitdepth_t;
typedef enum duss_disp_timing_fmt_t {
DUSS_DISP_FMT_RGB=0,
DUSS_DISP_FMT_YUV420=3,
DUSS_DISP_FMT_YUV422=2,
DUSS_DISP_FMT_YUV444=1
} duss_disp_timing_fmt_t;
struct duss_disp_timing_detail_t {
int32_t clock;
int32_t hdisplay;
int32_t hsync_start;
int32_t hsync_end;
int32_t htotal;
int32_t hskew;
int32_t vdisplay;
int32_t vsync_start;
int32_t vsync_end;
int32_t vtotal;
int32_t vscan;
enum duss_disp_timing_fmt_t disp_fmt;
enum duss_disp_timing_bitdepth_t disp_bitdepth;
};
typedef struct duss_disp_plane_blending_t duss_disp_plane_blending_t, *Pduss_disp_plane_blending_t;
typedef uint8_t duss_disp_plane_alpha_alg_t;
typedef int8_t duss_disp_plane_id_t;
struct __attribute__((__packed__)) duss_disp_plane_blending_t {
uint8_t is_enable;
uint8_t glb_alpha_en;
uint8_t field_0x2;
uint8_t field_0x3;
int32_t glb_alpha_val;
duss_disp_plane_alpha_alg_t blending_alg;
duss_disp_plane_id_t order;
uint8_t field_0xa;
uint8_t field_0xb;
int32_t hoffset;
int32_t voffset;
};
typedef duss_result_t (frame_pop_handler)(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_frame_buffer_t * , void * );
duss_result_t duss_hal_initialize(duss_hal_device_desc_t * );
duss_result_t duss_hal_deinitialize();
duss_result_t duss_hal_device_open(char *device_name, void *unknown, duss_hal_obj_handle_t * );
duss_result_t duss_hal_device_start(duss_hal_obj_handle_t , void * );
duss_result_t duss_hal_device_close(duss_hal_obj_handle_t );
duss_result_t duss_hal_device_stop(duss_hal_obj_handle_t );
duss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t , duss_hal_mem_handle_t * , uint32_t size, uint32_t , uint32_t , uint32_t );
duss_result_t duss_hal_mem_get_phys_addr(duss_hal_mem_handle_t , void * * );
duss_result_t duss_hal_mem_map(duss_hal_mem_handle_t , void * * );
duss_result_t duss_hal_mem_free(duss_hal_mem_handle_t );
duss_result_t duss_hal_mem_sync(duss_hal_mem_handle_t , uint32_t );
duss_result_t duss_hal_display_open(duss_hal_obj_handle_t , duss_disp_instance_handle_t * * , duss_disp_vop_id_t );
duss_result_t duss_hal_display_close(duss_hal_obj_handle_t , duss_disp_instance_handle_t * *);
// sic
duss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t * , duss_disp_plane_type_t , duss_disp_plane_id_t * );
duss_result_t duss_hal_display_reset(duss_disp_instance_handle_t *);
duss_result_t duss_hal_display_register_frame_cycle_callback(duss_disp_instance_handle_t * , duss_disp_plane_id_t , frame_pop_handler * , void * );
duss_result_t duss_hal_display_timing_detail_get(duss_disp_instance_handle_t * , duss_disp_timing_detail_t * );
duss_result_t duss_hal_display_port_enable(duss_disp_instance_handle_t * , duss_disp_port_id_t , uint8_t );
duss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_disp_plane_blending_t * );
duss_result_t duss_hal_display_release_plane(duss_disp_instance_handle_t * , duss_disp_plane_id_t );
duss_result_t duss_hal_display_push_frame(duss_disp_instance_handle_t * , duss_disp_plane_id_t , duss_frame_buffer_t * );
duss_result_t duss_hal_attach_disp(char *param_1,duss_hal_obj **param_2);
duss_result_t duss_hal_attach_ion_mem(char *param_1,duss_hal_obj **param_2);
duss_result_t duss_hal_detach_ion_mem();
duss_result_t duss_hal_detach_disp();
================================================
FILE: jni/json/osd_config.c
================================================
#include "parson.h"
#define JSON_CONFIG_PATH "/opt/etc/package-config/msp-osd/config.json"
static JSON_Object *root_object = NULL;
static void load_config() {
JSON_Value *root_value = NULL;
if(root_object == NULL) {
root_value = json_parse_file(JSON_CONFIG_PATH);
if (json_value_get_type(root_value) != JSONObject) {
return;
}
root_object = json_value_get_object(root_value);
}
}
int get_boolean_config_value(const char* key) {
load_config();
if (root_object != NULL) {
// parson returns -1 for undefined keys, which can happen when
// new keys are defined in the schema - ensure they come back as false
return json_object_get_boolean(root_object, key) > 0;
} else {
return 0;
}
}
const char * get_string_config_value(const char *key)
{
load_config();
if (root_object != NULL)
{
return json_object_get_string(root_object, key);
}
else
{
return NULL;
}
}
int get_integer_config_value(const char *key)
{
load_config();
if (root_object != NULL)
{
return (int)json_object_get_number(root_object, key);
}
else
{
return 0;
}
}
================================================
FILE: jni/json/osd_config.h
================================================
int get_boolean_config_value(const char* key);
const char *get_string_config_value(const char *key);
int get_integer_config_value(const char *key);
================================================
FILE: jni/json/parson.c
================================================
/*
SPDX-License-Identifier: MIT
Parson 1.4.0 (https://github.com/kgabis/parson)
Copyright (c) 2012 - 2022 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif /* _CRT_SECURE_NO_WARNINGS */
#endif /* _MSC_VER */
#include "parson.h"
#define PARSON_IMPL_VERSION_MAJOR 1
#define PARSON_IMPL_VERSION_MINOR 4
#define PARSON_IMPL_VERSION_PATCH 0
#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR)\
|| (PARSON_VERSION_MINOR != PARSON_IMPL_VERSION_MINOR)\
|| (PARSON_VERSION_PATCH != PARSON_IMPL_VERSION_PATCH)
#error "parson version mismatch between parson.c and parson.h"
#endif
#include
#include
#include
#include
#include
#include
/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you
* don't have to. */
#ifdef sscanf
#undef sscanf
#define sscanf THINK_TWICE_ABOUT_USING_SSCANF
#endif
/* strcpy is unsafe */
#ifdef strcpy
#undef strcpy
#endif
#define strcpy USE_MEMCPY_INSTEAD_OF_STRCPY
#define STARTING_CAPACITY 16
#define MAX_NESTING 2048
#ifndef PARSON_DEFAULT_FLOAT_FORMAT
#define PARSON_DEFAULT_FLOAT_FORMAT "%1.17g" /* do not increase precision without incresing NUM_BUF_SIZE */
#endif
#ifndef PARSON_NUM_BUF_SIZE
#define PARSON_NUM_BUF_SIZE 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */
#endif
#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
#define SKIP_CHAR(str) ((*str)++)
#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#undef malloc
#undef free
#if defined(isnan) && defined(isinf)
#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x)))
#else
#define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0)
#endif
#define OBJECT_INVALID_IX ((size_t)-1)
static JSON_Malloc_Function parson_malloc = malloc;
static JSON_Free_Function parson_free = free;
static int parson_escape_slashes = 1;
static char *parson_float_format = NULL;
#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
typedef int parson_bool_t;
#define PARSON_TRUE 1
#define PARSON_FALSE 0
typedef struct json_string {
char *chars;
size_t length;
} JSON_String;
/* Type definitions */
typedef union json_value_value {
JSON_String string;
double number;
JSON_Object *object;
JSON_Array *array;
int boolean;
int null;
} JSON_Value_Value;
struct json_value_t {
JSON_Value *parent;
JSON_Value_Type type;
JSON_Value_Value value;
};
struct json_object_t {
JSON_Value *wrapping_value;
size_t *cells;
unsigned long *hashes;
char **names;
JSON_Value **values;
size_t *cell_ixs;
size_t count;
size_t item_capacity;
size_t cell_capacity;
};
struct json_array_t {
JSON_Value *wrapping_value;
JSON_Value **items;
size_t count;
size_t capacity;
};
/* Various */
static char * read_file(const char *filename);
static void remove_comments(char *string, const char *start_token, const char *end_token);
static char * parson_strndup(const char *string, size_t n);
static char * parson_strdup(const char *string);
static int hex_char_to_int(char c);
static JSON_Status parse_utf16_hex(const char *string, unsigned int *result);
static int num_bytes_in_utf8_sequence(unsigned char c);
static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len);
static parson_bool_t is_valid_utf8(const char *string, size_t string_len);
static parson_bool_t is_decimal(const char *string, size_t length);
static unsigned long hash_string(const char *string, size_t n);
/* JSON Object */
static JSON_Object * json_object_make(JSON_Value *wrapping_value);
static JSON_Status json_object_init(JSON_Object *object, size_t capacity);
static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values);
static JSON_Status json_object_grow_and_rehash(JSON_Object *object);
static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found);
static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value);
static JSON_Value * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len);
static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);
static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);
static void json_object_free(JSON_Object *object);
/* JSON Array */
static JSON_Array * json_array_make(JSON_Value *wrapping_value);
static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value);
static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);
static void json_array_free(JSON_Array *array);
/* JSON Value */
static JSON_Value * json_value_init_string_no_copy(char *string, size_t length);
static const JSON_String * json_value_get_string_desc(const JSON_Value *value);
/* Parser */
static JSON_Status skip_quotes(const char **string);
static JSON_Status parse_utf16(const char **unprocessed, char **processed);
static char * process_string(const char *input, size_t input_len, size_t *output_len);
static char * get_quoted_string(const char **string, size_t *output_string_len);
static JSON_Value * parse_object_value(const char **string, size_t nesting);
static JSON_Value * parse_array_value(const char **string, size_t nesting);
static JSON_Value * parse_string_value(const char **string);
static JSON_Value * parse_boolean_value(const char **string);
static JSON_Value * parse_number_value(const char **string);
static JSON_Value * parse_null_value(const char **string);
static JSON_Value * parse_value(const char **string, size_t nesting);
/* Serialization */
static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf);
static int json_serialize_string(const char *string, size_t len, char *buf);
static int append_indent(char *buf, int level);
static int append_string(char *buf, const char *string);
/* Various */
static char * read_file(const char * filename) {
FILE *fp = fopen(filename, "r");
size_t size_to_read = 0;
size_t size_read = 0;
long pos;
char *file_contents;
if (!fp) {
return NULL;
}
fseek(fp, 0L, SEEK_END);
pos = ftell(fp);
if (pos < 0) {
fclose(fp);
return NULL;
}
size_to_read = pos;
rewind(fp);
file_contents = (char*)parson_malloc(sizeof(char) * (size_to_read + 1));
if (!file_contents) {
fclose(fp);
return NULL;
}
size_read = fread(file_contents, 1, size_to_read, fp);
if (size_read == 0 || ferror(fp)) {
fclose(fp);
parson_free(file_contents);
return NULL;
}
fclose(fp);
file_contents[size_read] = '\0';
return file_contents;
}
static void remove_comments(char *string, const char *start_token, const char *end_token) {
parson_bool_t in_string = PARSON_FALSE, escaped = PARSON_FALSE;
size_t i;
char *ptr = NULL, current_char;
size_t start_token_len = strlen(start_token);
size_t end_token_len = strlen(end_token);
if (start_token_len == 0 || end_token_len == 0) {
return;
}
while ((current_char = *string) != '\0') {
if (current_char == '\\' && !escaped) {
escaped = PARSON_TRUE;
string++;
continue;
} else if (current_char == '\"' && !escaped) {
in_string = !in_string;
} else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
for(i = 0; i < start_token_len; i++) {
string[i] = ' ';
}
string = string + start_token_len;
ptr = strstr(string, end_token);
if (!ptr) {
return;
}
for (i = 0; i < (ptr - string) + end_token_len; i++) {
string[i] = ' ';
}
string = ptr + end_token_len - 1;
}
escaped = PARSON_FALSE;
string++;
}
}
static char * parson_strndup(const char *string, size_t n) {
/* We expect the caller has validated that 'n' fits within the input buffer. */
char *output_string = (char*)parson_malloc(n + 1);
if (!output_string) {
return NULL;
}
output_string[n] = '\0';
memcpy(output_string, string, n);
return output_string;
}
static char * parson_strdup(const char *string) {
return parson_strndup(string, strlen(string));
}
static int hex_char_to_int(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
return -1;
}
static JSON_Status parse_utf16_hex(const char *s, unsigned int *result) {
int x1, x2, x3, x4;
if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') {
return JSONFailure;
}
x1 = hex_char_to_int(s[0]);
x2 = hex_char_to_int(s[1]);
x3 = hex_char_to_int(s[2]);
x4 = hex_char_to_int(s[3]);
if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {
return JSONFailure;
}
*result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);
return JSONSuccess;
}
static int num_bytes_in_utf8_sequence(unsigned char c) {
if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
return 0;
} else if ((c & 0x80) == 0) { /* 0xxxxxxx */
return 1;
} else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
return 2;
} else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
return 3;
} else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
return 4;
}
return 0; /* won't happen */
}
static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {
unsigned int cp = 0;
*len = num_bytes_in_utf8_sequence(string[0]);
if (*len == 1) {
cp = string[0];
} else if (*len == 2 && IS_CONT(string[1])) {
cp = string[0] & 0x1F;
cp = (cp << 6) | (string[1] & 0x3F);
} else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
cp = ((unsigned char)string[0]) & 0xF;
cp = (cp << 6) | (string[1] & 0x3F);
cp = (cp << 6) | (string[2] & 0x3F);
} else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
cp = string[0] & 0x7;
cp = (cp << 6) | (string[1] & 0x3F);
cp = (cp << 6) | (string[2] & 0x3F);
cp = (cp << 6) | (string[3] & 0x3F);
} else {
return JSONFailure;
}
/* overlong encodings */
if ((cp < 0x80 && *len > 1) ||
(cp < 0x800 && *len > 2) ||
(cp < 0x10000 && *len > 3)) {
return JSONFailure;
}
/* invalid unicode */
if (cp > 0x10FFFF) {
return JSONFailure;
}
/* surrogate halves */
if (cp >= 0xD800 && cp <= 0xDFFF) {
return JSONFailure;
}
return JSONSuccess;
}
static int is_valid_utf8(const char *string, size_t string_len) {
int len = 0;
const char *string_end = string + string_len;
while (string < string_end) {
if (verify_utf8_sequence((const unsigned char*)string, &len) != JSONSuccess) {
return PARSON_FALSE;
}
string += len;
}
return PARSON_TRUE;
}
static parson_bool_t is_decimal(const char *string, size_t length) {
if (length > 1 && string[0] == '0' && string[1] != '.') {
return PARSON_FALSE;
}
if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
return PARSON_FALSE;
}
while (length--) {
if (strchr("xX", string[length])) {
return PARSON_FALSE;
}
}
return PARSON_TRUE;
}
static unsigned long hash_string(const char *string, size_t n) {
#ifdef PARSON_FORCE_HASH_COLLISIONS
(void)string;
(void)n;
return 0;
#else
unsigned long hash = 5381;
unsigned char c;
size_t i = 0;
for (i = 0; i < n; i++) {
c = string[i];
if (c == '\0') {
break;
}
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
#endif
}
/* JSON Object */
static JSON_Object * json_object_make(JSON_Value *wrapping_value) {
JSON_Status res = JSONFailure;
JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
if (new_obj == NULL) {
return NULL;
}
new_obj->wrapping_value = wrapping_value;
res = json_object_init(new_obj, 0);
if (res != JSONSuccess) {
parson_free(new_obj);
return NULL;
}
return new_obj;
}
static JSON_Status json_object_init(JSON_Object *object, size_t capacity) {
unsigned int i = 0;
object->cells = NULL;
object->names = NULL;
object->values = NULL;
object->cell_ixs = NULL;
object->hashes = NULL;
object->count = 0;
object->cell_capacity = capacity;
object->item_capacity = (unsigned int)(capacity * 0.7f);
if (capacity == 0) {
return JSONSuccess;
}
object->cells = (size_t*)parson_malloc(object->cell_capacity * sizeof(*object->cells));
object->names = (char**)parson_malloc(object->item_capacity * sizeof(*object->names));
object->values = (JSON_Value**)parson_malloc(object->item_capacity * sizeof(*object->values));
object->cell_ixs = (size_t*)parson_malloc(object->item_capacity * sizeof(*object->cell_ixs));
object->hashes = (unsigned long*)parson_malloc(object->item_capacity * sizeof(*object->hashes));
if (object->cells == NULL
|| object->names == NULL
|| object->values == NULL
|| object->cell_ixs == NULL
|| object->hashes == NULL) {
goto error;
}
for (i = 0; i < object->cell_capacity; i++) {
object->cells[i] = OBJECT_INVALID_IX;
}
return JSONSuccess;
error:
parson_free(object->cells);
parson_free(object->names);
parson_free(object->values);
parson_free(object->cell_ixs);
parson_free(object->hashes);
return JSONFailure;
}
static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values) {
unsigned int i = 0;
for (i = 0; i < object->count; i++) {
if (free_keys) {
parson_free(object->names[i]);
}
if (free_values) {
json_value_free(object->values[i]);
}
}
object->count = 0;
object->item_capacity = 0;
object->cell_capacity = 0;
parson_free(object->cells);
parson_free(object->names);
parson_free(object->values);
parson_free(object->cell_ixs);
parson_free(object->hashes);
object->cells = NULL;
object->names = NULL;
object->values = NULL;
object->cell_ixs = NULL;
object->hashes = NULL;
}
static JSON_Status json_object_grow_and_rehash(JSON_Object *object) {
JSON_Value *wrapping_value = NULL;
JSON_Object new_object;
char *key = NULL;
JSON_Value *value = NULL;
unsigned int i = 0;
size_t new_capacity = MAX(object->cell_capacity * 2, STARTING_CAPACITY);
JSON_Status res = json_object_init(&new_object, new_capacity);
if (res != JSONSuccess) {
return JSONFailure;
}
wrapping_value = json_object_get_wrapping_value(object);
new_object.wrapping_value = wrapping_value;
for (i = 0; i < object->count; i++) {
key = object->names[i];
value = object->values[i];
res = json_object_add(&new_object, key, value);
if (res != JSONSuccess) {
json_object_deinit(&new_object, PARSON_FALSE, PARSON_FALSE);
return JSONFailure;
}
value->parent = wrapping_value;
}
json_object_deinit(object, PARSON_FALSE, PARSON_FALSE);
*object = new_object;
return JSONSuccess;
}
static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found) {
size_t cell_ix = hash & (object->cell_capacity - 1);
size_t cell = 0;
size_t ix = 0;
unsigned int i = 0;
unsigned long hash_to_check = 0;
const char *key_to_check = NULL;
size_t key_to_check_len = 0;
*out_found = PARSON_FALSE;
for (i = 0; i < object->cell_capacity; i++) {
ix = (cell_ix + i) & (object->cell_capacity - 1);
cell = object->cells[ix];
if (cell == OBJECT_INVALID_IX) {
return ix;
}
hash_to_check = object->hashes[cell];
if (hash != hash_to_check) {
continue;
}
key_to_check = object->names[cell];
key_to_check_len = strlen(key_to_check);
if (key_to_check_len == key_len && strncmp(key, key_to_check, key_len) == 0) {
*out_found = PARSON_TRUE;
return ix;
}
}
return OBJECT_INVALID_IX;
}
static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value) {
unsigned long hash = 0;
parson_bool_t found = PARSON_FALSE;
size_t cell_ix = 0;
JSON_Status res = JSONFailure;
if (!object || !name || !value) {
return JSONFailure;
}
hash = hash_string(name, strlen(name));
found = PARSON_FALSE;
cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
if (found) {
return JSONFailure;
}
if (object->count >= object->item_capacity) {
res = json_object_grow_and_rehash(object);
if (res != JSONSuccess) {
return JSONFailure;
}
cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
}
object->names[object->count] = name;
object->cells[cell_ix] = object->count;
object->values[object->count] = value;
object->cell_ixs[object->count] = cell_ix;
object->hashes[object->count] = hash;
object->count++;
value->parent = json_object_get_wrapping_value(object);
return JSONSuccess;
}
static JSON_Value * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len) {
unsigned long hash = 0;
parson_bool_t found = PARSON_FALSE;
size_t cell_ix = 0;
size_t item_ix = 0;
if (!object || !name) {
return NULL;
}
hash = hash_string(name, name_len);
found = PARSON_FALSE;
cell_ix = json_object_get_cell_ix(object, name, name_len, hash, &found);
if (!found) {
return NULL;
}
item_ix = object->cells[cell_ix];
return object->values[item_ix];
}
static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {
unsigned long hash = 0;
parson_bool_t found = PARSON_FALSE;
size_t cell = 0;
size_t item_ix = 0;
size_t last_item_ix = 0;
size_t i = 0;
size_t j = 0;
size_t x = 0;
size_t k = 0;
JSON_Value *val = NULL;
if (object == NULL) {
return JSONFailure;
}
hash = hash_string(name, strlen(name));
found = PARSON_FALSE;
cell = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
if (!found) {
return JSONFailure;
}
item_ix = object->cells[cell];
if (free_value) {
val = object->values[item_ix];
json_value_free(val);
val = NULL;
}
parson_free(object->names[item_ix]);
last_item_ix = object->count - 1;
if (item_ix < last_item_ix) {
object->names[item_ix] = object->names[last_item_ix];
object->values[item_ix] = object->values[last_item_ix];
object->cell_ixs[item_ix] = object->cell_ixs[last_item_ix];
object->hashes[item_ix] = object->hashes[last_item_ix];
object->cells[object->cell_ixs[item_ix]] = item_ix;
}
object->count--;
i = cell;
j = i;
for (x = 0; x < (object->cell_capacity - 1); x++) {
j = (j + 1) & (object->cell_capacity - 1);
if (object->cells[j] == OBJECT_INVALID_IX) {
break;
}
k = object->hashes[object->cells[j]] & (object->cell_capacity - 1);
if ((j > i && (k <= i || k > j))
|| (j < i && (k <= i && k > j))) {
object->cell_ixs[object->cells[j]] = i;
object->cells[i] = object->cells[j];
i = j;
}
}
object->cells[i] = OBJECT_INVALID_IX;
return JSONSuccess;
}
static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {
JSON_Value *temp_value = NULL;
JSON_Object *temp_object = NULL;
const char *dot_pos = strchr(name, '.');
if (!dot_pos) {
return json_object_remove_internal(object, name, free_value);
}
temp_value = json_object_getn_value(object, name, dot_pos - name);
if (json_value_get_type(temp_value) != JSONObject) {
return JSONFailure;
}
temp_object = json_value_get_object(temp_value);
return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value);
}
static void json_object_free(JSON_Object *object) {
json_object_deinit(object, PARSON_TRUE, PARSON_TRUE);
parson_free(object);
}
/* JSON Array */
static JSON_Array * json_array_make(JSON_Value *wrapping_value) {
JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));
if (new_array == NULL) {
return NULL;
}
new_array->wrapping_value = wrapping_value;
new_array->items = (JSON_Value**)NULL;
new_array->capacity = 0;
new_array->count = 0;
return new_array;
}
static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) {
if (array->count >= array->capacity) {
size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
if (json_array_resize(array, new_capacity) != JSONSuccess) {
return JSONFailure;
}
}
value->parent = json_array_get_wrapping_value(array);
array->items[array->count] = value;
array->count++;
return JSONSuccess;
}
static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) {
JSON_Value **new_items = NULL;
if (new_capacity == 0) {
return JSONFailure;
}
new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));
if (new_items == NULL) {
return JSONFailure;
}
if (array->items != NULL && array->count > 0) {
memcpy(new_items, array->items, array->count * sizeof(JSON_Value*));
}
parson_free(array->items);
array->items = new_items;
array->capacity = new_capacity;
return JSONSuccess;
}
static void json_array_free(JSON_Array *array) {
size_t i;
for (i = 0; i < array->count; i++) {
json_value_free(array->items[i]);
}
parson_free(array->items);
parson_free(array);
}
/* JSON Value */
static JSON_Value * json_value_init_string_no_copy(char *string, size_t length) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONString;
new_value->value.string.chars = string;
new_value->value.string.length = length;
return new_value;
}
/* Parser */
static JSON_Status skip_quotes(const char **string) {
if (**string != '\"') {
return JSONFailure;
}
SKIP_CHAR(string);
while (**string != '\"') {
if (**string == '\0') {
return JSONFailure;
} else if (**string == '\\') {
SKIP_CHAR(string);
if (**string == '\0') {
return JSONFailure;
}
}
SKIP_CHAR(string);
}
SKIP_CHAR(string);
return JSONSuccess;
}
static JSON_Status parse_utf16(const char **unprocessed, char **processed) {
unsigned int cp, lead, trail;
char *processed_ptr = *processed;
const char *unprocessed_ptr = *unprocessed;
JSON_Status status = JSONFailure;
unprocessed_ptr++; /* skips u */
status = parse_utf16_hex(unprocessed_ptr, &cp);
if (status != JSONSuccess) {
return JSONFailure;
}
if (cp < 0x80) {
processed_ptr[0] = (char)cp; /* 0xxxxxxx */
} else if (cp < 0x800) {
processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
processed_ptr += 1;
} else if (cp < 0xD800 || cp > 0xDFFF) {
processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
processed_ptr += 2;
} else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
lead = cp;
unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */
if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') {
return JSONFailure;
}
status = parse_utf16_hex(unprocessed_ptr, &trail);
if (status != JSONSuccess || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
return JSONFailure;
}
cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;
processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */
processed_ptr += 3;
} else { /* trail surrogate before lead surrogate */
return JSONFailure;
}
unprocessed_ptr += 3;
*processed = processed_ptr;
*unprocessed = unprocessed_ptr;
return JSONSuccess;
}
/* Copies and processes passed string up to supplied length.
Example: "\u006Corem ipsum" -> lorem ipsum */
static char* process_string(const char *input, size_t input_len, size_t *output_len) {
const char *input_ptr = input;
size_t initial_size = (input_len + 1) * sizeof(char);
size_t final_size = 0;
char *output = NULL, *output_ptr = NULL, *resized_output = NULL;
output = (char*)parson_malloc(initial_size);
if (output == NULL) {
goto error;
}
output_ptr = output;
while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < input_len) {
if (*input_ptr == '\\') {
input_ptr++;
switch (*input_ptr) {
case '\"': *output_ptr = '\"'; break;
case '\\': *output_ptr = '\\'; break;
case '/': *output_ptr = '/'; break;
case 'b': *output_ptr = '\b'; break;
case 'f': *output_ptr = '\f'; break;
case 'n': *output_ptr = '\n'; break;
case 'r': *output_ptr = '\r'; break;
case 't': *output_ptr = '\t'; break;
case 'u':
if (parse_utf16(&input_ptr, &output_ptr) != JSONSuccess) {
goto error;
}
break;
default:
goto error;
}
} else if ((unsigned char)*input_ptr < 0x20) {
goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
} else {
*output_ptr = *input_ptr;
}
output_ptr++;
input_ptr++;
}
*output_ptr = '\0';
/* resize to new length */
final_size = (size_t)(output_ptr-output) + 1;
/* todo: don't resize if final_size == initial_size */
resized_output = (char*)parson_malloc(final_size);
if (resized_output == NULL) {
goto error;
}
memcpy(resized_output, output, final_size);
*output_len = final_size - 1;
parson_free(output);
return resized_output;
error:
parson_free(output);
return NULL;
}
/* Return processed contents of a string between quotes and
skips passed argument to a matching quote. */
static char * get_quoted_string(const char **string, size_t *output_string_len) {
const char *string_start = *string;
size_t input_string_len = 0;
JSON_Status status = skip_quotes(string);
if (status != JSONSuccess) {
return NULL;
}
input_string_len = *string - string_start - 2; /* length without quotes */
return process_string(string_start + 1, input_string_len, output_string_len);
}
static JSON_Value * parse_value(const char **string, size_t nesting) {
if (nesting > MAX_NESTING) {
return NULL;
}
SKIP_WHITESPACES(string);
switch (**string) {
case '{':
return parse_object_value(string, nesting + 1);
case '[':
return parse_array_value(string, nesting + 1);
case '\"':
return parse_string_value(string);
case 'f': case 't':
return parse_boolean_value(string);
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return parse_number_value(string);
case 'n':
return parse_null_value(string);
default:
return NULL;
}
}
static JSON_Value * parse_object_value(const char **string, size_t nesting) {
JSON_Status status = JSONFailure;
JSON_Value *output_value = NULL, *new_value = NULL;
JSON_Object *output_object = NULL;
char *new_key = NULL;
output_value = json_value_init_object();
if (output_value == NULL) {
return NULL;
}
if (**string != '{') {
json_value_free(output_value);
return NULL;
}
output_object = json_value_get_object(output_value);
SKIP_CHAR(string);
SKIP_WHITESPACES(string);
if (**string == '}') { /* empty object */
SKIP_CHAR(string);
return output_value;
}
while (**string != '\0') {
size_t key_len = 0;
new_key = get_quoted_string(string, &key_len);
/* We do not support key names with embedded \0 chars */
if (!new_key) {
json_value_free(output_value);
return NULL;
}
if (key_len != strlen(new_key)) {
parson_free(new_key);
json_value_free(output_value);
return NULL;
}
SKIP_WHITESPACES(string);
if (**string != ':') {
parson_free(new_key);
json_value_free(output_value);
return NULL;
}
SKIP_CHAR(string);
new_value = parse_value(string, nesting);
if (new_value == NULL) {
parson_free(new_key);
json_value_free(output_value);
return NULL;
}
status = json_object_add(output_object, new_key, new_value);
if (status != JSONSuccess) {
parson_free(new_key);
json_value_free(new_value);
json_value_free(output_value);
return NULL;
}
SKIP_WHITESPACES(string);
if (**string != ',') {
break;
}
SKIP_CHAR(string);
SKIP_WHITESPACES(string);
if (**string == '}') {
break;
}
}
SKIP_WHITESPACES(string);
if (**string != '}') {
json_value_free(output_value);
return NULL;
}
SKIP_CHAR(string);
return output_value;
}
static JSON_Value * parse_array_value(const char **string, size_t nesting) {
JSON_Value *output_value = NULL, *new_array_value = NULL;
JSON_Array *output_array = NULL;
output_value = json_value_init_array();
if (output_value == NULL) {
return NULL;
}
if (**string != '[') {
json_value_free(output_value);
return NULL;
}
output_array = json_value_get_array(output_value);
SKIP_CHAR(string);
SKIP_WHITESPACES(string);
if (**string == ']') { /* empty array */
SKIP_CHAR(string);
return output_value;
}
while (**string != '\0') {
new_array_value = parse_value(string, nesting);
if (new_array_value == NULL) {
json_value_free(output_value);
return NULL;
}
if (json_array_add(output_array, new_array_value) != JSONSuccess) {
json_value_free(new_array_value);
json_value_free(output_value);
return NULL;
}
SKIP_WHITESPACES(string);
if (**string != ',') {
break;
}
SKIP_CHAR(string);
SKIP_WHITESPACES(string);
if (**string == ']') {
break;
}
}
SKIP_WHITESPACES(string);
if (**string != ']' || /* Trim array after parsing is over */
json_array_resize(output_array, json_array_get_count(output_array)) != JSONSuccess) {
json_value_free(output_value);
return NULL;
}
SKIP_CHAR(string);
return output_value;
}
static JSON_Value * parse_string_value(const char **string) {
JSON_Value *value = NULL;
size_t new_string_len = 0;
char *new_string = get_quoted_string(string, &new_string_len);
if (new_string == NULL) {
return NULL;
}
value = json_value_init_string_no_copy(new_string, new_string_len);
if (value == NULL) {
parson_free(new_string);
return NULL;
}
return value;
}
static JSON_Value * parse_boolean_value(const char **string) {
size_t true_token_size = SIZEOF_TOKEN("true");
size_t false_token_size = SIZEOF_TOKEN("false");
if (strncmp("true", *string, true_token_size) == 0) {
*string += true_token_size;
return json_value_init_boolean(1);
} else if (strncmp("false", *string, false_token_size) == 0) {
*string += false_token_size;
return json_value_init_boolean(0);
}
return NULL;
}
static JSON_Value * parse_number_value(const char **string) {
char *end;
double number = 0;
errno = 0;
number = strtod(*string, &end);
if (errno == ERANGE && (number <= -HUGE_VAL || number >= HUGE_VAL)) {
return NULL;
}
if ((errno && errno != ERANGE) || !is_decimal(*string, end - *string)) {
return NULL;
}
*string = end;
return json_value_init_number(number);
}
static JSON_Value * parse_null_value(const char **string) {
size_t token_size = SIZEOF_TOKEN("null");
if (strncmp("null", *string, token_size) == 0) {
*string += token_size;
return json_value_init_null();
}
return NULL;
}
/* Serialization */
#define APPEND_STRING(str) do { written = append_string(buf, (str));\
if (written < 0) { return -1; }\
if (buf != NULL) { buf += written; }\
written_total += written; } while(0)
#define APPEND_INDENT(level) do { written = append_indent(buf, (level));\
if (written < 0) { return -1; }\
if (buf != NULL) { buf += written; }\
written_total += written; } while(0)
static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf)
{
const char *key = NULL, *string = NULL;
JSON_Value *temp_value = NULL;
JSON_Array *array = NULL;
JSON_Object *object = NULL;
size_t i = 0, count = 0;
double num = 0.0;
int written = -1, written_total = 0;
size_t len = 0;
switch (json_value_get_type(value)) {
case JSONArray:
array = json_value_get_array(value);
count = json_array_get_count(array);
APPEND_STRING("[");
if (count > 0 && is_pretty) {
APPEND_STRING("\n");
}
for (i = 0; i < count; i++) {
if (is_pretty) {
APPEND_INDENT(level+1);
}
temp_value = json_array_get_value(array, i);
written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
if (written < 0) {
return -1;
}
if (buf != NULL) {
buf += written;
}
written_total += written;
if (i < (count - 1)) {
APPEND_STRING(",");
}
if (is_pretty) {
APPEND_STRING("\n");
}
}
if (count > 0 && is_pretty) {
APPEND_INDENT(level);
}
APPEND_STRING("]");
return written_total;
case JSONObject:
object = json_value_get_object(value);
count = json_object_get_count(object);
APPEND_STRING("{");
if (count > 0 && is_pretty) {
APPEND_STRING("\n");
}
for (i = 0; i < count; i++) {
key = json_object_get_name(object, i);
if (key == NULL) {
return -1;
}
if (is_pretty) {
APPEND_INDENT(level+1);
}
/* We do not support key names with embedded \0 chars */
written = json_serialize_string(key, strlen(key), buf);
if (written < 0) {
return -1;
}
if (buf != NULL) {
buf += written;
}
written_total += written;
APPEND_STRING(":");
if (is_pretty) {
APPEND_STRING(" ");
}
temp_value = json_object_get_value_at(object, i);
written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
if (written < 0) {
return -1;
}
if (buf != NULL) {
buf += written;
}
written_total += written;
if (i < (count - 1)) {
APPEND_STRING(",");
}
if (is_pretty) {
APPEND_STRING("\n");
}
}
if (count > 0 && is_pretty) {
APPEND_INDENT(level);
}
APPEND_STRING("}");
return written_total;
case JSONString:
string = json_value_get_string(value);
if (string == NULL) {
return -1;
}
len = json_value_get_string_len(value);
written = json_serialize_string(string, len, buf);
if (written < 0) {
return -1;
}
if (buf != NULL) {
buf += written;
}
written_total += written;
return written_total;
case JSONBoolean:
if (json_value_get_boolean(value)) {
APPEND_STRING("true");
} else {
APPEND_STRING("false");
}
return written_total;
case JSONNumber:
num = json_value_get_number(value);
if (buf != NULL) {
num_buf = buf;
}
if (parson_float_format) {
written = sprintf(num_buf, parson_float_format, num);
} else {
written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num);
}
if (written < 0) {
return -1;
}
if (buf != NULL) {
buf += written;
}
written_total += written;
return written_total;
case JSONNull:
APPEND_STRING("null");
return written_total;
case JSONError:
return -1;
default:
return -1;
}
}
static int json_serialize_string(const char *string, size_t len, char *buf) {
size_t i = 0;
char c = '\0';
int written = -1, written_total = 0;
APPEND_STRING("\"");
for (i = 0; i < len; i++) {
c = string[i];
switch (c) {
case '\"': APPEND_STRING("\\\""); break;
case '\\': APPEND_STRING("\\\\"); break;
case '\b': APPEND_STRING("\\b"); break;
case '\f': APPEND_STRING("\\f"); break;
case '\n': APPEND_STRING("\\n"); break;
case '\r': APPEND_STRING("\\r"); break;
case '\t': APPEND_STRING("\\t"); break;
case '\x00': APPEND_STRING("\\u0000"); break;
case '\x01': APPEND_STRING("\\u0001"); break;
case '\x02': APPEND_STRING("\\u0002"); break;
case '\x03': APPEND_STRING("\\u0003"); break;
case '\x04': APPEND_STRING("\\u0004"); break;
case '\x05': APPEND_STRING("\\u0005"); break;
case '\x06': APPEND_STRING("\\u0006"); break;
case '\x07': APPEND_STRING("\\u0007"); break;
/* '\x08' duplicate: '\b' */
/* '\x09' duplicate: '\t' */
/* '\x0a' duplicate: '\n' */
case '\x0b': APPEND_STRING("\\u000b"); break;
/* '\x0c' duplicate: '\f' */
/* '\x0d' duplicate: '\r' */
case '\x0e': APPEND_STRING("\\u000e"); break;
case '\x0f': APPEND_STRING("\\u000f"); break;
case '\x10': APPEND_STRING("\\u0010"); break;
case '\x11': APPEND_STRING("\\u0011"); break;
case '\x12': APPEND_STRING("\\u0012"); break;
case '\x13': APPEND_STRING("\\u0013"); break;
case '\x14': APPEND_STRING("\\u0014"); break;
case '\x15': APPEND_STRING("\\u0015"); break;
case '\x16': APPEND_STRING("\\u0016"); break;
case '\x17': APPEND_STRING("\\u0017"); break;
case '\x18': APPEND_STRING("\\u0018"); break;
case '\x19': APPEND_STRING("\\u0019"); break;
case '\x1a': APPEND_STRING("\\u001a"); break;
case '\x1b': APPEND_STRING("\\u001b"); break;
case '\x1c': APPEND_STRING("\\u001c"); break;
case '\x1d': APPEND_STRING("\\u001d"); break;
case '\x1e': APPEND_STRING("\\u001e"); break;
case '\x1f': APPEND_STRING("\\u001f"); break;
case '/':
if (parson_escape_slashes) {
APPEND_STRING("\\/"); /* to make json embeddable in xml\/html */
} else {
APPEND_STRING("/");
}
break;
default:
if (buf != NULL) {
buf[0] = c;
buf += 1;
}
written_total += 1;
break;
}
}
APPEND_STRING("\"");
return written_total;
}
static int append_indent(char *buf, int level) {
int i;
int written = -1, written_total = 0;
for (i = 0; i < level; i++) {
APPEND_STRING(" ");
}
return written_total;
}
static int append_string(char *buf, const char *string) {
if (buf == NULL) {
return (int)strlen(string);
}
return sprintf(buf, "%s", string);
}
#undef APPEND_STRING
#undef APPEND_INDENT
/* Parser API */
JSON_Value * json_parse_file(const char *filename) {
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (file_contents == NULL) {
return NULL;
}
output_value = json_parse_string(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_file_with_comments(const char *filename) {
char *file_contents = read_file(filename);
JSON_Value *output_value = NULL;
if (file_contents == NULL) {
return NULL;
}
output_value = json_parse_string_with_comments(file_contents);
parson_free(file_contents);
return output_value;
}
JSON_Value * json_parse_string(const char *string) {
if (string == NULL) {
return NULL;
}
if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') {
string = string + 3; /* Support for UTF-8 BOM */
}
return parse_value((const char**)&string, 0);
}
JSON_Value * json_parse_string_with_comments(const char *string) {
JSON_Value *result = NULL;
char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
string_mutable_copy = parson_strdup(string);
if (string_mutable_copy == NULL) {
return NULL;
}
remove_comments(string_mutable_copy, "/*", "*/");
remove_comments(string_mutable_copy, "//", "\n");
string_mutable_copy_ptr = string_mutable_copy;
result = parse_value((const char**)&string_mutable_copy_ptr, 0);
parson_free(string_mutable_copy);
return result;
}
/* JSON Object API */
JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
if (object == NULL || name == NULL) {
return NULL;
}
return json_object_getn_value(object, name, strlen(name));
}
const char * json_object_get_string(const JSON_Object *object, const char *name) {
return json_value_get_string(json_object_get_value(object, name));
}
size_t json_object_get_string_len(const JSON_Object *object, const char *name) {
return json_value_get_string_len(json_object_get_value(object, name));
}
double json_object_get_number(const JSON_Object *object, const char *name) {
return json_value_get_number(json_object_get_value(object, name));
}
JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) {
return json_value_get_object(json_object_get_value(object, name));
}
JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) {
return json_value_get_array(json_object_get_value(object, name));
}
int json_object_get_boolean(const JSON_Object *object, const char *name) {
return json_value_get_boolean(json_object_get_value(object, name));
}
JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) {
const char *dot_position = strchr(name, '.');
if (!dot_position) {
return json_object_get_value(object, name);
}
object = json_value_get_object(json_object_getn_value(object, name, dot_position - name));
return json_object_dotget_value(object, dot_position + 1);
}
const char * json_object_dotget_string(const JSON_Object *object, const char *name) {
return json_value_get_string(json_object_dotget_value(object, name));
}
size_t json_object_dotget_string_len(const JSON_Object *object, const char *name) {
return json_value_get_string_len(json_object_dotget_value(object, name));
}
double json_object_dotget_number(const JSON_Object *object, const char *name) {
return json_value_get_number(json_object_dotget_value(object, name));
}
JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) {
return json_value_get_object(json_object_dotget_value(object, name));
}
JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) {
return json_value_get_array(json_object_dotget_value(object, name));
}
int json_object_dotget_boolean(const JSON_Object *object, const char *name) {
return json_value_get_boolean(json_object_dotget_value(object, name));
}
size_t json_object_get_count(const JSON_Object *object) {
return object ? object->count : 0;
}
const char * json_object_get_name(const JSON_Object *object, size_t index) {
if (object == NULL || index >= json_object_get_count(object)) {
return NULL;
}
return object->names[index];
}
JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) {
if (object == NULL || index >= json_object_get_count(object)) {
return NULL;
}
return object->values[index];
}
JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) {
if (!object) {
return NULL;
}
return object->wrapping_value;
}
int json_object_has_value (const JSON_Object *object, const char *name) {
return json_object_get_value(object, name) != NULL;
}
int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {
JSON_Value *val = json_object_get_value(object, name);
return val != NULL && json_value_get_type(val) == type;
}
int json_object_dothas_value (const JSON_Object *object, const char *name) {
return json_object_dotget_value(object, name) != NULL;
}
int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {
JSON_Value *val = json_object_dotget_value(object, name);
return val != NULL && json_value_get_type(val) == type;
}
/* JSON Array API */
JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {
if (array == NULL || index >= json_array_get_count(array)) {
return NULL;
}
return array->items[index];
}
const char * json_array_get_string(const JSON_Array *array, size_t index) {
return json_value_get_string(json_array_get_value(array, index));
}
size_t json_array_get_string_len(const JSON_Array *array, size_t index) {
return json_value_get_string_len(json_array_get_value(array, index));
}
double json_array_get_number(const JSON_Array *array, size_t index) {
return json_value_get_number(json_array_get_value(array, index));
}
JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) {
return json_value_get_object(json_array_get_value(array, index));
}
JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) {
return json_value_get_array(json_array_get_value(array, index));
}
int json_array_get_boolean(const JSON_Array *array, size_t index) {
return json_value_get_boolean(json_array_get_value(array, index));
}
size_t json_array_get_count(const JSON_Array *array) {
return array ? array->count : 0;
}
JSON_Value * json_array_get_wrapping_value(const JSON_Array *array) {
if (!array) {
return NULL;
}
return array->wrapping_value;
}
/* JSON Value API */
JSON_Value_Type json_value_get_type(const JSON_Value *value) {
return value ? value->type : JSONError;
}
JSON_Object * json_value_get_object(const JSON_Value *value) {
return json_value_get_type(value) == JSONObject ? value->value.object : NULL;
}
JSON_Array * json_value_get_array(const JSON_Value *value) {
return json_value_get_type(value) == JSONArray ? value->value.array : NULL;
}
static const JSON_String * json_value_get_string_desc(const JSON_Value *value) {
return json_value_get_type(value) == JSONString ? &value->value.string : NULL;
}
const char * json_value_get_string(const JSON_Value *value) {
const JSON_String *str = json_value_get_string_desc(value);
return str ? str->chars : NULL;
}
size_t json_value_get_string_len(const JSON_Value *value) {
const JSON_String *str = json_value_get_string_desc(value);
return str ? str->length : 0;
}
double json_value_get_number(const JSON_Value *value) {
return json_value_get_type(value) == JSONNumber ? value->value.number : 0;
}
int json_value_get_boolean(const JSON_Value *value) {
return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;
}
JSON_Value * json_value_get_parent (const JSON_Value *value) {
return value ? value->parent : NULL;
}
void json_value_free(JSON_Value *value) {
switch (json_value_get_type(value)) {
case JSONObject:
json_object_free(value->value.object);
break;
case JSONString:
parson_free(value->value.string.chars);
break;
case JSONArray:
json_array_free(value->value.array);
break;
default:
break;
}
parson_free(value);
}
JSON_Value * json_value_init_object(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONObject;
new_value->value.object = json_object_make(new_value);
if (!new_value->value.object) {
parson_free(new_value);
return NULL;
}
return new_value;
}
JSON_Value * json_value_init_array(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONArray;
new_value->value.array = json_array_make(new_value);
if (!new_value->value.array) {
parson_free(new_value);
return NULL;
}
return new_value;
}
JSON_Value * json_value_init_string(const char *string) {
if (string == NULL) {
return NULL;
}
return json_value_init_string_with_len(string, strlen(string));
}
JSON_Value * json_value_init_string_with_len(const char *string, size_t length) {
char *copy = NULL;
JSON_Value *value;
if (string == NULL) {
return NULL;
}
if (!is_valid_utf8(string, length)) {
return NULL;
}
copy = parson_strndup(string, length);
if (copy == NULL) {
return NULL;
}
value = json_value_init_string_no_copy(copy, length);
if (value == NULL) {
parson_free(copy);
}
return value;
}
JSON_Value * json_value_init_number(double number) {
JSON_Value *new_value = NULL;
if (IS_NUMBER_INVALID(number)) {
return NULL;
}
new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (new_value == NULL) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONNumber;
new_value->value.number = number;
return new_value;
}
JSON_Value * json_value_init_boolean(int boolean) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONBoolean;
new_value->value.boolean = boolean ? 1 : 0;
return new_value;
}
JSON_Value * json_value_init_null(void) {
JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
if (!new_value) {
return NULL;
}
new_value->parent = NULL;
new_value->type = JSONNull;
return new_value;
}
JSON_Value * json_value_deep_copy(const JSON_Value *value) {
size_t i = 0;
JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;
const JSON_String *temp_string = NULL;
const char *temp_key = NULL;
char *temp_string_copy = NULL;
JSON_Array *temp_array = NULL, *temp_array_copy = NULL;
JSON_Object *temp_object = NULL, *temp_object_copy = NULL;
JSON_Status res = JSONFailure;
char *key_copy = NULL;
switch (json_value_get_type(value)) {
case JSONArray:
temp_array = json_value_get_array(value);
return_value = json_value_init_array();
if (return_value == NULL) {
return NULL;
}
temp_array_copy = json_value_get_array(return_value);
for (i = 0; i < json_array_get_count(temp_array); i++) {
temp_value = json_array_get_value(temp_array, i);
temp_value_copy = json_value_deep_copy(temp_value);
if (temp_value_copy == NULL) {
json_value_free(return_value);
return NULL;
}
if (json_array_add(temp_array_copy, temp_value_copy) != JSONSuccess) {
json_value_free(return_value);
json_value_free(temp_value_copy);
return NULL;
}
}
return return_value;
case JSONObject:
temp_object = json_value_get_object(value);
return_value = json_value_init_object();
if (!return_value) {
return NULL;
}
temp_object_copy = json_value_get_object(return_value);
for (i = 0; i < json_object_get_count(temp_object); i++) {
temp_key = json_object_get_name(temp_object, i);
temp_value = json_object_get_value(temp_object, temp_key);
temp_value_copy = json_value_deep_copy(temp_value);
if (!temp_value_copy) {
json_value_free(return_value);
return NULL;
}
key_copy = parson_strdup(temp_key);
if (!key_copy) {
json_value_free(temp_value_copy);
json_value_free(return_value);
return NULL;
}
res = json_object_add(temp_object_copy, key_copy, temp_value_copy);
if (res != JSONSuccess) {
parson_free(key_copy);
json_value_free(temp_value_copy);
json_value_free(return_value);
return NULL;
}
}
return return_value;
case JSONBoolean:
return json_value_init_boolean(json_value_get_boolean(value));
case JSONNumber:
return json_value_init_number(json_value_get_number(value));
case JSONString:
temp_string = json_value_get_string_desc(value);
if (temp_string == NULL) {
return NULL;
}
temp_string_copy = parson_strndup(temp_string->chars, temp_string->length);
if (temp_string_copy == NULL) {
return NULL;
}
return_value = json_value_init_string_no_copy(temp_string_copy, temp_string->length);
if (return_value == NULL) {
parson_free(temp_string_copy);
}
return return_value;
case JSONNull:
return json_value_init_null();
case JSONError:
return NULL;
default:
return NULL;
}
}
size_t json_serialization_size(const JSON_Value *value) {
char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_FALSE, num_buf);
return res < 0 ? 0 : (size_t)(res) + 1;
}
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
int written = -1;
size_t needed_size_in_bytes = json_serialization_size(value);
if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
return JSONFailure;
}
written = json_serialize_to_buffer_r(value, buf, 0, PARSON_FALSE, NULL);
if (written < 0) {
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) {
JSON_Status return_code = JSONSuccess;
FILE *fp = NULL;
char *serialized_string = json_serialize_to_string(value);
if (serialized_string == NULL) {
return JSONFailure;
}
fp = fopen(filename, "w");
if (fp == NULL) {
json_free_serialized_string(serialized_string);
return JSONFailure;
}
if (fputs(serialized_string, fp) == EOF) {
return_code = JSONFailure;
}
if (fclose(fp) == EOF) {
return_code = JSONFailure;
}
json_free_serialized_string(serialized_string);
return return_code;
}
char * json_serialize_to_string(const JSON_Value *value) {
JSON_Status serialization_result = JSONFailure;
size_t buf_size_bytes = json_serialization_size(value);
char *buf = NULL;
if (buf_size_bytes == 0) {
return NULL;
}
buf = (char*)parson_malloc(buf_size_bytes);
if (buf == NULL) {
return NULL;
}
serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);
if (serialization_result != JSONSuccess) {
json_free_serialized_string(buf);
return NULL;
}
return buf;
}
size_t json_serialization_size_pretty(const JSON_Value *value) {
char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_TRUE, num_buf);
return res < 0 ? 0 : (size_t)(res) + 1;
}
JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
int written = -1;
size_t needed_size_in_bytes = json_serialization_size_pretty(value);
if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
return JSONFailure;
}
written = json_serialize_to_buffer_r(value, buf, 0, PARSON_TRUE, NULL);
if (written < 0) {
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) {
JSON_Status return_code = JSONSuccess;
FILE *fp = NULL;
char *serialized_string = json_serialize_to_string_pretty(value);
if (serialized_string == NULL) {
return JSONFailure;
}
fp = fopen(filename, "w");
if (fp == NULL) {
json_free_serialized_string(serialized_string);
return JSONFailure;
}
if (fputs(serialized_string, fp) == EOF) {
return_code = JSONFailure;
}
if (fclose(fp) == EOF) {
return_code = JSONFailure;
}
json_free_serialized_string(serialized_string);
return return_code;
}
char * json_serialize_to_string_pretty(const JSON_Value *value) {
JSON_Status serialization_result = JSONFailure;
size_t buf_size_bytes = json_serialization_size_pretty(value);
char *buf = NULL;
if (buf_size_bytes == 0) {
return NULL;
}
buf = (char*)parson_malloc(buf_size_bytes);
if (buf == NULL) {
return NULL;
}
serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);
if (serialization_result != JSONSuccess) {
json_free_serialized_string(buf);
return NULL;
}
return buf;
}
void json_free_serialized_string(char *string) {
parson_free(string);
}
JSON_Status json_array_remove(JSON_Array *array, size_t ix) {
size_t to_move_bytes = 0;
if (array == NULL || ix >= json_array_get_count(array)) {
return JSONFailure;
}
json_value_free(json_array_get_value(array, ix));
to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value*);
memmove(array->items + ix, array->items + ix + 1, to_move_bytes);
array->count -= 1;
return JSONSuccess;
}
JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) {
if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) {
return JSONFailure;
}
json_value_free(json_array_get_value(array, ix));
value->parent = json_array_get_wrapping_value(array);
array->items[ix] = value;
return JSONSuccess;
}
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) {
JSON_Value *value = json_value_init_string(string);
if (value == NULL) {
return JSONFailure;
}
if (json_array_replace_value(array, i, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len) {
JSON_Value *value = json_value_init_string_with_len(string, len);
if (value == NULL) {
return JSONFailure;
}
if (json_array_replace_value(array, i, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) {
JSON_Value *value = json_value_init_number(number);
if (value == NULL) {
return JSONFailure;
}
if (json_array_replace_value(array, i, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) {
JSON_Value *value = json_value_init_boolean(boolean);
if (value == NULL) {
return JSONFailure;
}
if (json_array_replace_value(array, i, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_replace_null(JSON_Array *array, size_t i) {
JSON_Value *value = json_value_init_null();
if (value == NULL) {
return JSONFailure;
}
if (json_array_replace_value(array, i, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_clear(JSON_Array *array) {
size_t i = 0;
if (array == NULL) {
return JSONFailure;
}
for (i = 0; i < json_array_get_count(array); i++) {
json_value_free(json_array_get_value(array, i));
}
array->count = 0;
return JSONSuccess;
}
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) {
if (array == NULL || value == NULL || value->parent != NULL) {
return JSONFailure;
}
return json_array_add(array, value);
}
JSON_Status json_array_append_string(JSON_Array *array, const char *string) {
JSON_Value *value = json_value_init_string(string);
if (value == NULL) {
return JSONFailure;
}
if (json_array_append_value(array, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len) {
JSON_Value *value = json_value_init_string_with_len(string, len);
if (value == NULL) {
return JSONFailure;
}
if (json_array_append_value(array, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_append_number(JSON_Array *array, double number) {
JSON_Value *value = json_value_init_number(number);
if (value == NULL) {
return JSONFailure;
}
if (json_array_append_value(array, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) {
JSON_Value *value = json_value_init_boolean(boolean);
if (value == NULL) {
return JSONFailure;
}
if (json_array_append_value(array, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_array_append_null(JSON_Array *array) {
JSON_Value *value = json_value_init_null();
if (value == NULL) {
return JSONFailure;
}
if (json_array_append_value(array, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) {
unsigned long hash = 0;
parson_bool_t found = PARSON_FALSE;
size_t cell_ix = 0;
size_t item_ix = 0;
JSON_Value *old_value = NULL;
char *key_copy = NULL;
if (!object || !name || !value || value->parent) {
return JSONFailure;
}
hash = hash_string(name, strlen(name));
found = PARSON_FALSE;
cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
if (found) {
item_ix = object->cells[cell_ix];
old_value = object->values[item_ix];
json_value_free(old_value);
object->values[item_ix] = value;
value->parent = json_object_get_wrapping_value(object);
return JSONSuccess;
}
if (object->count >= object->item_capacity) {
JSON_Status res = json_object_grow_and_rehash(object);
if (res != JSONSuccess) {
return JSONFailure;
}
cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
}
key_copy = parson_strdup(name);
if (!key_copy) {
return JSONFailure;
}
object->names[object->count] = key_copy;
object->cells[cell_ix] = object->count;
object->values[object->count] = value;
object->cell_ixs[object->count] = cell_ix;
object->hashes[object->count] = hash;
object->count++;
value->parent = json_object_get_wrapping_value(object);
return JSONSuccess;
}
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) {
JSON_Value *value = json_value_init_string(string);
JSON_Status status = json_object_set_value(object, name, value);
if (status != JSONSuccess) {
json_value_free(value);
}
return status;
}
JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {
JSON_Value *value = json_value_init_string_with_len(string, len);
JSON_Status status = json_object_set_value(object, name, value);
if (status != JSONSuccess) {
json_value_free(value);
}
return status;
}
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) {
JSON_Value *value = json_value_init_number(number);
JSON_Status status = json_object_set_value(object, name, value);
if (status != JSONSuccess) {
json_value_free(value);
}
return status;
}
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) {
JSON_Value *value = json_value_init_boolean(boolean);
JSON_Status status = json_object_set_value(object, name, value);
if (status != JSONSuccess) {
json_value_free(value);
}
return status;
}
JSON_Status json_object_set_null(JSON_Object *object, const char *name) {
JSON_Value *value = json_value_init_null();
JSON_Status status = json_object_set_value(object, name, value);
if (status != JSONSuccess) {
json_value_free(value);
}
return status;
}
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) {
const char *dot_pos = NULL;
JSON_Value *temp_value = NULL, *new_value = NULL;
JSON_Object *temp_object = NULL, *new_object = NULL;
JSON_Status status = JSONFailure;
size_t name_len = 0;
char *name_copy = NULL;
if (object == NULL || name == NULL || value == NULL) {
return JSONFailure;
}
dot_pos = strchr(name, '.');
if (dot_pos == NULL) {
return json_object_set_value(object, name, value);
}
name_len = dot_pos - name;
temp_value = json_object_getn_value(object, name, name_len);
if (temp_value) {
/* Don't overwrite existing non-object (unlike json_object_set_value, but it shouldn't be changed at this point) */
if (json_value_get_type(temp_value) != JSONObject) {
return JSONFailure;
}
temp_object = json_value_get_object(temp_value);
return json_object_dotset_value(temp_object, dot_pos + 1, value);
}
new_value = json_value_init_object();
if (new_value == NULL) {
return JSONFailure;
}
new_object = json_value_get_object(new_value);
status = json_object_dotset_value(new_object, dot_pos + 1, value);
if (status != JSONSuccess) {
json_value_free(new_value);
return JSONFailure;
}
name_copy = parson_strndup(name, name_len);
if (!name_copy) {
json_object_dotremove_internal(new_object, dot_pos + 1, 0);
json_value_free(new_value);
return JSONFailure;
}
status = json_object_add(object, name_copy, new_value);
if (status != JSONSuccess) {
parson_free(name_copy);
json_object_dotremove_internal(new_object, dot_pos + 1, 0);
json_value_free(new_value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) {
JSON_Value *value = json_value_init_string(string);
if (value == NULL) {
return JSONFailure;
}
if (json_object_dotset_value(object, name, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {
JSON_Value *value = json_value_init_string_with_len(string, len);
if (value == NULL) {
return JSONFailure;
}
if (json_object_dotset_value(object, name, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) {
JSON_Value *value = json_value_init_number(number);
if (value == NULL) {
return JSONFailure;
}
if (json_object_dotset_value(object, name, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) {
JSON_Value *value = json_value_init_boolean(boolean);
if (value == NULL) {
return JSONFailure;
}
if (json_object_dotset_value(object, name, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) {
JSON_Value *value = json_value_init_null();
if (value == NULL) {
return JSONFailure;
}
if (json_object_dotset_value(object, name, value) != JSONSuccess) {
json_value_free(value);
return JSONFailure;
}
return JSONSuccess;
}
JSON_Status json_object_remove(JSON_Object *object, const char *name) {
return json_object_remove_internal(object, name, PARSON_TRUE);
}
JSON_Status json_object_dotremove(JSON_Object *object, const char *name) {
return json_object_dotremove_internal(object, name, PARSON_TRUE);
}
JSON_Status json_object_clear(JSON_Object *object) {
size_t i = 0;
if (object == NULL) {
return JSONFailure;
}
for (i = 0; i < json_object_get_count(object); i++) {
parson_free(object->names[i]);
json_value_free(object->values[i]);
}
object->count = 0;
return JSONSuccess;
}
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) {
JSON_Value *temp_schema_value = NULL, *temp_value = NULL;
JSON_Array *schema_array = NULL, *value_array = NULL;
JSON_Object *schema_object = NULL, *value_object = NULL;
JSON_Value_Type schema_type = JSONError, value_type = JSONError;
const char *key = NULL;
size_t i = 0, count = 0;
if (schema == NULL || value == NULL) {
return JSONFailure;
}
schema_type = json_value_get_type(schema);
value_type = json_value_get_type(value);
if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */
return JSONFailure;
}
switch (schema_type) {
case JSONArray:
schema_array = json_value_get_array(schema);
value_array = json_value_get_array(value);
count = json_array_get_count(schema_array);
if (count == 0) {
return JSONSuccess; /* Empty array allows all types */
}
/* Get first value from array, rest is ignored */
temp_schema_value = json_array_get_value(schema_array, 0);
for (i = 0; i < json_array_get_count(value_array); i++) {
temp_value = json_array_get_value(value_array, i);
if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
return JSONFailure;
}
}
return JSONSuccess;
case JSONObject:
schema_object = json_value_get_object(schema);
value_object = json_value_get_object(value);
count = json_object_get_count(schema_object);
if (count == 0) {
return JSONSuccess; /* Empty object allows all objects */
} else if (json_object_get_count(value_object) < count) {
return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */
}
for (i = 0; i < count; i++) {
key = json_object_get_name(schema_object, i);
temp_schema_value = json_object_get_value(schema_object, key);
temp_value = json_object_get_value(value_object, key);
if (temp_value == NULL) {
return JSONFailure;
}
if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
return JSONFailure;
}
}
return JSONSuccess;
case JSONString: case JSONNumber: case JSONBoolean: case JSONNull:
return JSONSuccess; /* equality already tested before switch */
case JSONError: default:
return JSONFailure;
}
}
int json_value_equals(const JSON_Value *a, const JSON_Value *b) {
JSON_Object *a_object = NULL, *b_object = NULL;
JSON_Array *a_array = NULL, *b_array = NULL;
const JSON_String *a_string = NULL, *b_string = NULL;
const char *key = NULL;
size_t a_count = 0, b_count = 0, i = 0;
JSON_Value_Type a_type, b_type;
a_type = json_value_get_type(a);
b_type = json_value_get_type(b);
if (a_type != b_type) {
return PARSON_FALSE;
}
switch (a_type) {
case JSONArray:
a_array = json_value_get_array(a);
b_array = json_value_get_array(b);
a_count = json_array_get_count(a_array);
b_count = json_array_get_count(b_array);
if (a_count != b_count) {
return PARSON_FALSE;
}
for (i = 0; i < a_count; i++) {
if (!json_value_equals(json_array_get_value(a_array, i),
json_array_get_value(b_array, i))) {
return PARSON_FALSE;
}
}
return PARSON_TRUE;
case JSONObject:
a_object = json_value_get_object(a);
b_object = json_value_get_object(b);
a_count = json_object_get_count(a_object);
b_count = json_object_get_count(b_object);
if (a_count != b_count) {
return PARSON_FALSE;
}
for (i = 0; i < a_count; i++) {
key = json_object_get_name(a_object, i);
if (!json_value_equals(json_object_get_value(a_object, key),
json_object_get_value(b_object, key))) {
return PARSON_FALSE;
}
}
return PARSON_TRUE;
case JSONString:
a_string = json_value_get_string_desc(a);
b_string = json_value_get_string_desc(b);
if (a_string == NULL || b_string == NULL) {
return PARSON_FALSE; /* shouldn't happen */
}
return a_string->length == b_string->length &&
memcmp(a_string->chars, b_string->chars, a_string->length) == 0;
case JSONBoolean:
return json_value_get_boolean(a) == json_value_get_boolean(b);
case JSONNumber:
return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */
case JSONError:
return PARSON_TRUE;
case JSONNull:
return PARSON_TRUE;
default:
return PARSON_TRUE;
}
}
JSON_Value_Type json_type(const JSON_Value *value) {
return json_value_get_type(value);
}
JSON_Object * json_object (const JSON_Value *value) {
return json_value_get_object(value);
}
JSON_Array * json_array(const JSON_Value *value) {
return json_value_get_array(value);
}
const char * json_string(const JSON_Value *value) {
return json_value_get_string(value);
}
size_t json_string_len(const JSON_Value *value) {
return json_value_get_string_len(value);
}
double json_number(const JSON_Value *value) {
return json_value_get_number(value);
}
int json_boolean(const JSON_Value *value) {
return json_value_get_boolean(value);
}
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {
parson_malloc = malloc_fun;
parson_free = free_fun;
}
void json_set_escape_slashes(int escape_slashes) {
parson_escape_slashes = escape_slashes;
}
void json_set_float_serialization_format(const char *format) {
if (parson_float_format) {
parson_free(parson_float_format);
}
if (!format) {
parson_float_format = NULL;
return;
}
parson_float_format = parson_strdup(format);
}
================================================
FILE: jni/json/parson.h
================================================
/*
SPDX-License-Identifier: MIT
Parson 1.4.0 (https://github.com/kgabis/parson)
Copyright (c) 2012 - 2022 Krzysztof Gabis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef parson_parson_h
#define parson_parson_h
#ifdef __cplusplus
extern "C"
{
#endif
#if 0
} /* unconfuse xcode */
#endif
#define PARSON_VERSION_MAJOR 1
#define PARSON_VERSION_MINOR 4
#define PARSON_VERSION_PATCH 0
#define PARSON_VERSION_STRING "1.4.0"
#include /* size_t */
/* Types and enums */
typedef struct json_object_t JSON_Object;
typedef struct json_array_t JSON_Array;
typedef struct json_value_t JSON_Value;
enum json_value_type {
JSONError = -1,
JSONNull = 1,
JSONString = 2,
JSONNumber = 3,
JSONObject = 4,
JSONArray = 5,
JSONBoolean = 6
};
typedef int JSON_Value_Type;
enum json_result_t {
JSONSuccess = 0,
JSONFailure = -1
};
typedef int JSON_Status;
typedef void * (*JSON_Malloc_Function)(size_t);
typedef void (*JSON_Free_Function)(void *);
/* Call only once, before calling any other function from parson API. If not called, malloc and free
from stdlib will be used for all allocations */
void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
/* Sets if slashes should be escaped or not when serializing JSON. By default slashes are escaped.
This function sets a global setting and is not thread safe. */
void json_set_escape_slashes(int escape_slashes);
/* Sets float format used for serialization of numbers.
Make sure it can't serialize to a string longer than PARSON_NUM_BUF_SIZE.
If format is null then the default format is used. */
void json_set_float_serialization_format(const char *format);
/* Parses first JSON value in a file, returns NULL in case of error */
JSON_Value * json_parse_file(const char *filename);
/* Parses first JSON value in a file and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_file_with_comments(const char *filename);
/* Parses first JSON value in a string, returns NULL in case of error */
JSON_Value * json_parse_string(const char *string);
/* Parses first JSON value in a string and ignores comments (/ * * / and //),
returns NULL in case of error */
JSON_Value * json_parse_string_with_comments(const char *string);
/* Serialization */
size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
char * json_serialize_to_string(const JSON_Value *value);
/* Pretty serialization */
size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
char * json_serialize_to_string_pretty(const JSON_Value *value);
void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
/* Comparing */
int json_value_equals(const JSON_Value *a, const JSON_Value *b);
/* Validation
This is *NOT* JSON Schema. It validates json by checking if object have identically
named fields with matching types.
For example schema {"name":"", "age":0} will validate
{"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
In case of arrays, only first value in schema is checked against all values in tested array.
Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
null validates values of every type.
*/
JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
/*
* JSON Object
*/
JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
const char * json_object_get_string (const JSON_Object *object, const char *name);
size_t json_object_get_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */
JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* dotget functions enable addressing values with dot notation in nested objects,
just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
Because valid names in JSON can contain dots, some values may be inaccessible
this way. */
JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
const char * json_object_dotget_string (const JSON_Object *object, const char *name);
size_t json_object_dotget_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */
JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
/* Functions to get available names */
size_t json_object_get_count (const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index);
JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index);
JSON_Value * json_object_get_wrapping_value(const JSON_Object *object);
/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
* a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
int json_object_has_value (const JSON_Object *object, const char *name);
int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
int json_object_dothas_value (const JSON_Object *object, const char *name);
int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
/* Creates new name-value pair or frees and replaces old value with a new one.
* json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */
JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_set_null(JSON_Object *object, const char *name);
/* Works like dotget functions, but creates whole hierarchy if necessary.
* json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */
JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
/* Frees and removes name-value pair */
JSON_Status json_object_remove(JSON_Object *object, const char *name);
/* Works like dotget function, but removes name-value pair only on exact match. */
JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
/* Removes all name-value pairs in object */
JSON_Status json_object_clear(JSON_Object *object);
/*
*JSON Array
*/
JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
const char * json_array_get_string (const JSON_Array *array, size_t index);
size_t json_array_get_string_len(const JSON_Array *array, size_t index); /* doesn't account for last null character */
JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
size_t json_array_get_count (const JSON_Array *array);
JSON_Value * json_array_get_wrapping_value(const JSON_Array *array);
/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
* Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i);
/* Frees and removes from array value at given index and replaces it with given one.
* Does nothing and returns JSONFailure if index doesn't exist.
* json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len); /* length shouldn't include last null character */
JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
/* Frees and removes all values from array */
JSON_Status json_array_clear(JSON_Array *array);
/* Appends new value at the end of array.
* json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
JSON_Status json_array_append_string(JSON_Array *array, const char *string);
JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len); /* length shouldn't include last null character */
JSON_Status json_array_append_number(JSON_Array *array, double number);
JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
JSON_Status json_array_append_null(JSON_Array *array);
/*
*JSON Value
*/
JSON_Value * json_value_init_object (void);
JSON_Value * json_value_init_array (void);
JSON_Value * json_value_init_string (const char *string); /* copies passed string */
JSON_Value * json_value_init_string_with_len(const char *string, size_t length); /* copies passed string, length shouldn't include last null character */
JSON_Value * json_value_init_number (double number);
JSON_Value * json_value_init_boolean(int boolean);
JSON_Value * json_value_init_null (void);
JSON_Value * json_value_deep_copy (const JSON_Value *value);
void json_value_free (JSON_Value *value);
JSON_Value_Type json_value_get_type (const JSON_Value *value);
JSON_Object * json_value_get_object (const JSON_Value *value);
JSON_Array * json_value_get_array (const JSON_Value *value);
const char * json_value_get_string (const JSON_Value *value);
size_t json_value_get_string_len(const JSON_Value *value); /* doesn't account for last null character */
double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(const JSON_Value *value);
JSON_Value * json_value_get_parent (const JSON_Value *value);
/* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value);
JSON_Object * json_object (const JSON_Value *value);
JSON_Array * json_array (const JSON_Value *value);
const char * json_string (const JSON_Value *value);
size_t json_string_len(const JSON_Value *value); /* doesn't account for last null character */
double json_number (const JSON_Value *value);
int json_boolean(const JSON_Value *value);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: jni/libspng/spng.c
================================================
/* SPDX-License-Identifier: (BSD-2-Clause AND libpng-2.0) */
#define SPNG__BUILD
#include "spng.h"
#include
#include
#include
#include
#define ZLIB_CONST
#ifdef __FRAMAC__
#define SPNG_DISABLE_OPT
#include "tests/framac_stubs.h"
#else
#ifdef SPNG_USE_MINIZ
#include
#else
#include
#endif
#endif
#ifdef SPNG_MULTITHREADING
#include
#endif
/* Not build options, edit at your own risk! */
#define SPNG_READ_SIZE (8192)
#define SPNG_WRITE_SIZE SPNG_READ_SIZE
#define SPNG_MAX_CHUNK_COUNT (1000)
#define SPNG_TARGET_CLONES(x)
#ifndef SPNG_DISABLE_OPT
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
#define SPNG_X86
#if defined(__x86_64__) || defined(_M_X64)
#define SPNG_X86_64
#endif
#elif defined(__aarch64__) || defined(_M_ARM64) /* || defined(__ARM_NEON) */
#define SPNG_ARM /* NOTE: only arm64 builds are tested! */
#else
#pragma message "disabling SIMD optimizations for unknown target"
#define SPNG_DISABLE_OPT
#endif
#if defined(SPNG_X86_64) && defined(SPNG_ENABLE_TARGET_CLONES)
#undef SPNG_TARGET_CLONES
#define SPNG_TARGET_CLONES(x) __attribute__((target_clones(x)))
#else
#define SPNG_TARGET_CLONES(x)
#endif
#ifndef SPNG_DISABLE_OPT
static void defilter_sub3(size_t rowbytes, unsigned char *row);
static void defilter_sub4(size_t rowbytes, unsigned char *row);
static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev);
static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev);
static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev);
static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev);
#if defined(SPNG_ARM)
static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width);
static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width);
#endif
#endif
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4244)
#endif
#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(__BIG_ENDIAN__)
#define SPNG_BIG_ENDIAN
#else
#define SPNG_LITTLE_ENDIAN
#endif
enum spng_state
{
SPNG_STATE_INVALID = 0,
SPNG_STATE_INIT = 1, /* No PNG buffer/stream is set */
SPNG_STATE_INPUT, /* Decoder input PNG was set */
SPNG_STATE_OUTPUT = SPNG_STATE_INPUT, /* Encoder output was set */
SPNG_STATE_IHDR, /* IHDR was read/written */
SPNG_STATE_FIRST_IDAT, /* Encoded up to / reached first IDAT */
SPNG_STATE_DECODE_INIT, /* Decoder is ready for progressive reads */
SPNG_STATE_ENCODE_INIT = SPNG_STATE_DECODE_INIT,
SPNG_STATE_EOI, /* Reached the last scanline/row */
SPNG_STATE_LAST_IDAT, /* Reached last IDAT, set at end of decode_image() */
SPNG_STATE_AFTER_IDAT, /* */
SPNG_STATE_IEND, /* Reached IEND */
};
enum spng__internal
{
SPNG__IO_SIGNAL = 1 << 9,
SPNG__CTX_FLAGS_ALL = (SPNG_CTX_IGNORE_ADLER32 | SPNG_CTX_ENCODER)
};
#define SPNG_STR(x) _SPNG_STR(x)
#define _SPNG_STR(x) #x
#define SPNG_VERSION_STRING SPNG_STR(SPNG_VERSION_MAJOR) "." \
SPNG_STR(SPNG_VERSION_MINOR) "." \
SPNG_STR(SPNG_VERSION_PATCH)
#define SPNG_GET_CHUNK_BOILERPLATE(chunk) \
if(ctx == NULL) return 1; \
int ret = read_chunks(ctx, 0); \
if(ret) return ret; \
if(!ctx->stored.chunk) return SPNG_ECHUNKAVAIL; \
if(chunk == NULL) return 1
#define SPNG_SET_CHUNK_BOILERPLATE(chunk) \
if(ctx == NULL || chunk == NULL) return 1; \
if(ctx->data == NULL && !ctx->encode_only) return SPNG_ENOSRC; \
int ret = read_chunks(ctx, 0); \
if(ret) return ret
/* Determine if the spng_option can be overriden/optimized */
#define spng__optimize(option) (ctx->optimize_option & (1 << option))
struct spng_subimage
{
uint32_t width;
uint32_t height;
size_t out_width; /* byte width based on output format */
size_t scanline_width;
};
struct spng_text2
{
int type;
char *keyword;
char *text;
size_t text_length;
uint8_t compression_flag; /* iTXt only */
char *language_tag; /* iTXt only */
char *translated_keyword; /* iTXt only */
size_t cache_usage;
char user_keyword_storage[80];
};
struct decode_flags
{
unsigned apply_trns: 1;
unsigned apply_gamma: 1;
unsigned use_sbit: 1;
unsigned indexed: 1;
unsigned do_scaling: 1;
unsigned interlaced: 1;
unsigned same_layout: 1;
unsigned zerocopy: 1;
unsigned unpack: 1;
};
struct encode_flags
{
unsigned interlace: 1;
unsigned same_layout: 1;
unsigned to_bigendian: 1;
unsigned progressive: 1;
unsigned finalize: 1;
enum spng_filter_choice filter_choice;
};
struct spng_chunk_bitfield
{
unsigned ihdr: 1;
unsigned plte: 1;
unsigned chrm: 1;
unsigned iccp: 1;
unsigned gama: 1;
unsigned sbit: 1;
unsigned srgb: 1;
unsigned text: 1;
unsigned bkgd: 1;
unsigned hist: 1;
unsigned trns: 1;
unsigned phys: 1;
unsigned splt: 1;
unsigned time: 1;
unsigned offs: 1;
unsigned exif: 1;
unsigned unknown: 1;
};
/* Packed sample iterator */
struct spng__iter
{
const uint8_t mask;
unsigned shift_amount;
const unsigned initial_shift, bit_depth;
const unsigned char *samples;
};
union spng__decode_plte
{
struct spng_plte_entry rgba[256];
unsigned char rgb[256 * 3];
unsigned char raw[256 * 4];
uint32_t align_this;
};
struct spng__zlib_options
{
int compression_level;
int window_bits;
int mem_level;
int strategy;
int data_type;
};
typedef void spng__undo(spng_ctx *ctx);
struct spng_ctx
{
size_t data_size;
size_t bytes_read;
size_t stream_buf_size;
unsigned char *stream_buf;
const unsigned char *data;
/* User-defined pointers for streaming */
spng_read_fn *read_fn;
spng_write_fn *write_fn;
void *stream_user_ptr;
/* Used for buffer reads */
const unsigned char *png_base;
size_t bytes_left;
size_t last_read_size;
/* Used for encoding */
int user_owns_out_png;
unsigned char *out_png;
unsigned char *write_ptr;
size_t out_png_size;
size_t bytes_encoded;
/* These are updated by read/write_header()/read_chunk_bytes() */
struct spng_chunk current_chunk;
uint32_t cur_chunk_bytes_left;
uint32_t cur_actual_crc;
struct spng_alloc alloc;
enum spng_ctx_flags flags;
enum spng_format fmt;
enum spng_state state;
unsigned streaming: 1;
unsigned internal_buffer: 1; /* encoding to internal buffer */
unsigned inflate: 1;
unsigned deflate: 1;
unsigned encode_only: 1;
unsigned strict: 1;
unsigned discard: 1;
unsigned skip_crc: 1;
unsigned keep_unknown: 1;
unsigned prev_was_idat: 1;
struct spng__zlib_options image_options;
struct spng__zlib_options text_options;
spng__undo *undo;
/* input file contains this chunk */
struct spng_chunk_bitfield file;
/* chunk was stored with spng_set_*() */
struct spng_chunk_bitfield user;
/* chunk was stored by reading or with spng_set_*() */
struct spng_chunk_bitfield stored;
/* used to reset the above in case of an error */
struct spng_chunk_bitfield prev_stored;
struct spng_chunk first_idat, last_idat;
uint32_t max_width, max_height;
size_t max_chunk_size;
size_t chunk_cache_limit;
size_t chunk_cache_usage;
uint32_t chunk_count_limit;
uint32_t chunk_count_total;
int crc_action_critical;
int crc_action_ancillary;
uint32_t optimize_option;
struct spng_ihdr ihdr;
struct spng_plte plte;
struct spng_chrm_int chrm_int;
struct spng_iccp iccp;
uint32_t gama;
struct spng_sbit sbit;
uint8_t srgb_rendering_intent;
uint32_t n_text;
struct spng_text2 *text_list;
struct spng_bkgd bkgd;
struct spng_hist hist;
struct spng_trns trns;
struct spng_phys phys;
uint32_t n_splt;
struct spng_splt *splt_list;
struct spng_time time;
struct spng_offs offs;
struct spng_exif exif;
uint32_t n_chunks;
struct spng_unknown_chunk *chunk_list;
struct spng_subimage subimage[7];
z_stream zstream;
unsigned char *scanline_buf, *prev_scanline_buf, *row_buf, *filtered_scanline_buf;
unsigned char *scanline, *prev_scanline, *row, *filtered_scanline;
/* based on fmt */
size_t image_size; /* may be zero */
size_t image_width;
unsigned bytes_per_pixel; /* derived from ihdr */
unsigned pixel_size; /* derived from spng_format+ihdr */
int widest_pass;
int last_pass; /* last non-empty pass */
uint16_t *gamma_lut; /* points to either _lut8 or _lut16 */
uint16_t *gamma_lut16;
uint16_t gamma_lut8[256];
unsigned char trns_px[8];
union spng__decode_plte decode_plte;
struct spng_sbit decode_sb;
struct decode_flags decode_flags;
struct spng_row_info row_info;
struct encode_flags encode_flags;
};
static const uint32_t spng_u32max = INT32_MAX;
static const uint32_t adam7_x_start[7] = { 0, 4, 0, 2, 0, 1, 0 };
static const uint32_t adam7_y_start[7] = { 0, 0, 4, 0, 2, 0, 1 };
static const uint32_t adam7_x_delta[7] = { 8, 8, 4, 4, 2, 2, 1 };
static const uint32_t adam7_y_delta[7] = { 8, 8, 8, 4, 4, 2, 2 };
static const uint8_t spng_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
static const uint8_t type_ihdr[4] = { 73, 72, 68, 82 };
static const uint8_t type_plte[4] = { 80, 76, 84, 69 };
static const uint8_t type_idat[4] = { 73, 68, 65, 84 };
static const uint8_t type_iend[4] = { 73, 69, 78, 68 };
static const uint8_t type_trns[4] = { 116, 82, 78, 83 };
static const uint8_t type_chrm[4] = { 99, 72, 82, 77 };
static const uint8_t type_gama[4] = { 103, 65, 77, 65 };
static const uint8_t type_iccp[4] = { 105, 67, 67, 80 };
static const uint8_t type_sbit[4] = { 115, 66, 73, 84 };
static const uint8_t type_srgb[4] = { 115, 82, 71, 66 };
static const uint8_t type_text[4] = { 116, 69, 88, 116 };
static const uint8_t type_ztxt[4] = { 122, 84, 88, 116 };
static const uint8_t type_itxt[4] = { 105, 84, 88, 116 };
static const uint8_t type_bkgd[4] = { 98, 75, 71, 68 };
static const uint8_t type_hist[4] = { 104, 73, 83, 84 };
static const uint8_t type_phys[4] = { 112, 72, 89, 115 };
static const uint8_t type_splt[4] = { 115, 80, 76, 84 };
static const uint8_t type_time[4] = { 116, 73, 77, 69 };
static const uint8_t type_offs[4] = { 111, 70, 70, 115 };
static const uint8_t type_exif[4] = { 101, 88, 73, 102 };
static inline void *spng__malloc(spng_ctx *ctx, size_t size)
{
return ctx->alloc.malloc_fn(size);
}
static inline void *spng__calloc(spng_ctx *ctx, size_t nmemb, size_t size)
{
return ctx->alloc.calloc_fn(nmemb, size);
}
static inline void *spng__realloc(spng_ctx *ctx, void *ptr, size_t size)
{
return ctx->alloc.realloc_fn(ptr, size);
}
static inline void spng__free(spng_ctx *ctx, void *ptr)
{
ctx->alloc.free_fn(ptr);
}
#if defined(SPNG_USE_MINIZ)
static void *spng__zalloc(void *opaque, size_t items, size_t size)
#else
static void *spng__zalloc(void *opaque, uInt items, uInt size)
#endif
{
spng_ctx *ctx = opaque;
if(size > SIZE_MAX / items) return NULL;
size_t len = (size_t)items * size;
return spng__malloc(ctx, len);
}
static void spng__zfree(void *opqaue, void *ptr)
{
spng_ctx *ctx = opqaue;
spng__free(ctx, ptr);
}
static inline uint16_t read_u16(const void *src)
{
const unsigned char *data = src;
return (data[0] & 0xFFU) << 8 | (data[1] & 0xFFU);
}
static inline uint32_t read_u32(const void *src)
{
const unsigned char *data = src;
return (data[0] & 0xFFUL) << 24 | (data[1] & 0xFFUL) << 16 |
(data[2] & 0xFFUL) << 8 | (data[3] & 0xFFUL);
}
static inline int32_t read_s32(const void *src)
{
int32_t ret = (int32_t)read_u32(src);
return ret;
}
static inline void write_u16(void *dest, uint16_t x)
{
unsigned char *data = dest;
data[0] = x >> 8;
data[1] = x & 0xFF;
}
static inline void write_u32(void *dest, uint32_t x)
{
unsigned char *data = dest;
data[0] = (x >> 24);
data[1] = (x >> 16) & 0xFF;
data[2] = (x >> 8) & 0xFF;
data[3] = x & 0xFF;
}
static inline void write_s32(void *dest, int32_t x)
{
uint32_t n = x;
write_u32(dest, n);
}
/* Returns an iterator for 1,2,4,8-bit samples */
static struct spng__iter spng__iter_init(unsigned bit_depth, const unsigned char *samples)
{
struct spng__iter iter =
{
.mask = (uint32_t)(1 << bit_depth) - 1,
.shift_amount = 8 - bit_depth,
.initial_shift = 8 - bit_depth,
.bit_depth = bit_depth,
.samples = samples
};
return iter;
}
/* Returns the current sample unpacked, iterates to the next one */
static inline uint8_t get_sample(struct spng__iter *iter)
{
uint8_t x = (iter->samples[0] >> iter->shift_amount) & iter->mask;
iter->shift_amount -= iter->bit_depth;
if(iter->shift_amount > 7)
{
iter->shift_amount = iter->initial_shift;
iter->samples++;
}
return x;
}
static void u16_row_to_host(void *row, size_t size)
{
uint16_t *px = row;
size_t i, n = size / 2;
for(i=0; i < n; i++)
{
px[i] = read_u16(&px[i]);
}
}
static void u16_row_to_bigendian(void *row, size_t size)
{
uint16_t *px = (uint16_t*)row;
size_t i, n = size / 2;
for(i=0; i < n; i++)
{
write_u16(&px[i], px[i]);
}
}
static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)
{
uint32_t i;
for(i=0; i < n; i++)
{
memcpy(out + i * 4, row + i * 3, 3);
out[i*4+3] = 255;
}
}
static unsigned num_channels(const struct spng_ihdr *ihdr)
{
switch(ihdr->color_type)
{
case SPNG_COLOR_TYPE_TRUECOLOR: return 3;
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: return 2;
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: return 4;
case SPNG_COLOR_TYPE_GRAYSCALE:
case SPNG_COLOR_TYPE_INDEXED:
return 1;
default: return 0;
}
}
/* Calculate scanline width in bits, round up to the nearest byte */
static int calculate_scanline_width(const struct spng_ihdr *ihdr, uint32_t width, size_t *scanline_width)
{
if(ihdr == NULL || !width) return SPNG_EINTERNAL;
size_t res = num_channels(ihdr) * ihdr->bit_depth;
if(res > SIZE_MAX / width) return SPNG_EOVERFLOW;
res = res * width;
res += 15; /* Filter byte + 7 for rounding */
if(res < 15) return SPNG_EOVERFLOW;
res /= 8;
if(res > UINT32_MAX) return SPNG_EOVERFLOW;
*scanline_width = res;
return 0;
}
static int calculate_subimages(struct spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
struct spng_ihdr *ihdr = &ctx->ihdr;
struct spng_subimage *sub = ctx->subimage;
if(ihdr->interlace_method == 1)
{
sub[0].width = (ihdr->width + 7) >> 3;
sub[0].height = (ihdr->height + 7) >> 3;
sub[1].width = (ihdr->width + 3) >> 3;
sub[1].height = (ihdr->height + 7) >> 3;
sub[2].width = (ihdr->width + 3) >> 2;
sub[2].height = (ihdr->height + 3) >> 3;
sub[3].width = (ihdr->width + 1) >> 2;
sub[3].height = (ihdr->height + 3) >> 2;
sub[4].width = (ihdr->width + 1) >> 1;
sub[4].height = (ihdr->height + 1) >> 2;
sub[5].width = ihdr->width >> 1;
sub[5].height = (ihdr->height + 1) >> 1;
sub[6].width = ihdr->width;
sub[6].height = ihdr->height >> 1;
}
else
{
sub[0].width = ihdr->width;
sub[0].height = ihdr->height;
}
int i;
for(i=0; i < 7; i++)
{
if(sub[i].width == 0 || sub[i].height == 0) continue;
int ret = calculate_scanline_width(ihdr, sub[i].width, &sub[i].scanline_width);
if(ret) return ret;
if(sub[ctx->widest_pass].scanline_width < sub[i].scanline_width) ctx->widest_pass = i;
ctx->last_pass = i;
}
return 0;
}
static int check_decode_fmt(const struct spng_ihdr *ihdr, const int fmt)
{
switch(fmt)
{
case SPNG_FMT_RGBA8:
case SPNG_FMT_RGBA16:
case SPNG_FMT_RGB8:
case SPNG_FMT_PNG:
case SPNG_FMT_RAW:
return 0;
case SPNG_FMT_G8:
case SPNG_FMT_GA8:
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8) return 0;
else return SPNG_EFMT;
case SPNG_FMT_GA16:
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16) return 0;
else return SPNG_EFMT;
default: return SPNG_EFMT;
}
}
static int calculate_image_width(const struct spng_ihdr *ihdr, int fmt, size_t *len)
{
if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL;
size_t res = ihdr->width;
unsigned bytes_per_pixel;
switch(fmt)
{
case SPNG_FMT_RGBA8:
case SPNG_FMT_GA16:
bytes_per_pixel = 4;
break;
case SPNG_FMT_RGBA16:
bytes_per_pixel = 8;
break;
case SPNG_FMT_RGB8:
bytes_per_pixel = 3;
break;
case SPNG_FMT_PNG:
case SPNG_FMT_RAW:
{
int ret = calculate_scanline_width(ihdr, ihdr->width, &res);
if(ret) return ret;
res -= 1; /* exclude filter byte */
bytes_per_pixel = 1;
break;
}
case SPNG_FMT_G8:
bytes_per_pixel = 1;
break;
case SPNG_FMT_GA8:
bytes_per_pixel = 2;
break;
default: return SPNG_EINTERNAL;
}
if(res > SIZE_MAX / bytes_per_pixel) return SPNG_EOVERFLOW;
res = res * bytes_per_pixel;
*len = res;
return 0;
}
static int calculate_image_size(const struct spng_ihdr *ihdr, int fmt, size_t *len)
{
if(ihdr == NULL || len == NULL) return SPNG_EINTERNAL;
size_t res = 0;
int ret = calculate_image_width(ihdr, fmt, &res);
if(ret) return ret;
if(res > SIZE_MAX / ihdr->height) return SPNG_EOVERFLOW;
res = res * ihdr->height;
*len = res;
return 0;
}
static int increase_cache_usage(spng_ctx *ctx, size_t bytes, int new_chunk)
{
if(ctx == NULL || !bytes) return SPNG_EINTERNAL;
if(new_chunk)
{
ctx->chunk_count_total++;
if(ctx->chunk_count_total < 1) return SPNG_EOVERFLOW;
if(ctx->chunk_count_total > ctx->chunk_count_limit) return SPNG_ECHUNK_LIMITS;
}
size_t new_usage = ctx->chunk_cache_usage + bytes;
if(new_usage < ctx->chunk_cache_usage) return SPNG_EOVERFLOW;
if(new_usage > ctx->chunk_cache_limit) return SPNG_ECHUNK_LIMITS;
ctx->chunk_cache_usage = new_usage;
return 0;
}
static int decrease_cache_usage(spng_ctx *ctx, size_t usage)
{
if(ctx == NULL || !usage) return SPNG_EINTERNAL;
if(usage > ctx->chunk_cache_usage) return SPNG_EINTERNAL;
ctx->chunk_cache_usage -= usage;
return 0;
}
static int is_critical_chunk(struct spng_chunk *chunk)
{
if(chunk == NULL) return 0;
if((chunk->type[0] & (1 << 5)) == 0) return 1;
return 0;
}
static int decode_err(spng_ctx *ctx, int err)
{
ctx->state = SPNG_STATE_INVALID;
return err;
}
static int encode_err(spng_ctx *ctx, int err)
{
ctx->state = SPNG_STATE_INVALID;
return err;
}
static inline int read_data(spng_ctx *ctx, size_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!bytes) return 0;
if(ctx->streaming && (bytes > SPNG_READ_SIZE)) return SPNG_EINTERNAL;
int ret = ctx->read_fn(ctx, ctx->stream_user_ptr, ctx->stream_buf, bytes);
if(ret)
{
if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR;
return ret;
}
ctx->bytes_read += bytes;
if(ctx->bytes_read < bytes) return SPNG_EOVERFLOW;
return 0;
}
/* Ensure there is enough space for encoding starting at ctx->write_ptr */
static int require_bytes(spng_ctx *ctx, size_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(ctx->streaming)
{
if(bytes > ctx->stream_buf_size)
{
size_t new_size = ctx->stream_buf_size;
/* Start at default IDAT size + header + crc */
if(new_size < (SPNG_WRITE_SIZE + 12)) new_size = SPNG_WRITE_SIZE + 12;
if(new_size < bytes) new_size = bytes;
void *temp = spng__realloc(ctx, ctx->stream_buf, new_size);
if(temp == NULL) return encode_err(ctx, SPNG_EMEM);
ctx->stream_buf = temp;
ctx->stream_buf_size = bytes;
ctx->write_ptr = ctx->stream_buf;
}
return 0;
}
if(!ctx->internal_buffer) return SPNG_ENODST;
size_t required = ctx->bytes_encoded + bytes;
if(required < bytes) return SPNG_EOVERFLOW;
if(required > ctx->out_png_size)
{
size_t new_size = ctx->out_png_size;
/* Start with a size that doesn't require a realloc() 100% of the time */
if(new_size < (SPNG_WRITE_SIZE * 2)) new_size = SPNG_WRITE_SIZE * 2;
/* Prefer the next power of two over the requested size */
while(new_size < required)
{
if(new_size / SIZE_MAX > 2) return encode_err(ctx, SPNG_EOVERFLOW);
new_size *= 2;
}
void *temp = spng__realloc(ctx, ctx->out_png, new_size);
if(temp == NULL) return encode_err(ctx, SPNG_EMEM);
ctx->out_png = temp;
ctx->out_png_size = new_size;
ctx->write_ptr = ctx->out_png + ctx->bytes_encoded;
}
return 0;
}
static int write_data(spng_ctx *ctx, const void *data, size_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!bytes) return 0;
if(ctx->streaming)
{
if(bytes > SPNG_WRITE_SIZE) return SPNG_EINTERNAL;
int ret = ctx->write_fn(ctx, ctx->stream_user_ptr, (void*)data, bytes);
if(ret)
{
if(ret > 0 || ret < SPNG_IO_ERROR) ret = SPNG_IO_ERROR;
return encode_err(ctx, ret);
}
}
else
{
int ret = require_bytes(ctx, bytes);
if(ret) return encode_err(ctx, ret);
memcpy(ctx->write_ptr, data, bytes);
ctx->write_ptr += bytes;
}
ctx->bytes_encoded += bytes;
if(ctx->bytes_encoded < bytes) return SPNG_EOVERFLOW;
return 0;
}
static int write_header(spng_ctx *ctx, const uint8_t chunk_type[4], size_t chunk_length, unsigned char **data)
{
if(ctx == NULL || chunk_type == NULL) return SPNG_EINTERNAL;
if(chunk_length > spng_u32max) return SPNG_EINTERNAL;
size_t total = chunk_length + 12;
int ret = require_bytes(ctx, total);
if(ret) return ret;
uint32_t crc = crc32(0, NULL, 0);
ctx->current_chunk.crc = crc32(crc, chunk_type, 4);
memcpy(&ctx->current_chunk.type, chunk_type, 4);
ctx->current_chunk.length = (uint32_t)chunk_length;
if(!data) return SPNG_EINTERNAL;
if(ctx->streaming) *data = ctx->stream_buf + 8;
else *data = ctx->write_ptr + 8;
return 0;
}
static int trim_chunk(spng_ctx *ctx, uint32_t length)
{
if(length > spng_u32max) return SPNG_EINTERNAL;
if(length > ctx->current_chunk.length) return SPNG_EINTERNAL;
ctx->current_chunk.length = length;
return 0;
}
static int finish_chunk(spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
struct spng_chunk *chunk = &ctx->current_chunk;
unsigned char *header;
unsigned char *chunk_data;
if(ctx->streaming)
{
chunk_data = ctx->stream_buf + 8;
header = ctx->stream_buf;
}
else
{
chunk_data = ctx->write_ptr + 8;
header = ctx->write_ptr;
}
write_u32(header, chunk->length);
memcpy(header + 4, chunk->type, 4);
chunk->crc = crc32(chunk->crc, chunk_data, chunk->length);
write_u32(chunk_data + chunk->length, chunk->crc);
if(ctx->streaming)
{
const unsigned char *ptr = ctx->stream_buf;
uint32_t bytes_left = chunk->length + 12;
uint32_t len = 0;
while(bytes_left)
{
ptr += len;
len = SPNG_WRITE_SIZE;
if(len > bytes_left) len = bytes_left;
int ret = write_data(ctx, ptr, len);
if(ret) return ret;
bytes_left -= len;
}
}
else
{
ctx->bytes_encoded += chunk->length;
if(ctx->bytes_encoded < chunk->length) return SPNG_EOVERFLOW;
ctx->bytes_encoded += 12;
if(ctx->bytes_encoded < 12) return SPNG_EOVERFLOW;
ctx->write_ptr += chunk->length + 12;
}
return 0;
}
static int write_chunk(spng_ctx *ctx, const uint8_t type[4], const void *data, size_t length)
{
if(ctx == NULL || type == NULL) return SPNG_EINTERNAL;
if(length && data == NULL) return SPNG_EINTERNAL;
unsigned char *write_ptr;
int ret = write_header(ctx, type, length, &write_ptr);
if(ret) return ret;
if(length) memcpy(write_ptr, data, length);
return finish_chunk(ctx);
}
static int write_iend(spng_ctx *ctx)
{
unsigned char iend_chunk[12] = { 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 };
return write_data(ctx, iend_chunk, 12);
}
static int write_unknown_chunks(spng_ctx *ctx, enum spng_location location)
{
if(!ctx->stored.unknown) return 0;
const struct spng_unknown_chunk *chunk = ctx->chunk_list;
uint32_t i;
for(i=0; i < ctx->n_chunks; i++, chunk++)
{
if(chunk->location != location) continue;
int ret = write_chunk(ctx, chunk->type, chunk->data, chunk->length);
if(ret) return ret;
}
return 0;
}
/* Read and check the current chunk's crc,
returns -SPNG_CRC_DISCARD if the chunk should be discarded */
static inline int read_and_check_crc(spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
int ret;
ret = read_data(ctx, 4);
if(ret) return ret;
ctx->current_chunk.crc = read_u32(ctx->data);
if(ctx->skip_crc) return 0;
if(ctx->cur_actual_crc != ctx->current_chunk.crc)
{
if(is_critical_chunk(&ctx->current_chunk))
{
if(ctx->crc_action_critical == SPNG_CRC_USE) return 0;
}
else
{
if(ctx->crc_action_ancillary == SPNG_CRC_USE) return 0;
if(ctx->crc_action_ancillary == SPNG_CRC_DISCARD) return -SPNG_CRC_DISCARD;
}
return SPNG_ECHUNK_CRC;
}
return 0;
}
/* Read and validate the current chunk's crc and the next chunk header */
static inline int read_header(spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
int ret;
struct spng_chunk chunk = { 0 };
ret = read_and_check_crc(ctx);
if(ret)
{
if(ret == -SPNG_CRC_DISCARD)
{
ctx->discard = 1;
}
else return ret;
}
ret = read_data(ctx, 8);
if(ret) return ret;
chunk.offset = ctx->bytes_read - 8;
chunk.length = read_u32(ctx->data);
memcpy(&chunk.type, ctx->data + 4, 4);
if(chunk.length > spng_u32max) return SPNG_ECHUNK_STDLEN;
ctx->cur_chunk_bytes_left = chunk.length;
if(is_critical_chunk(&chunk) && ctx->crc_action_critical == SPNG_CRC_USE) ctx->skip_crc = 1;
else if(ctx->crc_action_ancillary == SPNG_CRC_USE) ctx->skip_crc = 1;
else ctx->skip_crc = 0;
if(!ctx->skip_crc)
{
ctx->cur_actual_crc = crc32(0, NULL, 0);
ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, chunk.type, 4);
}
ctx->current_chunk = chunk;
return 0;
}
/* Read chunk bytes and update crc */
static int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL;
if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */
int ret;
ret = read_data(ctx, bytes);
if(ret) return ret;
if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, ctx->data, bytes);
ctx->cur_chunk_bytes_left -= bytes;
return ret;
}
/* read_chunk_bytes() + read_data() with custom output buffer */
static int read_chunk_bytes2(spng_ctx *ctx, void *out, uint32_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!ctx->cur_chunk_bytes_left || !bytes) return SPNG_EINTERNAL;
if(bytes > ctx->cur_chunk_bytes_left) return SPNG_EINTERNAL; /* XXX: more specific error? */
int ret;
uint32_t len = bytes;
if(ctx->streaming && len > SPNG_READ_SIZE) len = SPNG_READ_SIZE;
while(bytes)
{
if(len > bytes) len = bytes;
ret = ctx->read_fn(ctx, ctx->stream_user_ptr, out, len);
if(ret) return ret;
if(!ctx->streaming) memcpy(out, ctx->data, len);
ctx->bytes_read += len;
if(ctx->bytes_read < len) return SPNG_EOVERFLOW;
if(!ctx->skip_crc) ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, out, len);
ctx->cur_chunk_bytes_left -= len;
out = (char*)out + len;
bytes -= len;
len = SPNG_READ_SIZE;
}
return 0;
}
static int discard_chunk_bytes(spng_ctx *ctx, uint32_t bytes)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!bytes) return 0;
int ret;
if(ctx->streaming) /* Do small, consecutive reads */
{
while(bytes)
{
uint32_t len = SPNG_READ_SIZE;
if(len > bytes) len = bytes;
ret = read_chunk_bytes(ctx, len);
if(ret) return ret;
bytes -= len;
}
}
else
{
ret = read_chunk_bytes(ctx, bytes);
if(ret) return ret;
}
return 0;
}
static int spng__inflate_init(spng_ctx *ctx, int window_bits)
{
if(ctx->zstream.state) inflateEnd(&ctx->zstream);
ctx->inflate = 1;
ctx->zstream.zalloc = spng__zalloc;
ctx->zstream.zfree = spng__zfree;
ctx->zstream.opaque = ctx;
if(inflateInit2(&ctx->zstream, window_bits) != Z_OK) return SPNG_EZLIB_INIT;
#if ZLIB_VERNUM >= 0x1290 && !defined(SPNG_USE_MINIZ)
int validate = 1;
if(ctx->flags & SPNG_CTX_IGNORE_ADLER32) validate = 0;
if(is_critical_chunk(&ctx->current_chunk))
{
if(ctx->crc_action_critical == SPNG_CRC_USE) validate = 0;
}
else /* ancillary */
{
if(ctx->crc_action_ancillary == SPNG_CRC_USE) validate = 0;
}
if(inflateValidate(&ctx->zstream, validate)) return SPNG_EZLIB_INIT;
#else /* This requires zlib >= 1.2.11 */
#pragma message ("inflateValidate() not available, SPNG_CTX_IGNORE_ADLER32 will be ignored")
#endif
return 0;
}
static int spng__deflate_init(spng_ctx *ctx, struct spng__zlib_options *options)
{
if(ctx->zstream.state) deflateEnd(&ctx->zstream);
ctx->deflate = 1;
z_stream *zstream = &ctx->zstream;
zstream->zalloc = spng__zalloc;
zstream->zfree = spng__zfree;
zstream->opaque = ctx;
zstream->data_type = options->data_type;
int ret = deflateInit2(zstream, options->compression_level, Z_DEFLATED, options->window_bits, options->mem_level, options->strategy);
if(ret != Z_OK) return SPNG_EZLIB_INIT;
return 0;
}
/* Inflate a zlib stream starting with start_buf if non-NULL,
continuing from the datastream till an end marker,
allocating and writing the inflated stream to *out,
leaving "extra" bytes at the end, final buffer length is *len.
Takes into account the chunk size and cache limits.
*/
static int spng__inflate_stream(spng_ctx *ctx, char **out, size_t *len, size_t extra, const void *start_buf, size_t start_len)
{
int ret = spng__inflate_init(ctx, 15);
if(ret) return ret;
size_t max = ctx->chunk_cache_limit - ctx->chunk_cache_usage;
if(ctx->max_chunk_size < max) max = ctx->max_chunk_size;
if(extra > max) return SPNG_ECHUNK_LIMITS;
max -= extra;
uint32_t read_size;
size_t size = 8 * 1024;
void *t, *buf = spng__malloc(ctx, size);
if(buf == NULL) return SPNG_EMEM;
z_stream *stream = &ctx->zstream;
if(start_buf != NULL && start_len)
{
stream->avail_in = (uInt)start_len;
stream->next_in = start_buf;
}
else
{
stream->avail_in = 0;
stream->next_in = NULL;
}
stream->avail_out = (uInt)size;
stream->next_out = buf;
while(ret != Z_STREAM_END)
{
ret = inflate(stream, Z_NO_FLUSH);
if(ret == Z_STREAM_END) break;
if(ret != Z_OK && ret != Z_BUF_ERROR)
{
ret = SPNG_EZLIB;
goto err;
}
if(!stream->avail_out) /* Resize buffer */
{
/* overflow or reached chunk/cache limit */
if( (2 > SIZE_MAX / size) || (size > max / 2) )
{
ret = SPNG_ECHUNK_LIMITS;
goto err;
}
size *= 2;
t = spng__realloc(ctx, buf, size);
if(t == NULL) goto mem;
buf = t;
stream->avail_out = (uInt)size / 2;
stream->next_out = (unsigned char*)buf + size / 2;
}
else if(!stream->avail_in) /* Read more chunk bytes */
{
read_size = ctx->cur_chunk_bytes_left;
if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE;
ret = read_chunk_bytes(ctx, read_size);
if(ret)
{
if(!read_size) ret = SPNG_EZLIB;
goto err;
}
stream->avail_in = read_size;
stream->next_in = ctx->data;
}
}
size = stream->total_out;
if(!size)
{
ret = SPNG_EZLIB;
goto err;
}
size += extra;
if(size < extra) goto mem;
t = spng__realloc(ctx, buf, size);
if(t == NULL) goto mem;
buf = t;
(void)increase_cache_usage(ctx, size, 0);
*out = buf;
*len = size;
return 0;
mem:
ret = SPNG_EMEM;
err:
spng__free(ctx, buf);
return ret;
}
/* Read at least one byte from the IDAT stream */
static int read_idat_bytes(spng_ctx *ctx, uint32_t *bytes_read)
{
if(ctx == NULL || bytes_read == NULL) return SPNG_EINTERNAL;
if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT;
int ret;
uint32_t len;
while(!ctx->cur_chunk_bytes_left)
{
ret = read_header(ctx);
if(ret) return ret;
if(memcmp(ctx->current_chunk.type, type_idat, 4)) return SPNG_EIDAT_TOO_SHORT;
}
if(ctx->streaming)
{/* TODO: estimate bytes to read for progressive reads */
len = SPNG_READ_SIZE;
if(len > ctx->cur_chunk_bytes_left) len = ctx->cur_chunk_bytes_left;
}
else len = ctx->current_chunk.length;
ret = read_chunk_bytes(ctx, len);
*bytes_read = len;
return ret;
}
static int read_scanline_bytes(spng_ctx *ctx, unsigned char *dest, size_t len)
{
if(ctx == NULL || dest == NULL) return SPNG_EINTERNAL;
int ret = Z_OK;
uint32_t bytes_read;
z_stream *zstream = &ctx->zstream;
zstream->avail_out = (uInt)len;
zstream->next_out = dest;
while(zstream->avail_out != 0)
{
ret = inflate(zstream, Z_NO_FLUSH);
if(ret == Z_OK) continue;
if(ret == Z_STREAM_END) /* Reached an end-marker */
{
if(zstream->avail_out != 0) return SPNG_EIDAT_TOO_SHORT;
}
else if(ret == Z_BUF_ERROR) /* Read more IDAT bytes */
{
ret = read_idat_bytes(ctx, &bytes_read);
if(ret) return ret;
zstream->avail_in = bytes_read;
zstream->next_in = ctx->data;
}
else return SPNG_EIDAT_STREAM;
}
return 0;
}
static uint8_t paeth(uint8_t a, uint8_t b, uint8_t c)
{
int16_t p = a + b - c;
int16_t pa = abs(p - a);
int16_t pb = abs(p - b);
int16_t pc = abs(p - c);
if(pa <= pb && pa <= pc) return a;
else if(pb <= pc) return b;
return c;
}
SPNG_TARGET_CLONES("default,avx2")
static void defilter_up(size_t bytes, unsigned char *row, const unsigned char *prev)
{
size_t i;
for(i=0; i < bytes; i++)
{
row[i] += prev[i];
}
}
/* Defilter *scanline in-place.
*prev_scanline and *scanline should point to the first pixel,
scanline_width is the width of the scanline including the filter byte.
*/
static int defilter_scanline(const unsigned char *prev_scanline, unsigned char *scanline,
size_t scanline_width, unsigned bytes_per_pixel, unsigned filter)
{
if(prev_scanline == NULL || scanline == NULL || !scanline_width) return SPNG_EINTERNAL;
size_t i;
scanline_width--;
if(filter == 0) return 0;
#ifndef SPNG_DISABLE_OPT
if(filter == SPNG_FILTER_UP) goto no_opt;
if(bytes_per_pixel == 4)
{
if(filter == SPNG_FILTER_SUB)
defilter_sub4(scanline_width, scanline);
else if(filter == SPNG_FILTER_AVERAGE)
defilter_avg4(scanline_width, scanline, prev_scanline);
else if(filter == SPNG_FILTER_PAETH)
defilter_paeth4(scanline_width, scanline, prev_scanline);
else return SPNG_EFILTER;
return 0;
}
else if(bytes_per_pixel == 3)
{
if(filter == SPNG_FILTER_SUB)
defilter_sub3(scanline_width, scanline);
else if(filter == SPNG_FILTER_AVERAGE)
defilter_avg3(scanline_width, scanline, prev_scanline);
else if(filter == SPNG_FILTER_PAETH)
defilter_paeth3(scanline_width, scanline, prev_scanline);
else return SPNG_EFILTER;
return 0;
}
no_opt:
#endif
if(filter == SPNG_FILTER_UP)
{
defilter_up(scanline_width, scanline, prev_scanline);
return 0;
}
for(i=0; i < scanline_width; i++)
{
uint8_t x, a, b, c;
if(i >= bytes_per_pixel)
{
a = scanline[i - bytes_per_pixel];
b = prev_scanline[i];
c = prev_scanline[i - bytes_per_pixel];
}
else /* First pixel in row */
{
a = 0;
b = prev_scanline[i];
c = 0;
}
x = scanline[i];
switch(filter)
{
case SPNG_FILTER_SUB:
{
x = x + a;
break;
}
case SPNG_FILTER_AVERAGE:
{
uint16_t avg = (a + b) / 2;
x = x + avg;
break;
}
case SPNG_FILTER_PAETH:
{
x = x + paeth(a,b,c);
break;
}
}
scanline[i] = x;
}
return 0;
}
static int filter_scanline(unsigned char *filtered, const unsigned char *prev_scanline, const unsigned char *scanline,
size_t scanline_width, unsigned bytes_per_pixel, const unsigned filter)
{
if(prev_scanline == NULL || scanline == NULL || scanline_width <= 1) return SPNG_EINTERNAL;
if(filter > 4) return SPNG_EFILTER;
if(filter == 0) return 0;
scanline_width--;
uint32_t i;
for(i=0; i < scanline_width; i++)
{
uint8_t x, a, b, c;
if(i >= bytes_per_pixel)
{
a = scanline[i - bytes_per_pixel];
b = prev_scanline[i];
c = prev_scanline[i - bytes_per_pixel];
}
else /* first pixel in row */
{
a = 0;
b = prev_scanline[i];
c = 0;
}
x = scanline[i];
switch(filter)
{
case SPNG_FILTER_SUB:
{
x = x - a;
break;
}
case SPNG_FILTER_UP:
{
x = x - b;
break;
}
case SPNG_FILTER_AVERAGE:
{
uint16_t avg = (a + b) / 2;
x = x - avg;
break;
}
case SPNG_FILTER_PAETH:
{
x = x - paeth(a,b,c);
break;
}
}
filtered[i] = x;
}
return 0;
}
static int32_t filter_sum(const unsigned char *prev_scanline, const unsigned char *scanline,
size_t size, unsigned bytes_per_pixel, const unsigned filter)
{
/* prevent potential over/underflow, bails out at a width of ~8M pixels for RGBA8 */
if(size > (INT32_MAX / 128)) return INT32_MAX;
uint32_t i;
int32_t sum = 0;
uint8_t x, a, b, c;
for(i=0; i < size; i++)
{
if(i >= bytes_per_pixel)
{
a = scanline[i - bytes_per_pixel];
b = prev_scanline[i];
c = prev_scanline[i - bytes_per_pixel];
}
else /* first pixel in row */
{
a = 0;
b = prev_scanline[i];
c = 0;
}
x = scanline[i];
switch(filter)
{
case SPNG_FILTER_NONE:
{
break;
}
case SPNG_FILTER_SUB:
{
x = x - a;
break;
}
case SPNG_FILTER_UP:
{
x = x - b;
break;
}
case SPNG_FILTER_AVERAGE:
{
uint16_t avg = (a + b) / 2;
x = x - avg;
break;
}
case SPNG_FILTER_PAETH:
{
x = x - paeth(a,b,c);
break;
}
}
sum += 128 - abs((int)x - 128);
}
return sum;
}
static unsigned get_best_filter(const unsigned char *prev_scanline, const unsigned char *scanline,
size_t scanline_width, unsigned bytes_per_pixel, const int choices)
{
if(!choices) return SPNG_FILTER_NONE;
scanline_width--;
int i;
unsigned int best_filter = 0;
enum spng_filter_choice flag;
int32_t sum, best_score = INT32_MAX;
int32_t filter_scores[5] = { INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX };
if( !(choices & (choices - 1)) )
{/* only one choice/bit is set */
for(i=0; i < 5; i++)
{
if(choices == 1 << (i + 3)) return i;
}
}
for(i=0; i < 5; i++)
{
flag = 1 << (i + 3);
if(choices & flag) sum = filter_sum(prev_scanline, scanline, scanline_width, bytes_per_pixel, i);
else continue;
filter_scores[i] = abs(sum);
if(filter_scores[i] < best_score)
{
best_score = filter_scores[i];
best_filter = i;
}
}
return best_filter;
}
/* Scale "sbits" significant bits in "sample" from "bit_depth" to "target"
"bit_depth" must be a valid PNG depth
"sbits" must be less than or equal to "bit_depth"
"target" must be between 1 and 16
*/
static uint16_t sample_to_target(uint16_t sample, unsigned bit_depth, unsigned sbits, unsigned target)
{
if(bit_depth == sbits)
{
if(target == sbits) return sample; /* No scaling */
}/* bit_depth > sbits */
else sample = sample >> (bit_depth - sbits); /* Shift significant bits to bottom */
/* Downscale */
if(target < sbits) return sample >> (sbits - target);
/* Upscale using left bit replication */
int8_t shift_amount = target - sbits;
uint16_t sample_bits = sample;
sample = 0;
while(shift_amount >= 0)
{
sample = sample | (sample_bits << shift_amount);
shift_amount -= sbits;
}
int8_t partial = shift_amount + (int8_t)sbits;
if(partial != 0) sample = sample | (sample_bits >> abs(shift_amount));
return sample;
}
static inline void gamma_correct_row(unsigned char *row, uint32_t pixels, int fmt, const uint16_t *gamma_lut)
{
uint32_t i;
if(fmt == SPNG_FMT_RGBA8)
{
unsigned char *px;
for(i=0; i < pixels; i++)
{
px = row + i * 4;
px[0] = gamma_lut[px[0]];
px[1] = gamma_lut[px[1]];
px[2] = gamma_lut[px[2]];
}
}
else if(fmt == SPNG_FMT_RGBA16)
{
for(i=0; i < pixels; i++)
{
uint16_t px[4];
memcpy(px, row + i * 8, 8);
px[0] = gamma_lut[px[0]];
px[1] = gamma_lut[px[1]];
px[2] = gamma_lut[px[2]];
memcpy(row + i * 8, px, 8);
}
}
else if(fmt == SPNG_FMT_RGB8)
{
unsigned char *px;
for(i=0; i < pixels; i++)
{
px = row + i * 3;
px[0] = gamma_lut[px[0]];
px[1] = gamma_lut[px[1]];
px[2] = gamma_lut[px[2]];
}
}
}
/* Apply transparency to output row */
static inline void trns_row(unsigned char *row,
const unsigned char *scanline,
const unsigned char *trns,
unsigned scanline_stride,
struct spng_ihdr *ihdr,
uint32_t pixels,
int fmt)
{
uint32_t i;
unsigned row_stride;
unsigned depth = ihdr->bit_depth;
if(fmt == SPNG_FMT_RGBA8)
{
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */
row_stride = 4;
for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)
{
if(!memcmp(scanline, trns, scanline_stride)) row[3] = 0;
}
}
else if(fmt == SPNG_FMT_RGBA16)
{
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) return; /* already applied in the decoding loop */
row_stride = 8;
for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)
{
if(!memcmp(scanline, trns, scanline_stride)) memset(row + 6, 0, 2);
}
}
else if(fmt == SPNG_FMT_GA8)
{
row_stride = 2;
if(depth == 16)
{
for(i=0; i < pixels; i++, scanline+=scanline_stride, row+=row_stride)
{
if(!memcmp(scanline, trns, scanline_stride)) memset(row + 1, 0, 1);
}
}
else /* depth <= 8 */
{
struct spng__iter iter = spng__iter_init(depth, scanline);
for(i=0; i < pixels; i++, row+=row_stride)
{
if(trns[0] == get_sample(&iter)) row[1] = 0;
}
}
}
else if(fmt == SPNG_FMT_GA16)
{
row_stride = 4;
if(depth == 16)
{
for(i=0; i< pixels; i++, scanline+=scanline_stride, row+=row_stride)
{
if(!memcmp(scanline, trns, 2)) memset(row + 2, 0, 2);
}
}
else
{
struct spng__iter iter = spng__iter_init(depth, scanline);
for(i=0; i< pixels; i++, row+=row_stride)
{
if(trns[0] == get_sample(&iter)) memset(row + 2, 0, 2);
}
}
}
else return;
}
static inline void scale_row(unsigned char *row, uint32_t pixels, int fmt, unsigned depth, const struct spng_sbit *sbit)
{
uint32_t i;
if(fmt == SPNG_FMT_RGBA8)
{
unsigned char px[4];
for(i=0; i < pixels; i++)
{
memcpy(px, row + i * 4, 4);
px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8);
px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8);
px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8);
px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 8);
memcpy(row + i * 4, px, 4);
}
}
else if(fmt == SPNG_FMT_RGBA16)
{
uint16_t px[4];
for(i=0; i < pixels; i++)
{
memcpy(px, row + i * 8, 8);
px[0] = sample_to_target(px[0], depth, sbit->red_bits, 16);
px[1] = sample_to_target(px[1], depth, sbit->green_bits, 16);
px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 16);
px[3] = sample_to_target(px[3], depth, sbit->alpha_bits, 16);
memcpy(row + i * 8, px, 8);
}
}
else if(fmt == SPNG_FMT_RGB8)
{
unsigned char px[4];
for(i=0; i < pixels; i++)
{
memcpy(px, row + i * 3, 3);
px[0] = sample_to_target(px[0], depth, sbit->red_bits, 8);
px[1] = sample_to_target(px[1], depth, sbit->green_bits, 8);
px[2] = sample_to_target(px[2], depth, sbit->blue_bits, 8);
memcpy(row + i * 3, px, 3);
}
}
else if(fmt == SPNG_FMT_G8)
{
for(i=0; i < pixels; i++)
{
row[i] = sample_to_target(row[i], depth, sbit->grayscale_bits, 8);
}
}
else if(fmt == SPNG_FMT_GA8)
{
for(i=0; i < pixels; i++)
{
row[i*2] = sample_to_target(row[i*2], depth, sbit->grayscale_bits, 8);
}
}
}
/* Expand to *row using 8-bit palette indices from *scanline */
static void expand_row(unsigned char *row,
const unsigned char *scanline,
const union spng__decode_plte *decode_plte,
uint32_t width,
int fmt)
{
uint32_t i = 0;
unsigned char *px;
unsigned char entry;
const struct spng_plte_entry *plte = decode_plte->rgba;
#if defined(SPNG_ARM)
if(fmt == SPNG_FMT_RGBA8) i = expand_palette_rgba8_neon(row, scanline, decode_plte->raw, width);
else if(fmt == SPNG_FMT_RGB8)
{
i = expand_palette_rgb8_neon(row, scanline, decode_plte->raw, width);
for(; i < width; i++)
{/* In this case the LUT is 3 bytes packed */
px = row + i * 3;
entry = scanline[i];
px[0] = decode_plte->raw[entry * 3 + 0];
px[1] = decode_plte->raw[entry * 3 + 1];
px[2] = decode_plte->raw[entry * 3 + 2];
}
return;
}
#endif
if(fmt == SPNG_FMT_RGBA8)
{
for(; i < width; i++)
{
px = row + i * 4;
entry = scanline[i];
px[0] = plte[entry].red;
px[1] = plte[entry].green;
px[2] = plte[entry].blue;
px[3] = plte[entry].alpha;
}
}
else if(fmt == SPNG_FMT_RGB8)
{
for(; i < width; i++)
{
px = row + i * 3;
entry = scanline[i];
px[0] = plte[entry].red;
px[1] = plte[entry].green;
px[2] = plte[entry].blue;
}
}
}
/* Unpack 1/2/4/8-bit samples to G8/GA8/GA16 or G16 -> GA16 */
static void unpack_scanline(unsigned char *out, const unsigned char *scanline, uint32_t width, unsigned bit_depth, int fmt)
{
struct spng__iter iter = spng__iter_init(bit_depth, scanline);
uint32_t i;
uint16_t sample, alpha = 65535;
if(fmt == SPNG_FMT_GA8) goto ga8;
else if(fmt == SPNG_FMT_GA16) goto ga16;
/* 1/2/4-bit -> 8-bit */
for(i=0; i < width; i++) out[i] = get_sample(&iter);
return;
ga8:
/* 1/2/4/8-bit -> GA8 */
for(i=0; i < width; i++)
{
out[i*2] = get_sample(&iter);
out[i*2 + 1] = 255;
}
return;
ga16:
/* 16 -> GA16 */
if(bit_depth == 16)
{
for(i=0; i < width; i++)
{
memcpy(out + i * 4, scanline + i * 2, 2);
memcpy(out + i * 4 + 2, &alpha, 2);
}
return;
}
/* 1/2/4/8-bit -> GA16 */
for(i=0; i < width; i++)
{
sample = get_sample(&iter);
memcpy(out + i * 4, &sample, 2);
memcpy(out + i * 4 + 2, &alpha, 2);
}
}
static int check_ihdr(const struct spng_ihdr *ihdr, uint32_t max_width, uint32_t max_height)
{
if(ihdr->width > spng_u32max || !ihdr->width) return SPNG_EWIDTH;
if(ihdr->height > spng_u32max || !ihdr->height) return SPNG_EHEIGHT;
if(ihdr->width > max_width) return SPNG_EUSER_WIDTH;
if(ihdr->height > max_height) return SPNG_EUSER_HEIGHT;
switch(ihdr->color_type)
{
case SPNG_COLOR_TYPE_GRAYSCALE:
{
if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 ||
ihdr->bit_depth == 4 || ihdr->bit_depth == 8 ||
ihdr->bit_depth == 16) )
return SPNG_EBIT_DEPTH;
break;
}
case SPNG_COLOR_TYPE_TRUECOLOR:
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
{
if( !(ihdr->bit_depth == 8 || ihdr->bit_depth == 16) )
return SPNG_EBIT_DEPTH;
break;
}
case SPNG_COLOR_TYPE_INDEXED:
{
if( !(ihdr->bit_depth == 1 || ihdr->bit_depth == 2 ||
ihdr->bit_depth == 4 || ihdr->bit_depth == 8) )
return SPNG_EBIT_DEPTH;
break;
}
default: return SPNG_ECOLOR_TYPE;
}
if(ihdr->compression_method) return SPNG_ECOMPRESSION_METHOD;
if(ihdr->filter_method) return SPNG_EFILTER_METHOD;
if(ihdr->interlace_method > 1) return SPNG_EINTERLACE_METHOD;
return 0;
}
static int check_plte(const struct spng_plte *plte, const struct spng_ihdr *ihdr)
{
if(plte == NULL || ihdr == NULL) return 1;
if(plte->n_entries == 0) return 1;
if(plte->n_entries > 256) return 1;
if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED)
{
if(plte->n_entries > (1U << ihdr->bit_depth)) return 1;
}
return 0;
}
static int check_sbit(const struct spng_sbit *sbit, const struct spng_ihdr *ihdr)
{
if(sbit == NULL || ihdr == NULL) return 1;
if(ihdr->color_type == 0)
{
if(sbit->grayscale_bits == 0) return SPNG_ESBIT;
if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT;
}
else if(ihdr->color_type == 2 || ihdr->color_type == 3)
{
if(sbit->red_bits == 0) return SPNG_ESBIT;
if(sbit->green_bits == 0) return SPNG_ESBIT;
if(sbit->blue_bits == 0) return SPNG_ESBIT;
uint8_t bit_depth;
if(ihdr->color_type == 3) bit_depth = 8;
else bit_depth = ihdr->bit_depth;
if(sbit->red_bits > bit_depth) return SPNG_ESBIT;
if(sbit->green_bits > bit_depth) return SPNG_ESBIT;
if(sbit->blue_bits > bit_depth) return SPNG_ESBIT;
}
else if(ihdr->color_type == 4)
{
if(sbit->grayscale_bits == 0) return SPNG_ESBIT;
if(sbit->alpha_bits == 0) return SPNG_ESBIT;
if(sbit->grayscale_bits > ihdr->bit_depth) return SPNG_ESBIT;
if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT;
}
else if(ihdr->color_type == 6)
{
if(sbit->red_bits == 0) return SPNG_ESBIT;
if(sbit->green_bits == 0) return SPNG_ESBIT;
if(sbit->blue_bits == 0) return SPNG_ESBIT;
if(sbit->alpha_bits == 0) return SPNG_ESBIT;
if(sbit->red_bits > ihdr->bit_depth) return SPNG_ESBIT;
if(sbit->green_bits > ihdr->bit_depth) return SPNG_ESBIT;
if(sbit->blue_bits > ihdr->bit_depth) return SPNG_ESBIT;
if(sbit->alpha_bits > ihdr->bit_depth) return SPNG_ESBIT;
}
return 0;
}
static int check_chrm_int(const struct spng_chrm_int *chrm_int)
{
if(chrm_int == NULL) return 1;
if(chrm_int->white_point_x > spng_u32max ||
chrm_int->white_point_y > spng_u32max ||
chrm_int->red_x > spng_u32max ||
chrm_int->red_y > spng_u32max ||
chrm_int->green_x > spng_u32max ||
chrm_int->green_y > spng_u32max ||
chrm_int->blue_x > spng_u32max ||
chrm_int->blue_y > spng_u32max) return SPNG_ECHRM;
return 0;
}
static int check_phys(const struct spng_phys *phys)
{
if(phys == NULL) return 1;
if(phys->unit_specifier > 1) return SPNG_EPHYS;
if(phys->ppu_x > spng_u32max) return SPNG_EPHYS;
if(phys->ppu_y > spng_u32max) return SPNG_EPHYS;
return 0;
}
static int check_time(const struct spng_time *time)
{
if(time == NULL) return 1;
if(time->month == 0 || time->month > 12) return 1;
if(time->day == 0 || time->day > 31) return 1;
if(time->hour > 23) return 1;
if(time->minute > 59) return 1;
if(time->second > 60) return 1;
return 0;
}
static int check_offs(const struct spng_offs *offs)
{
if(offs == NULL) return 1;
if(offs->unit_specifier > 1) return 1;
return 0;
}
static int check_exif(const struct spng_exif *exif)
{
if(exif == NULL) return 1;
if(exif->data == NULL) return 1;
if(exif->length < 4) return SPNG_ECHUNK_SIZE;
if(exif->length > spng_u32max) return SPNG_ECHUNK_STDLEN;
const uint8_t exif_le[4] = { 73, 73, 42, 0 };
const uint8_t exif_be[4] = { 77, 77, 0, 42 };
if(memcmp(exif->data, exif_le, 4) && memcmp(exif->data, exif_be, 4)) return 1;
return 0;
}
/* Validate PNG keyword */
static int check_png_keyword(const char *str)
{
if(str == NULL) return 1;
size_t len = strlen(str);
const char *end = str + len;
if(!len) return 1;
if(len > 79) return 1;
if(str[0] == ' ') return 1; /* Leading space */
if(end[-1] == ' ') return 1; /* Trailing space */
if(strstr(str, " ") != NULL) return 1; /* Consecutive spaces */
uint8_t c;
while(str != end)
{
memcpy(&c, str, 1);
if( (c >= 32 && c <= 126) || (c >= 161) ) str++;
else return 1; /* Invalid character */
}
return 0;
}
/* Validate PNG text *str up to 'len' bytes */
static int check_png_text(const char *str, size_t len)
{/* XXX: are consecutive newlines permitted? */
if(str == NULL || len == 0) return 1;
uint8_t c;
size_t i = 0;
while(i < len)
{
memcpy(&c, str + i, 1);
if( (c >= 32 && c <= 126) || (c >= 161) || c == 10) i++;
else return 1; /* Invalid character */
}
return 0;
}
/* Returns non-zero for standard chunks which are stored without allocating memory */
static int is_small_chunk(uint8_t type[4])
{
if(!memcmp(type, type_plte, 4)) return 1;
else if(!memcmp(type, type_chrm, 4)) return 1;
else if(!memcmp(type, type_gama, 4)) return 1;
else if(!memcmp(type, type_sbit, 4)) return 1;
else if(!memcmp(type, type_srgb, 4)) return 1;
else if(!memcmp(type, type_bkgd, 4)) return 1;
else if(!memcmp(type, type_trns, 4)) return 1;
else if(!memcmp(type, type_hist, 4)) return 1;
else if(!memcmp(type, type_phys, 4)) return 1;
else if(!memcmp(type, type_time, 4)) return 1;
else if(!memcmp(type, type_offs, 4)) return 1;
else return 0;
}
static int read_ihdr(spng_ctx *ctx)
{
int ret;
struct spng_chunk *chunk = &ctx->current_chunk;
const unsigned char *data;
chunk->offset = 8;
chunk->length = 13;
size_t sizeof_sig_ihdr = 29;
ret = read_data(ctx, sizeof_sig_ihdr);
if(ret) return ret;
data = ctx->data;
if(memcmp(data, spng_signature, sizeof(spng_signature))) return SPNG_ESIGNATURE;
chunk->length = read_u32(data + 8);
memcpy(&chunk->type, data + 12, 4);
if(chunk->length != 13) return SPNG_EIHDR_SIZE;
if(memcmp(chunk->type, type_ihdr, 4)) return SPNG_ENOIHDR;
ctx->cur_actual_crc = crc32(0, NULL, 0);
ctx->cur_actual_crc = crc32(ctx->cur_actual_crc, data + 12, 17);
ctx->ihdr.width = read_u32(data + 16);
ctx->ihdr.height = read_u32(data + 20);
ctx->ihdr.bit_depth = data[24];
ctx->ihdr.color_type = data[25];
ctx->ihdr.compression_method = data[26];
ctx->ihdr.filter_method = data[27];
ctx->ihdr.interlace_method = data[28];
ret = check_ihdr(&ctx->ihdr, ctx->max_width, ctx->max_height);
if(ret) return ret;
ctx->file.ihdr = 1;
ctx->stored.ihdr = 1;
if(ctx->ihdr.bit_depth < 8) ctx->bytes_per_pixel = 1;
else ctx->bytes_per_pixel = num_channels(&ctx->ihdr) * (ctx->ihdr.bit_depth / 8);
ret = calculate_subimages(ctx);
if(ret) return ret;
return 0;
}
static void splt_undo(spng_ctx *ctx)
{
struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1];
spng__free(ctx, splt->entries);
decrease_cache_usage(ctx, sizeof(struct spng_splt));
decrease_cache_usage(ctx, splt->n_entries * sizeof(struct spng_splt_entry));
splt->entries = NULL;
ctx->n_splt--;
}
static void text_undo(spng_ctx *ctx)
{
struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1];
spng__free(ctx, text->keyword);
if(text->compression_flag) spng__free(ctx, text->text);
decrease_cache_usage(ctx, text->cache_usage);
decrease_cache_usage(ctx, sizeof(struct spng_text2));
text->keyword = NULL;
text->text = NULL;
ctx->n_text--;
}
static void chunk_undo(spng_ctx *ctx)
{
struct spng_unknown_chunk *chunk = &ctx->chunk_list[ctx->n_chunks - 1];
spng__free(ctx, chunk->data);
decrease_cache_usage(ctx, chunk->length);
decrease_cache_usage(ctx, sizeof(struct spng_unknown_chunk));
chunk->data = NULL;
ctx->n_chunks--;
}
static int read_non_idat_chunks(spng_ctx *ctx)
{
int ret;
struct spng_chunk chunk;
const unsigned char *data;
ctx->discard = 0;
ctx->undo = NULL;
ctx->prev_stored = ctx->stored;
while( !(ret = read_header(ctx)))
{
if(ctx->discard)
{
if(ctx->undo) ctx->undo(ctx);
ctx->stored = ctx->prev_stored;
}
ctx->discard = 0;
ctx->undo = NULL;
ctx->prev_stored = ctx->stored;
chunk = ctx->current_chunk;
if(!memcmp(chunk.type, type_idat, 4))
{
if(ctx->state < SPNG_STATE_FIRST_IDAT)
{
if(ctx->ihdr.color_type == 3 && !ctx->stored.plte) return SPNG_ENOPLTE;
ctx->first_idat = chunk;
return 0;
}
if(ctx->prev_was_idat)
{
/* Ignore extra IDAT's */
ret = discard_chunk_bytes(ctx, chunk.length);
if(ret) return ret;
continue;
}
else return SPNG_ECHUNK_POS; /* IDAT chunk not at the end of the IDAT sequence */
}
ctx->prev_was_idat = 0;
if(is_small_chunk(chunk.type))
{
/* None of the known chunks can be zero length */
if(!chunk.length) return SPNG_ECHUNK_SIZE;
/* The largest of these chunks is PLTE with 256 entries */
ret = read_chunk_bytes(ctx, chunk.length > 768 ? 768 : chunk.length);
if(ret) return ret;
}
data = ctx->data;
if(is_critical_chunk(&chunk))
{
if(!memcmp(chunk.type, type_plte, 4))
{
if(ctx->file.trns || ctx->file.hist || ctx->file.bkgd) return SPNG_ECHUNK_POS;
if(chunk.length % 3 != 0) return SPNG_ECHUNK_SIZE;
ctx->plte.n_entries = chunk.length / 3;
if(check_plte(&ctx->plte, &ctx->ihdr)) return SPNG_ECHUNK_SIZE; /* XXX: EPLTE? */
size_t i;
for(i=0; i < ctx->plte.n_entries; i++)
{
ctx->plte.entries[i].red = data[i * 3];
ctx->plte.entries[i].green = data[i * 3 + 1];
ctx->plte.entries[i].blue = data[i * 3 + 2];
}
ctx->file.plte = 1;
ctx->stored.plte = 1;
}
else if(!memcmp(chunk.type, type_iend, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT)
{
if(chunk.length) return SPNG_ECHUNK_SIZE;
ret = read_and_check_crc(ctx);
if(ret == -SPNG_CRC_DISCARD) ret = 0;
return ret;
}
else return SPNG_ECHUNK_POS;
}
else if(!memcmp(chunk.type, type_ihdr, 4)) return SPNG_ECHUNK_POS;
else return SPNG_ECHUNK_UNKNOWN_CRITICAL;
}
else if(!memcmp(chunk.type, type_chrm, 4)) /* Ancillary chunks */
{
if(ctx->file.plte) return SPNG_ECHUNK_POS;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.chrm) return SPNG_EDUP_CHRM;
if(chunk.length != 32) return SPNG_ECHUNK_SIZE;
ctx->chrm_int.white_point_x = read_u32(data);
ctx->chrm_int.white_point_y = read_u32(data + 4);
ctx->chrm_int.red_x = read_u32(data + 8);
ctx->chrm_int.red_y = read_u32(data + 12);
ctx->chrm_int.green_x = read_u32(data + 16);
ctx->chrm_int.green_y = read_u32(data + 20);
ctx->chrm_int.blue_x = read_u32(data + 24);
ctx->chrm_int.blue_y = read_u32(data + 28);
if(check_chrm_int(&ctx->chrm_int)) return SPNG_ECHRM;
ctx->file.chrm = 1;
ctx->stored.chrm = 1;
}
else if(!memcmp(chunk.type, type_gama, 4))
{
if(ctx->file.plte) return SPNG_ECHUNK_POS;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.gama) return SPNG_EDUP_GAMA;
if(chunk.length != 4) return SPNG_ECHUNK_SIZE;
ctx->gama = read_u32(data);
if(!ctx->gama) return SPNG_EGAMA;
if(ctx->gama > spng_u32max) return SPNG_EGAMA;
ctx->file.gama = 1;
ctx->stored.gama = 1;
}
else if(!memcmp(chunk.type, type_sbit, 4))
{
if(ctx->file.plte) return SPNG_ECHUNK_POS;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.sbit) return SPNG_EDUP_SBIT;
if(ctx->ihdr.color_type == 0)
{
if(chunk.length != 1) return SPNG_ECHUNK_SIZE;
ctx->sbit.grayscale_bits = data[0];
}
else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 3)
{
if(chunk.length != 3) return SPNG_ECHUNK_SIZE;
ctx->sbit.red_bits = data[0];
ctx->sbit.green_bits = data[1];
ctx->sbit.blue_bits = data[2];
}
else if(ctx->ihdr.color_type == 4)
{
if(chunk.length != 2) return SPNG_ECHUNK_SIZE;
ctx->sbit.grayscale_bits = data[0];
ctx->sbit.alpha_bits = data[1];
}
else if(ctx->ihdr.color_type == 6)
{
if(chunk.length != 4) return SPNG_ECHUNK_SIZE;
ctx->sbit.red_bits = data[0];
ctx->sbit.green_bits = data[1];
ctx->sbit.blue_bits = data[2];
ctx->sbit.alpha_bits = data[3];
}
if(check_sbit(&ctx->sbit, &ctx->ihdr)) return SPNG_ESBIT;
ctx->file.sbit = 1;
ctx->stored.sbit = 1;
}
else if(!memcmp(chunk.type, type_srgb, 4))
{
if(ctx->file.plte) return SPNG_ECHUNK_POS;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.srgb) return SPNG_EDUP_SRGB;
if(chunk.length != 1) return SPNG_ECHUNK_SIZE;
ctx->srgb_rendering_intent = data[0];
if(ctx->srgb_rendering_intent > 3) return SPNG_ESRGB;
ctx->file.srgb = 1;
ctx->stored.srgb = 1;
}
else if(!memcmp(chunk.type, type_bkgd, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.bkgd) return SPNG_EDUP_BKGD;
if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4)
{
if(chunk.length != 2) return SPNG_ECHUNK_SIZE;
ctx->bkgd.gray = read_u16(data);
}
else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6)
{
if(chunk.length != 6) return SPNG_ECHUNK_SIZE;
ctx->bkgd.red = read_u16(data);
ctx->bkgd.green = read_u16(data + 2);
ctx->bkgd.blue = read_u16(data + 4);
}
else if(ctx->ihdr.color_type == 3)
{
if(chunk.length != 1) return SPNG_ECHUNK_SIZE;
if(!ctx->file.plte) return SPNG_EBKGD_NO_PLTE;
ctx->bkgd.plte_index = data[0];
if(ctx->bkgd.plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX;
}
ctx->file.bkgd = 1;
ctx->stored.bkgd = 1;
}
else if(!memcmp(chunk.type, type_trns, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.trns) return SPNG_EDUP_TRNS;
if(!chunk.length) return SPNG_ECHUNK_SIZE;
if(ctx->ihdr.color_type == 0)
{
if(chunk.length != 2) return SPNG_ECHUNK_SIZE;
ctx->trns.gray = read_u16(data);
}
else if(ctx->ihdr.color_type == 2)
{
if(chunk.length != 6) return SPNG_ECHUNK_SIZE;
ctx->trns.red = read_u16(data);
ctx->trns.green = read_u16(data + 2);
ctx->trns.blue = read_u16(data + 4);
}
else if(ctx->ihdr.color_type == 3)
{
if(chunk.length > ctx->plte.n_entries) return SPNG_ECHUNK_SIZE;
if(!ctx->file.plte) return SPNG_ETRNS_NO_PLTE;
memcpy(ctx->trns.type3_alpha, data, chunk.length);
ctx->trns.n_type3_entries = chunk.length;
}
if(ctx->ihdr.color_type == 4 || ctx->ihdr.color_type == 6) return SPNG_ETRNS_COLOR_TYPE;
ctx->file.trns = 1;
ctx->stored.trns = 1;
}
else if(!memcmp(chunk.type, type_hist, 4))
{
if(!ctx->file.plte) return SPNG_EHIST_NO_PLTE;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.hist) return SPNG_EDUP_HIST;
if( (chunk.length / 2) != (ctx->plte.n_entries) ) return SPNG_ECHUNK_SIZE;
size_t k;
for(k=0; k < (chunk.length / 2); k++)
{
ctx->hist.frequency[k] = read_u16(data + k*2);
}
ctx->file.hist = 1;
ctx->stored.hist = 1;
}
else if(!memcmp(chunk.type, type_phys, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.phys) return SPNG_EDUP_PHYS;
if(chunk.length != 9) return SPNG_ECHUNK_SIZE;
ctx->phys.ppu_x = read_u32(data);
ctx->phys.ppu_y = read_u32(data + 4);
ctx->phys.unit_specifier = data[8];
if(check_phys(&ctx->phys)) return SPNG_EPHYS;
ctx->file.phys = 1;
ctx->stored.phys = 1;
}
else if(!memcmp(chunk.type, type_time, 4))
{
if(ctx->file.time) return SPNG_EDUP_TIME;
if(chunk.length != 7) return SPNG_ECHUNK_SIZE;
struct spng_time time;
time.year = read_u16(data);
time.month = data[2];
time.day = data[3];
time.hour = data[4];
time.minute = data[5];
time.second = data[6];
if(check_time(&time)) return SPNG_ETIME;
ctx->file.time = 1;
if(!ctx->user.time) ctx->time = time;
ctx->stored.time = 1;
}
else if(!memcmp(chunk.type, type_offs, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.offs) return SPNG_EDUP_OFFS;
if(chunk.length != 9) return SPNG_ECHUNK_SIZE;
ctx->offs.x = read_s32(data);
ctx->offs.y = read_s32(data + 4);
ctx->offs.unit_specifier = data[8];
if(check_offs(&ctx->offs)) return SPNG_EOFFS;
ctx->file.offs = 1;
ctx->stored.offs = 1;
}
else /* Arbitrary-length chunk */
{
if(!memcmp(chunk.type, type_exif, 4))
{
if(ctx->file.exif) return SPNG_EDUP_EXIF;
ctx->file.exif = 1;
if(ctx->user.exif) goto discard;
if(increase_cache_usage(ctx, chunk.length, 1)) return SPNG_ECHUNK_LIMITS;
struct spng_exif exif;
exif.length = chunk.length;
exif.data = spng__malloc(ctx, chunk.length);
if(exif.data == NULL) return SPNG_EMEM;
ret = read_chunk_bytes2(ctx, exif.data, chunk.length);
if(ret)
{
spng__free(ctx, exif.data);
return ret;
}
if(check_exif(&exif))
{
spng__free(ctx, exif.data);
return SPNG_EEXIF;
}
ctx->exif = exif;
ctx->stored.exif = 1;
}
else if(!memcmp(chunk.type, type_iccp, 4))
{/* TODO: add test file with color profile */
if(ctx->file.plte) return SPNG_ECHUNK_POS;
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->file.iccp) return SPNG_EDUP_ICCP;
if(!chunk.length) return SPNG_ECHUNK_SIZE;
ctx->file.iccp = 1;
uint32_t peek_bytes = 81 > chunk.length ? chunk.length : 81;
ret = read_chunk_bytes(ctx, peek_bytes);
if(ret) return ret;
unsigned char *keyword_nul = memchr(ctx->data, '\0', peek_bytes);
if(keyword_nul == NULL) return SPNG_EICCP_NAME;
uint32_t keyword_len = keyword_nul - ctx->data;
if(keyword_len > 79) return SPNG_EICCP_NAME;
memcpy(ctx->iccp.profile_name, ctx->data, keyword_len);
if(check_png_keyword(ctx->iccp.profile_name)) return SPNG_EICCP_NAME;
if(chunk.length < (keyword_len + 2)) return SPNG_ECHUNK_SIZE;
if(ctx->data[keyword_len + 1] != 0) return SPNG_EICCP_COMPRESSION_METHOD;
ret = spng__inflate_stream(ctx, &ctx->iccp.profile, &ctx->iccp.profile_len, 0, ctx->data + keyword_len + 2, peek_bytes - (keyword_len + 2));
if(ret) return ret;
ctx->stored.iccp = 1;
}
else if(!memcmp(chunk.type, type_text, 4) ||
!memcmp(chunk.type, type_ztxt, 4) ||
!memcmp(chunk.type, type_itxt, 4))
{
if(!chunk.length) return SPNG_ECHUNK_SIZE;
ctx->file.text = 1;
if(ctx->user.text) goto discard;
if(increase_cache_usage(ctx, sizeof(struct spng_text2), 1)) return SPNG_ECHUNK_LIMITS;
ctx->n_text++;
if(ctx->n_text < 1) return SPNG_EOVERFLOW;
if(sizeof(struct spng_text2) > SIZE_MAX / ctx->n_text) return SPNG_EOVERFLOW;
void *buf = spng__realloc(ctx, ctx->text_list, ctx->n_text * sizeof(struct spng_text2));
if(buf == NULL) return SPNG_EMEM;
ctx->text_list = buf;
struct spng_text2 *text = &ctx->text_list[ctx->n_text - 1];
memset(text, 0, sizeof(struct spng_text2));
ctx->undo = text_undo;
uint32_t text_offset = 0, language_tag_offset = 0, translated_keyword_offset = 0;
uint32_t peek_bytes = 256; /* enough for 3 80-byte keywords and some text bytes */
uint32_t keyword_len;
if(peek_bytes > chunk.length) peek_bytes = chunk.length;
ret = read_chunk_bytes(ctx, peek_bytes);
if(ret) return ret;
data = ctx->data;
const unsigned char *zlib_stream = NULL;
const unsigned char *peek_end = data + peek_bytes;
const unsigned char *keyword_nul = memchr(data, 0, chunk.length > 80 ? 80 : chunk.length);
if(keyword_nul == NULL) return SPNG_ETEXT_KEYWORD;
keyword_len = keyword_nul - data;
if(!memcmp(chunk.type, type_text, 4))
{
text->type = SPNG_TEXT;
text->text_length = chunk.length - keyword_len - 1;
text_offset = keyword_len;
/* increment past nul if there is a text field */
if(text->text_length) text_offset++;
}
else if(!memcmp(chunk.type, type_ztxt, 4))
{
text->type = SPNG_ZTXT;
if((peek_bytes - keyword_len) <= 2) return SPNG_EZTXT;
if(keyword_nul[1]) return SPNG_EZTXT_COMPRESSION_METHOD;
text->compression_flag = 1;
text_offset = keyword_len + 2;
}
else if(!memcmp(chunk.type, type_itxt, 4))
{
text->type = SPNG_ITXT;
/* at least two 1-byte fields, two >=0 length strings, and one byte of (compressed) text */
if((peek_bytes - keyword_len) < 5) return SPNG_EITXT;
text->compression_flag = keyword_nul[1];
if(text->compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG;
if(keyword_nul[2]) return SPNG_EITXT_COMPRESSION_METHOD;
language_tag_offset = keyword_len + 3;
const unsigned char *term;
term = memchr(data + language_tag_offset, 0, peek_bytes - language_tag_offset);
if(term == NULL) return SPNG_EITXT_LANG_TAG;
if((peek_end - term) < 2) return SPNG_EITXT;
translated_keyword_offset = term - data + 1;
zlib_stream = memchr(data + translated_keyword_offset, 0, peek_bytes - translated_keyword_offset);
if(zlib_stream == NULL) return SPNG_EITXT;
if(zlib_stream == peek_end) return SPNG_EITXT;
text_offset = zlib_stream - data + 1;
text->text_length = chunk.length - text_offset;
}
else return SPNG_EINTERNAL;
if(text->compression_flag)
{
/* cache usage = peek_bytes + decompressed text size + nul */
if(increase_cache_usage(ctx, peek_bytes, 0)) return SPNG_ECHUNK_LIMITS;
text->keyword = spng__calloc(ctx, 1, peek_bytes);
if(text->keyword == NULL) return SPNG_EMEM;
memcpy(text->keyword, data, peek_bytes);
zlib_stream = ctx->data + text_offset;
ret = spng__inflate_stream(ctx, &text->text, &text->text_length, 1, zlib_stream, peek_bytes - text_offset);
if(ret) return ret;
text->text[text->text_length - 1] = '\0';
text->cache_usage = text->text_length + peek_bytes;
}
else
{
if(increase_cache_usage(ctx, chunk.length + 1, 0)) return SPNG_ECHUNK_LIMITS;
text->keyword = spng__malloc(ctx, chunk.length + 1);
if(text->keyword == NULL) return SPNG_EMEM;
memcpy(text->keyword, data, peek_bytes);
if(chunk.length > peek_bytes)
{
ret = read_chunk_bytes2(ctx, text->keyword + peek_bytes, chunk.length - peek_bytes);
if(ret) return ret;
}
text->text = text->keyword + text_offset;
text->text_length = chunk.length - text_offset;
text->text[text->text_length] = '\0';
text->cache_usage = chunk.length + 1;
}
if(check_png_keyword(text->keyword)) return SPNG_ETEXT_KEYWORD;
text->text_length = strlen(text->text);
if(text->type != SPNG_ITXT)
{
language_tag_offset = keyword_len;
translated_keyword_offset = keyword_len;
if(ctx->strict && check_png_text(text->text, text->text_length))
{
if(text->type == SPNG_ZTXT) return SPNG_EZTXT;
else return SPNG_ETEXT;
}
}
text->language_tag = text->keyword + language_tag_offset;
text->translated_keyword = text->keyword + translated_keyword_offset;
ctx->stored.text = 1;
}
else if(!memcmp(chunk.type, type_splt, 4))
{
if(ctx->state == SPNG_STATE_AFTER_IDAT) return SPNG_ECHUNK_POS;
if(ctx->user.splt) goto discard; /* XXX: could check profile names for uniqueness */
if(!chunk.length) return SPNG_ECHUNK_SIZE;
ctx->file.splt = 1;
/* chunk.length + sizeof(struct spng_splt) + splt->n_entries * sizeof(struct spng_splt_entry) */
if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_splt), 1)) return SPNG_ECHUNK_LIMITS;
ctx->n_splt++;
if(ctx->n_splt < 1) return SPNG_EOVERFLOW;
if(sizeof(struct spng_splt) > SIZE_MAX / ctx->n_splt) return SPNG_EOVERFLOW;
void *buf = spng__realloc(ctx, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt));
if(buf == NULL) return SPNG_EMEM;
ctx->splt_list = buf;
struct spng_splt *splt = &ctx->splt_list[ctx->n_splt - 1];
memset(splt, 0, sizeof(struct spng_splt));
ctx->undo = splt_undo;
void *t = spng__malloc(ctx, chunk.length);
if(t == NULL) return SPNG_EMEM;
splt->entries = t; /* simplifies error handling */
data = t;
ret = read_chunk_bytes2(ctx, t, chunk.length);
if(ret) return ret;
uint32_t keyword_len = chunk.length < 80 ? chunk.length : 80;
const unsigned char *keyword_nul = memchr(data, 0, keyword_len);
if(keyword_nul == NULL) return SPNG_ESPLT_NAME;
keyword_len = keyword_nul - data;
memcpy(splt->name, data, keyword_len);
if(check_png_keyword(splt->name)) return SPNG_ESPLT_NAME;
uint32_t j;
for(j=0; j < (ctx->n_splt - 1); j++)
{
if(!strcmp(ctx->splt_list[j].name, splt->name)) return SPNG_ESPLT_DUP_NAME;
}
if( (chunk.length - keyword_len) <= 2) return SPNG_ECHUNK_SIZE;
splt->sample_depth = data[keyword_len + 1];
uint32_t entries_len = chunk.length - keyword_len - 2;
if(!entries_len) return SPNG_ECHUNK_SIZE;
if(splt->sample_depth == 16)
{
if(entries_len % 10 != 0) return SPNG_ECHUNK_SIZE;
splt->n_entries = entries_len / 10;
}
else if(splt->sample_depth == 8)
{
if(entries_len % 6 != 0) return SPNG_ECHUNK_SIZE;
splt->n_entries = entries_len / 6;
}
else return SPNG_ESPLT_DEPTH;
if(!splt->n_entries) return SPNG_ECHUNK_SIZE;
size_t list_size = splt->n_entries;
if(list_size > SIZE_MAX / sizeof(struct spng_splt_entry)) return SPNG_EOVERFLOW;
list_size *= sizeof(struct spng_splt_entry);
if(increase_cache_usage(ctx, list_size, 0)) return SPNG_ECHUNK_LIMITS;
splt->entries = spng__malloc(ctx, list_size);
if(splt->entries == NULL)
{
spng__free(ctx, t);
return SPNG_EMEM;
}
data = (unsigned char*)t + keyword_len + 2;
uint32_t k;
if(splt->sample_depth == 16)
{
for(k=0; k < splt->n_entries; k++)
{
splt->entries[k].red = read_u16(data + k * 10);
splt->entries[k].green = read_u16(data + k * 10 + 2);
splt->entries[k].blue = read_u16(data + k * 10 + 4);
splt->entries[k].alpha = read_u16(data + k * 10 + 6);
splt->entries[k].frequency = read_u16(data + k * 10 + 8);
}
}
else if(splt->sample_depth == 8)
{
for(k=0; k < splt->n_entries; k++)
{
splt->entries[k].red = data[k * 6];
splt->entries[k].green = data[k * 6 + 1];
splt->entries[k].blue = data[k * 6 + 2];
splt->entries[k].alpha = data[k * 6 + 3];
splt->entries[k].frequency = read_u16(data + k * 6 + 4);
}
}
spng__free(ctx, t);
decrease_cache_usage(ctx, chunk.length);
ctx->stored.splt = 1;
}
else /* Unknown chunk */
{
ctx->file.unknown = 1;
if(!ctx->keep_unknown) goto discard;
if(ctx->user.unknown) goto discard;
if(increase_cache_usage(ctx, chunk.length + sizeof(struct spng_unknown_chunk), 1)) return SPNG_ECHUNK_LIMITS;
ctx->n_chunks++;
if(ctx->n_chunks < 1) return SPNG_EOVERFLOW;
if(sizeof(struct spng_unknown_chunk) > SIZE_MAX / ctx->n_chunks) return SPNG_EOVERFLOW;
void *buf = spng__realloc(ctx, ctx->chunk_list, ctx->n_chunks * sizeof(struct spng_unknown_chunk));
if(buf == NULL) return SPNG_EMEM;
ctx->chunk_list = buf;
struct spng_unknown_chunk *chunkp = &ctx->chunk_list[ctx->n_chunks - 1];
memset(chunkp, 0, sizeof(struct spng_unknown_chunk));
ctx->undo = chunk_undo;
memcpy(chunkp->type, chunk.type, 4);
if(ctx->state < SPNG_STATE_FIRST_IDAT)
{
if(ctx->file.plte) chunkp->location = SPNG_AFTER_PLTE;
else chunkp->location = SPNG_AFTER_IHDR;
}
else if(ctx->state >= SPNG_STATE_AFTER_IDAT) chunkp->location = SPNG_AFTER_IDAT;
if(chunk.length > 0)
{
void *t = spng__malloc(ctx, chunk.length);
if(t == NULL) return SPNG_EMEM;
ret = read_chunk_bytes2(ctx, t, chunk.length);
if(ret)
{
spng__free(ctx, t);
return ret;
}
chunkp->length = chunk.length;
chunkp->data = t;
}
ctx->stored.unknown = 1;
}
discard:
ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);
if(ret) return ret;
}
}
return ret;
}
/* Read chunks before or after the IDAT chunks depending on state */
static int read_chunks(spng_ctx *ctx, int only_ihdr)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!ctx->state) return SPNG_EBADSTATE;
if(ctx->data == NULL)
{
if(ctx->encode_only) return 0;
else return SPNG_EINTERNAL;
}
int ret = 0;
if(ctx->state == SPNG_STATE_INPUT)
{
ret = read_ihdr(ctx);
if(ret) return decode_err(ctx, ret);
ctx->state = SPNG_STATE_IHDR;
}
if(only_ihdr) return 0;
if(ctx->state == SPNG_STATE_EOI)
{
ctx->state = SPNG_STATE_AFTER_IDAT;
ctx->prev_was_idat = 1;
}
while(ctx->state < SPNG_STATE_FIRST_IDAT || ctx->state == SPNG_STATE_AFTER_IDAT)
{
ret = read_non_idat_chunks(ctx);
if(!ret)
{
if(ctx->state < SPNG_STATE_FIRST_IDAT) ctx->state = SPNG_STATE_FIRST_IDAT;
else if(ctx->state == SPNG_STATE_AFTER_IDAT) ctx->state = SPNG_STATE_IEND;
}
else
{
switch(ret)
{
case SPNG_ECHUNK_POS:
case SPNG_ECHUNK_SIZE: /* size != expected size, SPNG_ECHUNK_STDLEN = invalid size */
case SPNG_EDUP_PLTE:
case SPNG_EDUP_CHRM:
case SPNG_EDUP_GAMA:
case SPNG_EDUP_ICCP:
case SPNG_EDUP_SBIT:
case SPNG_EDUP_SRGB:
case SPNG_EDUP_BKGD:
case SPNG_EDUP_HIST:
case SPNG_EDUP_TRNS:
case SPNG_EDUP_PHYS:
case SPNG_EDUP_TIME:
case SPNG_EDUP_OFFS:
case SPNG_EDUP_EXIF:
case SPNG_ECHRM:
case SPNG_ETRNS_COLOR_TYPE:
case SPNG_ETRNS_NO_PLTE:
case SPNG_EGAMA:
case SPNG_EICCP_NAME:
case SPNG_EICCP_COMPRESSION_METHOD:
case SPNG_ESBIT:
case SPNG_ESRGB:
case SPNG_ETEXT:
case SPNG_ETEXT_KEYWORD:
case SPNG_EZTXT:
case SPNG_EZTXT_COMPRESSION_METHOD:
case SPNG_EITXT:
case SPNG_EITXT_COMPRESSION_FLAG:
case SPNG_EITXT_COMPRESSION_METHOD:
case SPNG_EITXT_LANG_TAG:
case SPNG_EITXT_TRANSLATED_KEY:
case SPNG_EBKGD_NO_PLTE:
case SPNG_EBKGD_PLTE_IDX:
case SPNG_EHIST_NO_PLTE:
case SPNG_EPHYS:
case SPNG_ESPLT_NAME:
case SPNG_ESPLT_DUP_NAME:
case SPNG_ESPLT_DEPTH:
case SPNG_ETIME:
case SPNG_EOFFS:
case SPNG_EEXIF:
case SPNG_EZLIB:
{
if(!ctx->strict && !is_critical_chunk(&ctx->current_chunk))
{
ret = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);
if(ret) return decode_err(ctx, ret);
if(ctx->undo) ctx->undo(ctx);
ctx->stored = ctx->prev_stored;
ctx->discard = 0;
ctx->undo = NULL;
continue;
}
else return decode_err(ctx, ret);
break;
}
default: return decode_err(ctx, ret);
}
}
}
return ret;
}
static int read_scanline(spng_ctx *ctx)
{
int ret, pass = ctx->row_info.pass;
struct spng_row_info *ri = &ctx->row_info;
const struct spng_subimage *sub = ctx->subimage;
size_t scanline_width = sub[pass].scanline_width;
uint32_t scanline_idx = ri->scanline_idx;
uint8_t next_filter = 0;
if(scanline_idx == (sub[pass].height - 1) && ri->pass == ctx->last_pass)
{
ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width - 1);
}
else
{
ret = read_scanline_bytes(ctx, ctx->scanline, scanline_width);
if(ret) return ret;
next_filter = ctx->scanline[scanline_width - 1];
if(next_filter > 4) ret = SPNG_EFILTER;
}
if(ret) return ret;
if(!scanline_idx && ri->filter > 1)
{
/* prev_scanline is all zeros for the first scanline */
memset(ctx->prev_scanline, 0, scanline_width);
}
if(ctx->ihdr.bit_depth == 16 && ctx->fmt != SPNG_FMT_RAW) u16_row_to_host(ctx->scanline, scanline_width - 1);
ret = defilter_scanline(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, ri->filter);
if(ret) return ret;
ri->filter = next_filter;
return 0;
}
static int update_row_info(spng_ctx *ctx)
{
int interlacing = ctx->ihdr.interlace_method;
struct spng_row_info *ri = &ctx->row_info;
const struct spng_subimage *sub = ctx->subimage;
if(ri->scanline_idx == (sub[ri->pass].height - 1)) /* Last scanline */
{
if(ri->pass == ctx->last_pass)
{
ctx->state = SPNG_STATE_EOI;
return SPNG_EOI;
}
ri->scanline_idx = 0;
ri->pass++;
/* Skip empty passes */
while( (!sub[ri->pass].width || !sub[ri->pass].height) && (ri->pass < ctx->last_pass)) ri->pass++;
}
else
{
ri->row_num++;
ri->scanline_idx++;
}
if(interlacing) ri->row_num = adam7_y_start[ri->pass] + ri->scanline_idx * adam7_y_delta[ri->pass];
return 0;
}
int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len)
{
if(ctx == NULL || out == NULL) return 1;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
struct decode_flags f = ctx->decode_flags;
struct spng_row_info *ri = &ctx->row_info;
const struct spng_subimage *sub = ctx->subimage;
const struct spng_ihdr *ihdr = &ctx->ihdr;
const uint16_t *gamma_lut = ctx->gamma_lut;
unsigned char *trns_px = ctx->trns_px;
const struct spng_sbit *sb = &ctx->decode_sb;
const struct spng_plte_entry *plte = ctx->decode_plte.rgba;
struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->scanline);
const unsigned char *scanline;
const int pass = ri->pass;
const int fmt = ctx->fmt;
const size_t scanline_width = sub[pass].scanline_width;
const uint32_t width = sub[pass].width;
uint32_t k;
uint8_t r_8, g_8, b_8, a_8, gray_8;
uint16_t r_16, g_16, b_16, a_16, gray_16;
r_8=0; g_8=0; b_8=0; a_8=0; gray_8=0;
r_16=0; g_16=0; b_16=0; a_16=0; gray_16=0;
size_t pixel_size = 4; /* SPNG_FMT_RGBA8 */
size_t pixel_offset = 0;
unsigned char *pixel;
unsigned processing_depth = ihdr->bit_depth;
if(f.indexed) processing_depth = 8;
if(fmt == SPNG_FMT_RGBA16) pixel_size = 8;
else if(fmt == SPNG_FMT_RGB8) pixel_size = 3;
if(len < sub[pass].out_width) return SPNG_EBUFSIZ;
int ret = read_scanline(ctx);
if(ret) return decode_err(ctx, ret);
scanline = ctx->scanline;
for(k=0; k < width; k++)
{
pixel = (unsigned char*)out + pixel_offset;
pixel_offset += pixel_size;
if(f.same_layout)
{
if(f.zerocopy) break;
memcpy(out, scanline, scanline_width - 1);
break;
}
if(f.unpack)
{
unpack_scanline(out, scanline, width, ihdr->bit_depth, fmt);
break;
}
if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
if(ihdr->bit_depth == 16)
{
memcpy(&r_16, scanline + (k * 6), 2);
memcpy(&g_16, scanline + (k * 6) + 2, 2);
memcpy(&b_16, scanline + (k * 6) + 4, 2);
a_16 = 65535;
}
else /* == 8 */
{
if(fmt == SPNG_FMT_RGBA8)
{
rgb8_row_to_rgba8(scanline, out, width);
break;
}
r_8 = scanline[k * 3];
g_8 = scanline[k * 3 + 1];
b_8 = scanline[k * 3 + 2];
a_8 = 255;
}
}
else if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED)
{
uint8_t entry = 0;
if(ihdr->bit_depth == 8)
{
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))
{
expand_row(out, scanline, &ctx->decode_plte, width, fmt);
break;
}
entry = scanline[k];
}
else /* < 8 */
{
entry = get_sample(&iter);
}
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))
{
pixel[0] = plte[entry].red;
pixel[1] = plte[entry].green;
pixel[2] = plte[entry].blue;
if(fmt == SPNG_FMT_RGBA8) pixel[3] = plte[entry].alpha;
continue;
}
else /* RGBA16 */
{
r_16 = plte[entry].red;
g_16 = plte[entry].green;
b_16 = plte[entry].blue;
a_16 = plte[entry].alpha;
r_16 = (r_16 << 8) | r_16;
g_16 = (g_16 << 8) | g_16;
b_16 = (b_16 << 8) | b_16;
a_16 = (a_16 << 8) | a_16;
memcpy(pixel, &r_16, 2);
memcpy(pixel + 2, &g_16, 2);
memcpy(pixel + 4, &b_16, 2);
memcpy(pixel + 6, &a_16, 2);
continue;
}
}
else if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
{
if(ihdr->bit_depth == 16)
{
memcpy(&r_16, scanline + (k * 8), 2);
memcpy(&g_16, scanline + (k * 8) + 2, 2);
memcpy(&b_16, scanline + (k * 8) + 4, 2);
memcpy(&a_16, scanline + (k * 8) + 6, 2);
}
else /* == 8 */
{
r_8 = scanline[k * 4];
g_8 = scanline[k * 4 + 1];
b_8 = scanline[k * 4 + 2];
a_8 = scanline[k * 4 + 3];
}
}
else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE)
{
if(ihdr->bit_depth == 16)
{
memcpy(&gray_16, scanline + k * 2, 2);
if(f.apply_trns && ctx->trns.gray == gray_16) a_16 = 0;
else a_16 = 65535;
r_16 = gray_16;
g_16 = gray_16;
b_16 = gray_16;
}
else /* <= 8 */
{
gray_8 = get_sample(&iter);
if(f.apply_trns && ctx->trns.gray == gray_8) a_8 = 0;
else a_8 = 255;
r_8 = gray_8; g_8 = gray_8; b_8 = gray_8;
}
}
else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
{
if(ihdr->bit_depth == 16)
{
memcpy(&gray_16, scanline + (k * 4), 2);
memcpy(&a_16, scanline + (k * 4) + 2, 2);
r_16 = gray_16;
g_16 = gray_16;
b_16 = gray_16;
}
else /* == 8 */
{
gray_8 = scanline[k * 2];
a_8 = scanline[k * 2 + 1];
r_8 = gray_8;
g_8 = gray_8;
b_8 = gray_8;
}
}
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))
{
if(ihdr->bit_depth == 16)
{
r_8 = r_16 >> 8;
g_8 = g_16 >> 8;
b_8 = b_16 >> 8;
a_8 = a_16 >> 8;
}
pixel[0] = r_8;
pixel[1] = g_8;
pixel[2] = b_8;
if(fmt == SPNG_FMT_RGBA8) pixel[3] = a_8;
}
else if(fmt == SPNG_FMT_RGBA16)
{
if(ihdr->bit_depth != 16)
{
r_16 = r_8;
g_16 = g_8;
b_16 = b_8;
a_16 = a_8;
}
memcpy(pixel, &r_16, 2);
memcpy(pixel + 2, &g_16, 2);
memcpy(pixel + 4, &b_16, 2);
memcpy(pixel + 6, &a_16, 2);
}
}/* for(k=0; k < width; k++) */
if(f.apply_trns) trns_row(out, scanline, trns_px, ctx->bytes_per_pixel, &ctx->ihdr, width, fmt);
if(f.do_scaling) scale_row(out, width, fmt, processing_depth, sb);
if(f.apply_gamma) gamma_correct_row(out, width, fmt, gamma_lut);
/* The previous scanline is always defiltered */
void *t = ctx->prev_scanline;
ctx->prev_scanline = ctx->scanline;
ctx->scanline = t;
ret = update_row_info(ctx);
if(ret == SPNG_EOI)
{
if(ctx->cur_chunk_bytes_left) /* zlib stream ended before an IDAT chunk boundary */
{/* Discard the rest of the chunk */
int error = discard_chunk_bytes(ctx, ctx->cur_chunk_bytes_left);
if(error) return decode_err(ctx, error);
}
ctx->last_idat = ctx->current_chunk;
}
return ret;
}
int spng_decode_row(spng_ctx *ctx, void *out, size_t len)
{
if(ctx == NULL || out == NULL) return 1;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
if(len < ctx->image_width) return SPNG_EBUFSIZ;
const struct spng_ihdr *ihdr = &ctx->ihdr;
int ret, pass = ctx->row_info.pass;
unsigned char *outptr = out;
if(!ihdr->interlace_method || pass == 6) return spng_decode_scanline(ctx, out, len);
ret = spng_decode_scanline(ctx, ctx->row, ctx->image_width);
if(ret && ret != SPNG_EOI) return ret;
uint32_t k;
unsigned pixel_size = 4; /* RGBA8 */
if(ctx->fmt == SPNG_FMT_RGBA16) pixel_size = 8;
else if(ctx->fmt == SPNG_FMT_RGB8) pixel_size = 3;
else if(ctx->fmt == SPNG_FMT_G8) pixel_size = 1;
else if(ctx->fmt == SPNG_FMT_GA8) pixel_size = 2;
else if(ctx->fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW))
{
if(ihdr->bit_depth < 8)
{
struct spng__iter iter = spng__iter_init(ihdr->bit_depth, ctx->row);
const uint8_t samples_per_byte = 8 / ihdr->bit_depth;
uint8_t sample;
for(k=0; k < ctx->subimage[pass].width; k++)
{
sample = get_sample(&iter);
size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass];
sample = sample << (iter.initial_shift - ioffset * ihdr->bit_depth % 8);
ioffset /= samples_per_byte;
outptr[ioffset] |= sample;
}
return 0;
}
else pixel_size = ctx->bytes_per_pixel;
}
for(k=0; k < ctx->subimage[pass].width; k++)
{
size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size;
memcpy(outptr + ioffset, ctx->row + k * pixel_size, pixel_size);
}
return 0;
}
int spng_decode_chunks(spng_ctx *ctx)
{
if(ctx == NULL) return 1;
if(ctx->encode_only) return SPNG_ECTXTYPE;
if(ctx->state < SPNG_STATE_INPUT) return SPNG_ENOSRC;
if(ctx->state == SPNG_STATE_IEND) return 0;
return read_chunks(ctx, 0);
}
int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags)
{
if(ctx == NULL) return 1;
if(ctx->encode_only) return SPNG_ECTXTYPE;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
const struct spng_ihdr *ihdr = &ctx->ihdr;
int ret = read_chunks(ctx, 0);
if(ret) return decode_err(ctx, ret);
ret = check_decode_fmt(ihdr, fmt);
if(ret) return ret;
ret = calculate_image_width(ihdr, fmt, &ctx->image_width);
if(ret) return decode_err(ctx, ret);
if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */
else ctx->image_size = ctx->image_width * ihdr->height;
if( !(flags & SPNG_DECODE_PROGRESSIVE) )
{
if(out == NULL) return 1;
if(!ctx->image_size) return SPNG_EOVERFLOW;
if(len < ctx->image_size) return SPNG_EBUFSIZ;
}
uint32_t bytes_read = 0;
ret = read_idat_bytes(ctx, &bytes_read);
if(ret) return decode_err(ctx, ret);
if(bytes_read > 1)
{
int valid = read_u16(ctx->data) % 31 ? 0 : 1;
unsigned flg = ctx->data[1];
unsigned flevel = flg >> 6;
int compression_level = Z_DEFAULT_COMPRESSION;
if(flevel == 0) compression_level = 0; /* fastest */
else if(flevel == 1) compression_level = 1; /* fast */
else if(flevel == 2) compression_level = 6; /* default */
else if(flevel == 3) compression_level = 9; /* slowest, max compression */
if(valid) ctx->image_options.compression_level = compression_level;
}
ret = spng__inflate_init(ctx, ctx->image_options.window_bits);
if(ret) return decode_err(ctx, ret);
ctx->zstream.avail_in = bytes_read;
ctx->zstream.next_in = ctx->data;
size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width;
scanline_buf_size += 32;
if(scanline_buf_size < 32) return SPNG_EOVERFLOW;
ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size);
ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size);
ctx->scanline = ctx->scanline_buf;
ctx->prev_scanline = ctx->prev_scanline_buf;
struct decode_flags f = {0};
ctx->fmt = fmt;
if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED) f.indexed = 1;
unsigned processing_depth = ihdr->bit_depth;
if(f.indexed) processing_depth = 8;
if(ihdr->interlace_method)
{
f.interlaced = 1;
ctx->row_buf = spng__malloc(ctx, ctx->image_width);
ctx->row = ctx->row_buf;
if(ctx->row == NULL) return decode_err(ctx, SPNG_EMEM);
}
if(ctx->scanline == NULL || ctx->prev_scanline == NULL)
{
return decode_err(ctx, SPNG_EMEM);
}
f.do_scaling = 1;
if(f.indexed) f.do_scaling = 0;
unsigned depth_target = 8; /* FMT_RGBA8, G8 */
if(fmt == SPNG_FMT_RGBA16) depth_target = 16;
if(flags & SPNG_DECODE_TRNS && ctx->stored.trns) f.apply_trns = 1;
else flags &= ~SPNG_DECODE_TRNS;
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA ||
ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA) flags &= ~SPNG_DECODE_TRNS;
if(flags & SPNG_DECODE_GAMMA && ctx->stored.gama) f.apply_gamma = 1;
else flags &= ~SPNG_DECODE_GAMMA;
if(flags & SPNG_DECODE_USE_SBIT && ctx->stored.sbit) f.use_sbit = 1;
else flags &= ~SPNG_DECODE_USE_SBIT;
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16))
{
if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA &&
ihdr->bit_depth == depth_target) f.same_layout = 1;
}
else if(fmt == SPNG_FMT_RGB8)
{
if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR &&
ihdr->bit_depth == depth_target) f.same_layout = 1;
f.apply_trns = 0; /* not applicable */
}
else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW))
{
f.same_layout = 1;
f.do_scaling = 0;
f.apply_gamma = 0; /* for now */
f.apply_trns = 0;
}
else if(fmt == SPNG_FMT_G8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8)
{
if(ihdr->bit_depth == depth_target) f.same_layout = 1;
else if(ihdr->bit_depth < 8) f.unpack = 1;
f.apply_trns = 0;
}
else if(fmt == SPNG_FMT_GA8 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth <= 8)
{
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA &&
ihdr->bit_depth == depth_target) f.same_layout = 1;
else if(ihdr->bit_depth <= 8) f.unpack = 1;
}
else if(fmt == SPNG_FMT_GA16 && ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE && ihdr->bit_depth == 16)
{
if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA &&
ihdr->bit_depth == depth_target) f.same_layout = 1;
else if(ihdr->bit_depth == 16) f.unpack = 1;
}
/*if(f.same_layout && !flags && !f.interlaced) f.zerocopy = 1;*/
uint16_t *gamma_lut = NULL;
if(f.apply_gamma)
{
float file_gamma = (float)ctx->gama / 100000.0f;
float max;
unsigned lut_entries;
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))
{
lut_entries = 256;
max = 255.0f;
gamma_lut = ctx->gamma_lut8;
ctx->gamma_lut = ctx->gamma_lut8;
}
else /* SPNG_FMT_RGBA16 */
{
lut_entries = 65536;
max = 65535.0f;
ctx->gamma_lut16 = spng__malloc(ctx, lut_entries * sizeof(uint16_t));
if(ctx->gamma_lut16 == NULL) return decode_err(ctx, SPNG_EMEM);
gamma_lut = ctx->gamma_lut16;
ctx->gamma_lut = ctx->gamma_lut16;
}
float screen_gamma = 2.2f;
float exponent = file_gamma * screen_gamma;
if(FP_ZERO == fpclassify(exponent)) return decode_err(ctx, SPNG_EGAMA);
exponent = 1.0f / exponent;
unsigned i;
for(i=0; i < lut_entries; i++)
{
float c = pow((float)i / max, exponent) * max;
if(c > max) c = max;
gamma_lut[i] = (uint16_t)c;
}
}
struct spng_sbit *sb = &ctx->decode_sb;
sb->red_bits = processing_depth;
sb->green_bits = processing_depth;
sb->blue_bits = processing_depth;
sb->alpha_bits = processing_depth;
sb->grayscale_bits = processing_depth;
if(f.use_sbit)
{
if(ihdr->color_type == 0)
{
sb->grayscale_bits = ctx->sbit.grayscale_bits;
sb->alpha_bits = ihdr->bit_depth;
}
else if(ihdr->color_type == 2 || ihdr->color_type == 3)
{
sb->red_bits = ctx->sbit.red_bits;
sb->green_bits = ctx->sbit.green_bits;
sb->blue_bits = ctx->sbit.blue_bits;
sb->alpha_bits = ihdr->bit_depth;
}
else if(ihdr->color_type == 4)
{
sb->grayscale_bits = ctx->sbit.grayscale_bits;
sb->alpha_bits = ctx->sbit.alpha_bits;
}
else /* == 6 */
{
sb->red_bits = ctx->sbit.red_bits;
sb->green_bits = ctx->sbit.green_bits;
sb->blue_bits = ctx->sbit.blue_bits;
sb->alpha_bits = ctx->sbit.alpha_bits;
}
}
if(ihdr->bit_depth == 16 && fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGB8))
{/* samples are scaled down by 8 bits in the decode loop */
sb->red_bits -= 8;
sb->green_bits -= 8;
sb->blue_bits -= 8;
sb->alpha_bits -= 8;
sb->grayscale_bits -= 8;
processing_depth = 8;
}
/* Prevent infinite loops in sample_to_target() */
if(!depth_target || depth_target > 16 ||
!processing_depth || processing_depth > 16 ||
!sb->grayscale_bits || sb->grayscale_bits > processing_depth ||
!sb->alpha_bits || sb->alpha_bits > processing_depth ||
!sb->red_bits || sb->red_bits > processing_depth ||
!sb->green_bits || sb->green_bits > processing_depth ||
!sb->blue_bits || sb->blue_bits > processing_depth)
{
return decode_err(ctx, SPNG_ESBIT);
}
if(sb->red_bits == sb->green_bits &&
sb->green_bits == sb->blue_bits &&
sb->blue_bits == sb->alpha_bits &&
sb->alpha_bits == processing_depth &&
processing_depth == depth_target) f.do_scaling = 0;
struct spng_plte_entry *plte = ctx->decode_plte.rgba;
/* Pre-process palette entries */
if(f.indexed)
{
uint8_t red, green, blue, alpha;
uint32_t i;
for(i=0; i < 256; i++)
{
if(f.apply_trns && i < ctx->trns.n_type3_entries)
ctx->plte.entries[i].alpha = ctx->trns.type3_alpha[i];
else
ctx->plte.entries[i].alpha = 255;
red = sample_to_target(ctx->plte.entries[i].red, 8, sb->red_bits, 8);
green = sample_to_target(ctx->plte.entries[i].green, 8, sb->green_bits, 8);
blue = sample_to_target(ctx->plte.entries[i].blue, 8, sb->blue_bits, 8);
alpha = sample_to_target(ctx->plte.entries[i].alpha, 8, sb->alpha_bits, 8);
#if defined(SPNG_ARM)
if(fmt == SPNG_FMT_RGB8 && ihdr->bit_depth == 8)
{/* Working with 3 bytes at a time is more of an ARM thing */
ctx->decode_plte.rgb[i * 3 + 0] = red;
ctx->decode_plte.rgb[i * 3 + 1] = green;
ctx->decode_plte.rgb[i * 3 + 2] = blue;
continue;
}
#endif
plte[i].red = red;
plte[i].green = green;
plte[i].blue = blue;
plte[i].alpha = alpha;
}
f.apply_trns = 0;
}
unsigned char *trns_px = ctx->trns_px;
if(f.apply_trns)
{
uint16_t mask = ~0;
if(ctx->ihdr.bit_depth < 16) mask = (1 << ctx->ihdr.bit_depth) - 1;
if(fmt & (SPNG_FMT_RGBA8 | SPNG_FMT_RGBA16))
{
if(ihdr->color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
if(ihdr->bit_depth == 16)
{
memcpy(trns_px, &ctx->trns.red, 2);
memcpy(trns_px + 2, &ctx->trns.green, 2);
memcpy(trns_px + 4, &ctx->trns.blue, 2);
}
else
{
trns_px[0] = ctx->trns.red & mask;
trns_px[1] = ctx->trns.green & mask;
trns_px[2] = ctx->trns.blue & mask;
}
}
}
else if(ihdr->color_type == SPNG_COLOR_TYPE_GRAYSCALE) // fmt == SPNG_FMT_GA8 &&
{
if(ihdr->bit_depth == 16)
{
memcpy(trns_px, &ctx->trns.gray, 2);
}
else
{
trns_px[0] = ctx->trns.gray & mask;
}
}
}
ctx->decode_flags = f;
ctx->state = SPNG_STATE_DECODE_INIT;
struct spng_row_info *ri = &ctx->row_info;
struct spng_subimage *sub = ctx->subimage;
while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++;
if(f.interlaced) ri->row_num = adam7_y_start[ri->pass];
unsigned pixel_size = 4; /* SPNG_FMT_RGBA8 */
if(fmt == SPNG_FMT_RGBA16) pixel_size = 8;
else if(fmt == SPNG_FMT_RGB8) pixel_size = 3;
else if(fmt == SPNG_FMT_G8) pixel_size = 1;
else if(fmt == SPNG_FMT_GA8) pixel_size = 2;
int i;
for(i=ri->pass; i <= ctx->last_pass; i++)
{
if(!sub[i].scanline_width) continue;
if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) sub[i].out_width = sub[i].scanline_width - 1;
else sub[i].out_width = (size_t)sub[i].width * pixel_size;
if(sub[i].out_width > UINT32_MAX) return decode_err(ctx, SPNG_EOVERFLOW);
}
/* Read the first filter byte, offsetting all reads by 1 byte.
The scanlines will be aligned with the start of the array with
the next scanline's filter byte at the end,
the last scanline will end up being 1 byte "shorter". */
ret = read_scanline_bytes(ctx, &ri->filter, 1);
if(ret) return decode_err(ctx, ret);
if(ri->filter > 4) return decode_err(ctx, SPNG_EFILTER);
if(flags & SPNG_DECODE_PROGRESSIVE)
{
return 0;
}
do
{
size_t ioffset = ri->row_num * ctx->image_width;
ret = spng_decode_row(ctx, (unsigned char*)out + ioffset, ctx->image_width);
}while(!ret);
if(ret != SPNG_EOI) return decode_err(ctx, ret);
return 0;
}
int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info)
{
if(ctx == NULL || row_info == NULL || ctx->state < SPNG_STATE_DECODE_INIT) return 1;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
*row_info = ctx->row_info;
return 0;
}
static int write_chunks_before_idat(spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
if(!ctx->encode_only) return SPNG_EINTERNAL;
if(!ctx->stored.ihdr) return SPNG_EINTERNAL;
int ret;
uint32_t i;
size_t length;
const struct spng_ihdr *ihdr = &ctx->ihdr;
unsigned char *data = ctx->decode_plte.raw;
ret = write_data(ctx, spng_signature, 8);
if(ret) return ret;
write_u32(data, ihdr->width);
write_u32(data + 4, ihdr->height);
data[8] = ihdr->bit_depth;
data[9] = ihdr->color_type;
data[10] = ihdr->compression_method;
data[11] = ihdr->filter_method;
data[12] = ihdr->interlace_method;
ret = write_chunk(ctx, type_ihdr, data, 13);
if(ret) return ret;
if(ctx->stored.chrm)
{
write_u32(data, ctx->chrm_int.white_point_x);
write_u32(data + 4, ctx->chrm_int.white_point_y);
write_u32(data + 8, ctx->chrm_int.red_x);
write_u32(data + 12, ctx->chrm_int.red_y);
write_u32(data + 16, ctx->chrm_int.green_x);
write_u32(data + 20, ctx->chrm_int.green_y);
write_u32(data + 24, ctx->chrm_int.blue_x);
write_u32(data + 28, ctx->chrm_int.blue_y);
ret = write_chunk(ctx, type_chrm, data, 32);
if(ret) return ret;
}
if(ctx->stored.gama)
{
write_u32(data, ctx->gama);
ret = write_chunk(ctx, type_gama, data, 4);
if(ret) return ret;
}
if(ctx->stored.iccp)
{
uLongf dest_len = compressBound((uLong)ctx->iccp.profile_len);
Bytef *buf = spng__malloc(ctx, dest_len);
if(buf == NULL) return SPNG_EMEM;
ret = compress2(buf, &dest_len, (void*)ctx->iccp.profile, (uLong)ctx->iccp.profile_len, Z_DEFAULT_COMPRESSION);
if(ret != Z_OK)
{
spng__free(ctx, buf);
return SPNG_EZLIB;
}
size_t name_len = strlen(ctx->iccp.profile_name);
length = name_len + 2;
length += dest_len;
if(dest_len > length) return SPNG_EOVERFLOW;
unsigned char *cdata = NULL;
ret = write_header(ctx, type_iccp, length, &cdata);
if(ret)
{
spng__free(ctx, buf);
return ret;
}
memcpy(cdata, ctx->iccp.profile_name, name_len + 1);
cdata[name_len + 1] = 0; /* compression method */
memcpy(cdata + name_len + 2, buf, dest_len);
spng__free(ctx, buf);
ret = finish_chunk(ctx);
if(ret) return ret;
}
if(ctx->stored.sbit)
{
switch(ctx->ihdr.color_type)
{
case SPNG_COLOR_TYPE_GRAYSCALE:
{
length = 1;
data[0] = ctx->sbit.grayscale_bits;
break;
}
case SPNG_COLOR_TYPE_TRUECOLOR:
case SPNG_COLOR_TYPE_INDEXED:
{
length = 3;
data[0] = ctx->sbit.red_bits;
data[1] = ctx->sbit.green_bits;
data[2] = ctx->sbit.blue_bits;
break;
}
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
{
length = 2;
data[0] = ctx->sbit.grayscale_bits;
data[1] = ctx->sbit.alpha_bits;
break;
}
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
{
length = 4;
data[0] = ctx->sbit.red_bits;
data[1] = ctx->sbit.green_bits;
data[2] = ctx->sbit.blue_bits;
data[3] = ctx->sbit.alpha_bits;
break;
}
default: return SPNG_EINTERNAL;
}
ret = write_chunk(ctx, type_sbit, data, length);
if(ret) return ret;
}
if(ctx->stored.srgb)
{
ret = write_chunk(ctx, type_srgb, &ctx->srgb_rendering_intent, 1);
if(ret) return ret;
}
ret = write_unknown_chunks(ctx, SPNG_AFTER_IHDR);
if(ret) return ret;
if(ctx->stored.plte)
{
for(i=0; i < ctx->plte.n_entries; i++)
{
data[i * 3 + 0] = ctx->plte.entries[i].red;
data[i * 3 + 1] = ctx->plte.entries[i].green;
data[i * 3 + 2] = ctx->plte.entries[i].blue;
}
ret = write_chunk(ctx, type_plte, data, ctx->plte.n_entries * 3);
if(ret) return ret;
}
if(ctx->stored.bkgd)
{
switch(ctx->ihdr.color_type)
{
case SPNG_COLOR_TYPE_GRAYSCALE:
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
{
length = 2;
write_u16(data, ctx->bkgd.gray);
break;
}
case SPNG_COLOR_TYPE_TRUECOLOR:
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
{
length = 6;
write_u16(data, ctx->bkgd.red);
write_u16(data + 2, ctx->bkgd.green);
write_u16(data + 4, ctx->bkgd.blue);
break;
}
case SPNG_COLOR_TYPE_INDEXED:
{
length = 1;
data[0] = ctx->bkgd.plte_index;
break;
}
default: return SPNG_EINTERNAL;
}
ret = write_chunk(ctx, type_bkgd, data, length);
if(ret) return ret;
}
if(ctx->stored.hist)
{
length = ctx->plte.n_entries * 2;
for(i=0; i < ctx->plte.n_entries; i++)
{
write_u16(data + i * 2, ctx->hist.frequency[i]);
}
ret = write_chunk(ctx, type_hist, data, length);
if(ret) return ret;
}
if(ctx->stored.trns)
{
if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)
{
write_u16(data, ctx->trns.gray);
ret = write_chunk(ctx, type_trns, data, 2);
}
else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
write_u16(data, ctx->trns.red);
write_u16(data + 2, ctx->trns.green);
write_u16(data + 4, ctx->trns.blue);
ret = write_chunk(ctx, type_trns, data, 6);
}
else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
{
ret = write_chunk(ctx, type_trns, ctx->trns.type3_alpha, ctx->trns.n_type3_entries);
}
if(ret) return ret;
}
if(ctx->stored.phys)
{
write_u32(data, ctx->phys.ppu_x);
write_u32(data + 4, ctx->phys.ppu_y);
data[8] = ctx->phys.unit_specifier;
ret = write_chunk(ctx, type_phys, data, 9);
if(ret) return ret;
}
if(ctx->stored.splt)
{
const struct spng_splt *splt;
unsigned char *cdata = NULL;
uint32_t k;
for(i=0; i < ctx->n_splt; i++)
{
splt = &ctx->splt_list[i];
size_t name_len = strlen(splt->name);
length = name_len + 1;
if(splt->sample_depth == 8) length += splt->n_entries * 6 + 1;
else if(splt->sample_depth == 16) length += splt->n_entries * 10 + 1;
ret = write_header(ctx, type_splt, length, &cdata);
if(ret) return ret;
memcpy(cdata, splt->name, name_len + 1);
cdata += name_len + 2;
cdata[-1] = splt->sample_depth;
if(splt->sample_depth == 8)
{
for(k=0; k < splt->n_entries; k++)
{
cdata[k * 6 + 0] = splt->entries[k].red;
cdata[k * 6 + 1] = splt->entries[k].green;
cdata[k * 6 + 2] = splt->entries[k].blue;
cdata[k * 6 + 3] = splt->entries[k].alpha;
write_u16(cdata + k * 6 + 4, splt->entries[k].frequency);
}
}
else if(splt->sample_depth == 16)
{
for(k=0; k < splt->n_entries; k++)
{
write_u16(cdata + k * 10 + 0, splt->entries[k].red);
write_u16(cdata + k * 10 + 2, splt->entries[k].green);
write_u16(cdata + k * 10 + 4, splt->entries[k].blue);
write_u16(cdata + k * 10 + 6, splt->entries[k].alpha);
write_u16(cdata + k * 10 + 8, splt->entries[k].frequency);
}
}
ret = finish_chunk(ctx);
if(ret) return ret;
}
}
if(ctx->stored.time)
{
write_u16(data, ctx->time.year);
data[2] = ctx->time.month;
data[3] = ctx->time.day;
data[4] = ctx->time.hour;
data[5] = ctx->time.minute;
data[6] = ctx->time.second;
ret = write_chunk(ctx, type_time, data, 7);
if(ret) return ret;
}
if(ctx->stored.text)
{
unsigned char *cdata = NULL;
const struct spng_text2 *text;
const uint8_t *text_type_array[4] = { 0, type_text, type_ztxt, type_itxt };
for(i=0; i < ctx->n_text; i++)
{
text = &ctx->text_list[i];
const uint8_t *text_chunk_type = text_type_array[text->type];
Bytef *compressed_text = NULL;
size_t keyword_len = 0;
size_t language_tag_len = 0;
size_t translated_keyword_len = 0;
size_t compressed_length = 0;
size_t text_length = 0;
keyword_len = strlen(text->keyword);
text_length = strlen(text->text);
length = keyword_len + 1;
if(text->type == SPNG_ZTXT)
{
length += 1; /* compression method */
}
else if(text->type == SPNG_ITXT)
{
if(!text->language_tag || !text->translated_keyword) return SPNG_EINTERNAL;
language_tag_len = strlen(text->language_tag);
translated_keyword_len = strlen(text->translated_keyword);
length += language_tag_len;
if(length < language_tag_len) return SPNG_EOVERFLOW;
length += translated_keyword_len;
if(length < translated_keyword_len) return SPNG_EOVERFLOW;
length += 4; /* compression flag + method + nul for the two strings */
if(length < 4) return SPNG_EOVERFLOW;
}
if(text->compression_flag)
{
ret = spng__deflate_init(ctx, &ctx->text_options);
if(ret) return ret;
z_stream *zstream = &ctx->zstream;
uLongf dest_len = deflateBound(zstream, (uLong)text_length);
compressed_text = spng__malloc(ctx, dest_len);
if(compressed_text == NULL) return SPNG_EMEM;
zstream->next_in = (void*)text->text;
zstream->avail_in = (uInt)text_length;
zstream->next_out = compressed_text;
zstream->avail_out = dest_len;
ret = deflate(zstream, Z_FINISH);
if(ret != Z_STREAM_END)
{
spng__free(ctx, compressed_text);
return SPNG_EZLIB;
}
compressed_length = zstream->total_out;
length += compressed_length;
if(length < compressed_length) return SPNG_EOVERFLOW;
}
else
{
text_length = strlen(text->text);
length += text_length;
if(length < text_length) return SPNG_EOVERFLOW;
}
ret = write_header(ctx, text_chunk_type, length, &cdata);
if(ret)
{
spng__free(ctx, compressed_text);
return ret;
}
memcpy(cdata, text->keyword, keyword_len + 1);
cdata += keyword_len + 1;
if(text->type == SPNG_ITXT)
{
cdata[0] = text->compression_flag;
cdata[1] = 0; /* compression method */
cdata += 2;
memcpy(cdata, text->language_tag, language_tag_len + 1);
cdata += language_tag_len + 1;
memcpy(cdata, text->translated_keyword, translated_keyword_len + 1);
cdata += translated_keyword_len + 1;
}
else if(text->type == SPNG_ZTXT)
{
cdata[0] = 0; /* compression method */
cdata++;
}
if(text->compression_flag) memcpy(cdata, compressed_text, compressed_length);
else memcpy(cdata, text->text, text_length);
spng__free(ctx, compressed_text);
ret = finish_chunk(ctx);
if(ret) return ret;
}
}
if(ctx->stored.offs)
{
write_s32(data, ctx->offs.x);
write_s32(data + 4, ctx->offs.y);
data[8] = ctx->offs.unit_specifier;
ret = write_chunk(ctx, type_offs, data, 9);
if(ret) return ret;
}
if(ctx->stored.exif)
{
ret = write_chunk(ctx, type_exif, ctx->exif.data, ctx->exif.length);
if(ret) return ret;
}
ret = write_unknown_chunks(ctx, SPNG_AFTER_PLTE);
if(ret) return ret;
return 0;
}
static int write_chunks_after_idat(spng_ctx *ctx)
{
if(ctx == NULL) return SPNG_EINTERNAL;
int ret = write_unknown_chunks(ctx, SPNG_AFTER_IDAT);
if(ret) return ret;
return write_iend(ctx);
}
/* Compress and write scanline to IDAT stream */
static int write_idat_bytes(spng_ctx *ctx, const void *scanline, size_t len, int flush)
{
if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL;
if(len > UINT_MAX) return SPNG_EINTERNAL;
int ret = 0;
unsigned char *data = NULL;
z_stream *zstream = &ctx->zstream;
uint32_t idat_length = SPNG_WRITE_SIZE;
zstream->next_in = scanline;
zstream->avail_in = (uInt)len;
do
{
ret = deflate(zstream, flush);
if(zstream->avail_out == 0)
{
ret = finish_chunk(ctx);
if(ret) return encode_err(ctx, ret);
ret = write_header(ctx, type_idat, idat_length, &data);
if(ret) return encode_err(ctx, ret);
zstream->next_out = data;
zstream->avail_out = idat_length;
}
}while(zstream->avail_in);
if(ret != Z_OK) return SPNG_EZLIB;
return 0;
}
static int finish_idat(spng_ctx *ctx)
{
int ret = 0;
unsigned char *data = NULL;
z_stream *zstream = &ctx->zstream;
uint32_t idat_length = SPNG_WRITE_SIZE;
while(ret != Z_STREAM_END)
{
ret = deflate(zstream, Z_FINISH);
if(ret)
{
if(ret == Z_STREAM_END) break;
if(ret != Z_BUF_ERROR) return SPNG_EZLIB;
}
if(zstream->avail_out == 0)
{
ret = finish_chunk(ctx);
if(ret) return encode_err(ctx, ret);
ret = write_header(ctx, type_idat, idat_length, &data);
if(ret) return encode_err(ctx, ret);
zstream->next_out = data;
zstream->avail_out = idat_length;
}
}
uint32_t trimmed_length = idat_length - zstream->avail_out;
ret = trim_chunk(ctx, trimmed_length);
if(ret) return ret;
return finish_chunk(ctx);
}
static int encode_scanline(spng_ctx *ctx, const void *scanline, size_t len)
{
if(ctx == NULL || scanline == NULL) return SPNG_EINTERNAL;
int ret, pass = ctx->row_info.pass;
uint8_t filter = 0;
struct spng_row_info *ri = &ctx->row_info;
const struct spng_subimage *sub = ctx->subimage;
struct encode_flags f = ctx->encode_flags;
unsigned char *filtered_scanline = ctx->filtered_scanline;
size_t scanline_width = sub[pass].scanline_width;
if(len < scanline_width - 1) return SPNG_EINTERNAL;
/* encode_row() interlaces directly to ctx->scanline */
if(scanline != ctx->scanline) memcpy(ctx->scanline, scanline, scanline_width - 1);
if(f.to_bigendian) u16_row_to_bigendian(ctx->scanline, scanline_width - 1);
const int requires_previous = f.filter_choice & (SPNG_FILTER_CHOICE_UP | SPNG_FILTER_CHOICE_AVG | SPNG_FILTER_CHOICE_PAETH);
/* XXX: exclude 'requires_previous' filters by default for first scanline? */
if(!ri->scanline_idx && requires_previous)
{
/* prev_scanline is all zeros for the first scanline */
memset(ctx->prev_scanline, 0, scanline_width);
}
filter = get_best_filter(ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, f.filter_choice);
if(!filter) filtered_scanline = ctx->scanline;
filtered_scanline[-1] = filter;
if(filter)
{
ret = filter_scanline(filtered_scanline, ctx->prev_scanline, ctx->scanline, scanline_width, ctx->bytes_per_pixel, filter);
if(ret) return encode_err(ctx, ret);
}
ret = write_idat_bytes(ctx, filtered_scanline - 1, scanline_width, Z_NO_FLUSH);
if(ret) return encode_err(ctx, ret);
/* The previous scanline is always unfiltered */
void *t = ctx->prev_scanline;
ctx->prev_scanline = ctx->scanline;
ctx->scanline = t;
ret = update_row_info(ctx);
if(ret == SPNG_EOI)
{
int error = finish_idat(ctx);
if(error) encode_err(ctx, error);
if(f.finalize)
{
error = spng_encode_chunks(ctx);
if(error) return encode_err(ctx, error);
}
}
return ret;
}
static int encode_row(spng_ctx *ctx, const void *row, size_t len)
{
if(ctx == NULL || row == NULL) return SPNG_EINTERNAL;
const int pass = ctx->row_info.pass;
if(!ctx->ihdr.interlace_method || pass == 6) return encode_scanline(ctx, row, len);
uint32_t k;
const unsigned pixel_size = ctx->pixel_size;
const unsigned bit_depth = ctx->ihdr.bit_depth;
if(bit_depth < 8)
{
const unsigned samples_per_byte = 8 / bit_depth;
const uint8_t mask = (1 << bit_depth) - 1;
const unsigned initial_shift = 8 - bit_depth;
unsigned shift_amount = initial_shift;
unsigned char *scanline = ctx->scanline;
const unsigned char *row_uc = row;
uint8_t sample;
memset(scanline, 0, ctx->subimage[pass].scanline_width);
for(k=0; k < ctx->subimage[pass].width; k++)
{
size_t ioffset = adam7_x_start[pass] + k * adam7_x_delta[pass];
sample = row_uc[ioffset / samples_per_byte];
sample = sample >> (initial_shift - ioffset * bit_depth % 8);
sample = sample & mask;
sample = sample << shift_amount;
scanline[0] |= sample;
shift_amount -= bit_depth;
if(shift_amount > 7)
{
shift_amount = initial_shift;
scanline++;
}
}
return encode_scanline(ctx, ctx->scanline, len);
}
for(k=0; k < ctx->subimage[pass].width; k++)
{
size_t ioffset = (adam7_x_start[pass] + (size_t) k * adam7_x_delta[pass]) * pixel_size;
memcpy(ctx->scanline + k * pixel_size, (unsigned char*)row + ioffset, pixel_size);
}
return encode_scanline(ctx, ctx->scanline, len);
}
int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len)
{
if(ctx == NULL || scanline == NULL) return SPNG_EINVAL;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
if(len < (ctx->subimage[ctx->row_info.pass].scanline_width -1) ) return SPNG_EBUFSIZ;
return encode_scanline(ctx, scanline, len);
}
int spng_encode_row(spng_ctx *ctx, const void *row, size_t len)
{
if(ctx == NULL || row == NULL) return SPNG_EINVAL;
if(ctx->state >= SPNG_STATE_EOI) return SPNG_EOI;
if(len < ctx->image_width) return SPNG_EBUFSIZ;
return encode_row(ctx, row, len);
}
int spng_encode_chunks(spng_ctx *ctx)
{
if(ctx == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
if(ctx->state < SPNG_STATE_OUTPUT) return SPNG_ENODST;
if(!ctx->encode_only) return SPNG_ECTXTYPE;
int ret = 0;
if(ctx->state < SPNG_STATE_FIRST_IDAT)
{
if(!ctx->stored.ihdr) return SPNG_ENOIHDR;
ret = write_chunks_before_idat(ctx);
if(ret) return encode_err(ctx, ret);
ctx->state = SPNG_STATE_FIRST_IDAT;
}
else if(ctx->state == SPNG_STATE_FIRST_IDAT)
{
return 0;
}
else if(ctx->state == SPNG_STATE_EOI)
{
ret = write_chunks_after_idat(ctx);
if(ret) return encode_err(ctx, ret);
ctx->state = SPNG_STATE_IEND;
}
else return SPNG_EOPSTATE;
return 0;
}
int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags)
{
if(ctx == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
if(!ctx->encode_only) return SPNG_ECTXTYPE;
if(!ctx->stored.ihdr) return SPNG_ENOIHDR;
if( !(fmt == SPNG_FMT_PNG || fmt == SPNG_FMT_RAW) ) return SPNG_EFMT;
int ret = 0;
const struct spng_ihdr *ihdr = &ctx->ihdr;
struct encode_flags *encode_flags = &ctx->encode_flags;
if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED && !ctx->stored.plte) return SPNG_ENOPLTE;
ret = calculate_image_width(ihdr, fmt, &ctx->image_width);
if(ret) return encode_err(ctx, ret);
if(ctx->image_width > SIZE_MAX / ihdr->height) ctx->image_size = 0; /* overflow */
else ctx->image_size = ctx->image_width * ihdr->height;
if( !(flags & SPNG_ENCODE_PROGRESSIVE) )
{
if(img == NULL) return 1;
if(!ctx->image_size) return SPNG_EOVERFLOW;
if(len != ctx->image_size) return SPNG_EBUFSIZ;
}
ret = spng_encode_chunks(ctx);
if(ret) return encode_err(ctx, ret);
ret = calculate_subimages(ctx);
if(ret) return encode_err(ctx, ret);
if(ihdr->bit_depth < 8) ctx->bytes_per_pixel = 1;
else ctx->bytes_per_pixel = num_channels(ihdr) * (ihdr->bit_depth / 8);
if(spng__optimize(SPNG_FILTER_CHOICE))
{
/* Filtering would make no difference */
if(!ctx->image_options.compression_level)
{
encode_flags->filter_choice = SPNG_DISABLE_FILTERING;
}
/* Palette indices and low bit-depth images do not benefit from filtering */
if(ihdr->color_type == SPNG_COLOR_TYPE_INDEXED || ihdr->bit_depth < 8)
{
encode_flags->filter_choice = SPNG_DISABLE_FILTERING;
}
}
/* This is technically the same as disabling filtering */
if(encode_flags->filter_choice == SPNG_FILTER_CHOICE_NONE)
{
encode_flags->filter_choice = SPNG_DISABLE_FILTERING;
}
if(!encode_flags->filter_choice && spng__optimize(SPNG_IMG_COMPRESSION_STRATEGY))
{
ctx->image_options.strategy = Z_DEFAULT_STRATEGY;
}
ret = spng__deflate_init(ctx, &ctx->image_options);
if(ret) return encode_err(ctx, ret);
size_t scanline_buf_size = ctx->subimage[ctx->widest_pass].scanline_width;
scanline_buf_size += 32;
if(scanline_buf_size < 32) return SPNG_EOVERFLOW;
ctx->scanline_buf = spng__malloc(ctx, scanline_buf_size);
ctx->prev_scanline_buf = spng__malloc(ctx, scanline_buf_size);
if(ctx->scanline_buf == NULL || ctx->prev_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM);
/* Maintain alignment for pixels, filter at [-1] */
ctx->scanline = ctx->scanline_buf + 16;
ctx->prev_scanline = ctx->prev_scanline_buf + 16;
if(encode_flags->filter_choice)
{
ctx->filtered_scanline_buf = spng__malloc(ctx, scanline_buf_size);
if(ctx->filtered_scanline_buf == NULL) return encode_err(ctx, SPNG_EMEM);
ctx->filtered_scanline = ctx->filtered_scanline_buf + 16;
}
struct spng_subimage *sub = ctx->subimage;
struct spng_row_info *ri = &ctx->row_info;
ctx->fmt = fmt;
z_stream *zstream = &ctx->zstream;
zstream->avail_out = SPNG_WRITE_SIZE;
ret = write_header(ctx, type_idat, zstream->avail_out, &zstream->next_out);
if(ret) return encode_err(ctx, ret);
if(ihdr->interlace_method) encode_flags->interlace = 1;
if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW) ) encode_flags->same_layout = 1;
if(ihdr->bit_depth == 16 && fmt != SPNG_FMT_RAW) encode_flags->to_bigendian = 1;
if(flags & SPNG_ENCODE_FINALIZE) encode_flags->finalize = 1;
while(!sub[ri->pass].width || !sub[ri->pass].height) ri->pass++;
if(encode_flags->interlace) ri->row_num = adam7_y_start[ri->pass];
ctx->pixel_size = 4; /* SPNG_FMT_RGBA8 */
if(fmt == SPNG_FMT_RGBA16) ctx->pixel_size = 8;
else if(fmt == SPNG_FMT_RGB8) ctx->pixel_size = 3;
else if(fmt == SPNG_FMT_G8) ctx->pixel_size = 1;
else if(fmt == SPNG_FMT_GA8) ctx->pixel_size = 2;
else if(fmt & (SPNG_FMT_PNG | SPNG_FMT_RAW)) ctx->pixel_size = ctx->bytes_per_pixel;
ctx->state = SPNG_STATE_ENCODE_INIT;
if(flags & SPNG_ENCODE_PROGRESSIVE)
{
encode_flags->progressive = 1;
return 0;
}
do
{
size_t ioffset = ri->row_num * ctx->image_width;
ret = encode_row(ctx, (unsigned char*)img + ioffset, ctx->image_width);
}while(!ret);
if(ret != SPNG_EOI) return encode_err(ctx, ret);
return 0;
}
spng_ctx *spng_ctx_new(int flags)
{
struct spng_alloc alloc =
{
.malloc_fn = malloc,
.realloc_fn = realloc,
.calloc_fn = calloc,
.free_fn = free
};
return spng_ctx_new2(&alloc, flags);
}
spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags)
{
if(alloc == NULL) return NULL;
if(flags != (flags & SPNG__CTX_FLAGS_ALL)) return NULL;
if(alloc->malloc_fn == NULL) return NULL;
if(alloc->realloc_fn == NULL) return NULL;
if(alloc->calloc_fn == NULL) return NULL;
if(alloc->free_fn == NULL) return NULL;
spng_ctx *ctx = alloc->calloc_fn(1, sizeof(spng_ctx));
if(ctx == NULL) return NULL;
ctx->alloc = *alloc;
ctx->max_width = spng_u32max;
ctx->max_height = spng_u32max;
ctx->max_chunk_size = spng_u32max;
ctx->chunk_cache_limit = SIZE_MAX;
ctx->chunk_count_limit = SPNG_MAX_CHUNK_COUNT;
ctx->state = SPNG_STATE_INIT;
ctx->crc_action_critical = SPNG_CRC_ERROR;
ctx->crc_action_ancillary = SPNG_CRC_DISCARD;
const struct spng__zlib_options image_defaults =
{
.compression_level = Z_DEFAULT_COMPRESSION,
.window_bits = 15,
.mem_level = 8,
.strategy = Z_FILTERED,
.data_type = 0 /* Z_BINARY */
};
const struct spng__zlib_options text_defaults =
{
.compression_level = Z_DEFAULT_COMPRESSION,
.window_bits = 15,
.mem_level = 8,
.strategy = Z_DEFAULT_STRATEGY,
.data_type = 1 /* Z_TEXT */
};
ctx->image_options = image_defaults;
ctx->text_options = text_defaults;
ctx->optimize_option = ~0;
ctx->encode_flags.filter_choice = SPNG_FILTER_CHOICE_ALL;
ctx->flags = flags;
if(flags & SPNG_CTX_ENCODER) ctx->encode_only = 1;
return ctx;
}
void spng_ctx_free(spng_ctx *ctx)
{
if(ctx == NULL) return;
if(ctx->streaming && ctx->stream_buf != NULL) spng__free(ctx, ctx->stream_buf);
if(!ctx->user.exif) spng__free(ctx, ctx->exif.data);
if(!ctx->user.iccp) spng__free(ctx, ctx->iccp.profile);
uint32_t i;
if(ctx->splt_list != NULL && !ctx->user.splt)
{
for(i=0; i < ctx->n_splt; i++)
{
spng__free(ctx, ctx->splt_list[i].entries);
}
spng__free(ctx, ctx->splt_list);
}
if(ctx->text_list != NULL)
{
for(i=0; i< ctx->n_text; i++)
{
if(ctx->user.text) break;
spng__free(ctx, ctx->text_list[i].keyword);
if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text);
}
spng__free(ctx, ctx->text_list);
}
if(ctx->chunk_list != NULL && !ctx->user.unknown)
{
for(i=0; i< ctx->n_chunks; i++)
{
spng__free(ctx, ctx->chunk_list[i].data);
}
spng__free(ctx, ctx->chunk_list);
}
if(ctx->deflate) deflateEnd(&ctx->zstream);
else inflateEnd(&ctx->zstream);
if(!ctx->user_owns_out_png) spng__free(ctx, ctx->out_png);
spng__free(ctx, ctx->gamma_lut16);
spng__free(ctx, ctx->row_buf);
spng__free(ctx, ctx->scanline_buf);
spng__free(ctx, ctx->prev_scanline_buf);
spng__free(ctx, ctx->filtered_scanline_buf);
spng_free_fn *free_fn = ctx->alloc.free_fn;
memset(ctx, 0, sizeof(spng_ctx));
free_fn(ctx);
}
static int buffer_read_fn(spng_ctx *ctx, void *user, void *data, size_t n)
{
if(n > ctx->bytes_left) return SPNG_IO_EOF;
(void)user;
(void)data;
ctx->data = ctx->data + ctx->last_read_size;
ctx->last_read_size = n;
ctx->bytes_left -= n;
return 0;
}
static int file_read_fn(spng_ctx *ctx, void *user, void *data, size_t n)
{
FILE *file = user;
(void)ctx;
if(fread(data, n, 1, file) != 1)
{
if(feof(file)) return SPNG_IO_EOF;
else return SPNG_IO_ERROR;
}
return 0;
}
static int file_write_fn(spng_ctx *ctx, void *user, void *data, size_t n)
{
FILE *file = user;
(void)ctx;
if(fwrite(data, n, 1, file) != 1) return SPNG_IO_ERROR;
return 0;
}
int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size)
{
if(ctx == NULL || buf == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
if(ctx->encode_only) return SPNG_ECTXTYPE; /* not supported */
if(ctx->data != NULL) return SPNG_EBUF_SET;
ctx->data = buf;
ctx->png_base = buf;
ctx->data_size = size;
ctx->bytes_left = size;
ctx->read_fn = buffer_read_fn;
ctx->state = SPNG_STATE_INPUT;
return 0;
}
int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user)
{
if(ctx == NULL || rw_func == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
/* SPNG_STATE_OUTPUT shares the same value */
if(ctx->state >= SPNG_STATE_INPUT) return SPNG_EBUF_SET;
if(ctx->encode_only)
{
if(ctx->out_png != NULL) return SPNG_EBUF_SET;
ctx->write_fn = rw_func;
ctx->write_ptr = ctx->stream_buf;
ctx->state = SPNG_STATE_OUTPUT;
}
else
{
ctx->stream_buf = spng__malloc(ctx, SPNG_READ_SIZE);
if(ctx->stream_buf == NULL) return SPNG_EMEM;
ctx->read_fn = rw_func;
ctx->data = ctx->stream_buf;
ctx->data_size = SPNG_READ_SIZE;
ctx->state = SPNG_STATE_INPUT;
}
ctx->stream_user_ptr = user;
ctx->streaming = 1;
return 0;
}
int spng_set_png_file(spng_ctx *ctx, FILE *file)
{
if(file == NULL) return 1;
if(ctx->encode_only) return spng_set_png_stream(ctx, file_write_fn, file);
return spng_set_png_stream(ctx, file_read_fn, file);
}
void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error)
{
int tmp = 0;
error = error ? error : &tmp;
*error = 0;
if(ctx == NULL || !len) *error = SPNG_EINVAL;
if(*error) return NULL;
if(!ctx->encode_only) *error = SPNG_ECTXTYPE;
else if(!ctx->state) *error = SPNG_EBADSTATE;
else if(!ctx->internal_buffer) *error = SPNG_EOPSTATE;
else if(ctx->state < SPNG_STATE_EOI) *error = SPNG_EOPSTATE;
else if(ctx->state != SPNG_STATE_IEND) *error = SPNG_ENOTFINAL;
if(*error) return NULL;
ctx->user_owns_out_png = 1;
*len = ctx->bytes_encoded;
return ctx->out_png;
}
int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height)
{
if(ctx == NULL) return 1;
if(width > spng_u32max || height > spng_u32max) return 1;
ctx->max_width = width;
ctx->max_height = height;
return 0;
}
int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height)
{
if(ctx == NULL || width == NULL || height == NULL) return 1;
*width = ctx->max_width;
*height = ctx->max_height;
return 0;
}
int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_limit)
{
if(ctx == NULL || chunk_size > spng_u32max || chunk_size > cache_limit) return 1;
ctx->max_chunk_size = chunk_size;
ctx->chunk_cache_limit = cache_limit;
return 0;
}
int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_limit)
{
if(ctx == NULL || chunk_size == NULL || cache_limit == NULL) return 1;
*chunk_size = ctx->max_chunk_size;
*cache_limit = ctx->chunk_cache_limit;
return 0;
}
int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary)
{
if(ctx == NULL) return 1;
if(ctx->encode_only) return SPNG_ECTXTYPE;
if(critical > 2 || critical < 0) return 1;
if(ancillary > 2 || ancillary < 0) return 1;
if(critical == SPNG_CRC_DISCARD) return 1;
ctx->crc_action_critical = critical;
ctx->crc_action_ancillary = ancillary;
return 0;
}
int spng_set_option(spng_ctx *ctx, enum spng_option option, int value)
{
if(ctx == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
switch(option)
{
case SPNG_KEEP_UNKNOWN_CHUNKS:
{
ctx->keep_unknown = value ? 1 : 0;
break;
}
case SPNG_IMG_COMPRESSION_LEVEL:
{
ctx->image_options.compression_level = value;
break;
}
case SPNG_IMG_WINDOW_BITS:
{
ctx->image_options.window_bits = value;
break;
}
case SPNG_IMG_MEM_LEVEL:
{
ctx->image_options.mem_level = value;
break;
}
case SPNG_IMG_COMPRESSION_STRATEGY:
{
ctx->image_options.strategy = value;
break;
}
case SPNG_TEXT_COMPRESSION_LEVEL:
{
ctx->text_options.compression_level = value;
break;
}
case SPNG_TEXT_WINDOW_BITS:
{
ctx->text_options.window_bits = value;
break;
}
case SPNG_TEXT_MEM_LEVEL:
{
ctx->text_options.mem_level = value;
break;
}
case SPNG_TEXT_COMPRESSION_STRATEGY:
{
ctx->text_options.strategy = value;
break;
}
case SPNG_FILTER_CHOICE:
{
if(value & ~SPNG_FILTER_CHOICE_ALL) return 1;
ctx->encode_flags.filter_choice = value;
break;
}
case SPNG_CHUNK_COUNT_LIMIT:
{
if(value < 0) return 1;
if(value > (int)ctx->chunk_count_total) return 1;
ctx->chunk_count_limit = value;
break;
}
case SPNG_ENCODE_TO_BUFFER:
{
if(value < 0) return 1;
if(!ctx->encode_only) return SPNG_ECTXTYPE;
if(ctx->state >= SPNG_STATE_OUTPUT) return SPNG_EOPSTATE;
if(!value) break;
ctx->internal_buffer = 1;
ctx->state = SPNG_STATE_OUTPUT;
break;
}
default: return 1;
}
/* Option can no longer be overriden by the library */
if(option < 32) ctx->optimize_option &= ~(1 << option);
return 0;
}
int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value)
{
if(ctx == NULL || value == NULL) return 1;
if(!ctx->state) return SPNG_EBADSTATE;
switch(option)
{
case SPNG_KEEP_UNKNOWN_CHUNKS:
{
*value = ctx->keep_unknown;
break;
}
case SPNG_IMG_COMPRESSION_LEVEL:
{
*value = ctx->image_options.compression_level;
break;
}
case SPNG_IMG_WINDOW_BITS:
{
*value = ctx->image_options.window_bits;
break;
}
case SPNG_IMG_MEM_LEVEL:
{
*value = ctx->image_options.mem_level;
break;
}
case SPNG_IMG_COMPRESSION_STRATEGY:
{
*value = ctx->image_options.strategy;
break;
}
case SPNG_TEXT_COMPRESSION_LEVEL:
{
*value = ctx->text_options.compression_level;
break;
}
case SPNG_TEXT_WINDOW_BITS:
{
*value = ctx->text_options.window_bits;
break;
}
case SPNG_TEXT_MEM_LEVEL:
{
*value = ctx->text_options.mem_level;
break;
}
case SPNG_TEXT_COMPRESSION_STRATEGY:
{
*value = ctx->text_options.strategy;
break;
}
case SPNG_FILTER_CHOICE:
{
*value = ctx->encode_flags.filter_choice;
break;
}
case SPNG_CHUNK_COUNT_LIMIT:
{
*value = ctx->chunk_count_limit;
break;
}
case SPNG_ENCODE_TO_BUFFER:
{
if(ctx->internal_buffer) *value = 1;
else *value = 0;
break;
}
default: return 1;
}
return 0;
}
int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len)
{
if(ctx == NULL || len == NULL) return 1;
int ret = read_chunks(ctx, 1);
if(ret) return ret;
ret = check_decode_fmt(&ctx->ihdr, fmt);
if(ret) return ret;
return calculate_image_size(&ctx->ihdr, fmt, len);
}
int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr)
{
if(ctx == NULL) return 1;
int ret = read_chunks(ctx, 1);
if(ret) return ret;
if(ihdr == NULL) return 1;
*ihdr = ctx->ihdr;
return 0;
}
int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte)
{
SPNG_GET_CHUNK_BOILERPLATE(plte);
*plte = ctx->plte;
return 0;
}
int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns)
{
SPNG_GET_CHUNK_BOILERPLATE(trns);
*trns = ctx->trns;
return 0;
}
int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm)
{
SPNG_GET_CHUNK_BOILERPLATE(chrm);
chrm->white_point_x = (double)ctx->chrm_int.white_point_x / 100000.0;
chrm->white_point_y = (double)ctx->chrm_int.white_point_y / 100000.0;
chrm->red_x = (double)ctx->chrm_int.red_x / 100000.0;
chrm->red_y = (double)ctx->chrm_int.red_y / 100000.0;
chrm->blue_y = (double)ctx->chrm_int.blue_y / 100000.0;
chrm->blue_x = (double)ctx->chrm_int.blue_x / 100000.0;
chrm->green_x = (double)ctx->chrm_int.green_x / 100000.0;
chrm->green_y = (double)ctx->chrm_int.green_y / 100000.0;
return 0;
}
int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm)
{
SPNG_GET_CHUNK_BOILERPLATE(chrm);
*chrm = ctx->chrm_int;
return 0;
}
int spng_get_gama(spng_ctx *ctx, double *gamma)
{
double *gama = gamma;
SPNG_GET_CHUNK_BOILERPLATE(gama);
*gama = (double)ctx->gama / 100000.0;
return 0;
}
int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int)
{
uint32_t *gama = gama_int;
SPNG_GET_CHUNK_BOILERPLATE(gama);
*gama_int = ctx->gama;
return 0;
}
int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp)
{
SPNG_GET_CHUNK_BOILERPLATE(iccp);
*iccp = ctx->iccp;
return 0;
}
int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit)
{
SPNG_GET_CHUNK_BOILERPLATE(sbit);
*sbit = ctx->sbit;
return 0;
}
int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent)
{
uint8_t *srgb = rendering_intent;
SPNG_GET_CHUNK_BOILERPLATE(srgb);
*srgb = ctx->srgb_rendering_intent;
return 0;
}
int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text)
{
if(ctx == NULL) return 1;
int ret = read_chunks(ctx, 0);
if(ret) return ret;
if(!ctx->stored.text) return SPNG_ECHUNKAVAIL;
if(n_text == NULL) return 1;
if(text == NULL)
{
*n_text = ctx->n_text;
return 0;
}
if(*n_text < ctx->n_text) return 1;
uint32_t i;
for(i=0; i< ctx->n_text; i++)
{
text[i].type = ctx->text_list[i].type;
memcpy(&text[i].keyword, ctx->text_list[i].keyword, strlen(ctx->text_list[i].keyword) + 1);
text[i].compression_method = 0;
text[i].compression_flag = ctx->text_list[i].compression_flag;
text[i].language_tag = ctx->text_list[i].language_tag;
text[i].translated_keyword = ctx->text_list[i].translated_keyword;
text[i].length = ctx->text_list[i].text_length;
text[i].text = ctx->text_list[i].text;
}
return ret;
}
int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd)
{
SPNG_GET_CHUNK_BOILERPLATE(bkgd);
*bkgd = ctx->bkgd;
return 0;
}
int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist)
{
SPNG_GET_CHUNK_BOILERPLATE(hist);
*hist = ctx->hist;
return 0;
}
int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys)
{
SPNG_GET_CHUNK_BOILERPLATE(phys);
*phys = ctx->phys;
return 0;
}
int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt)
{
if(ctx == NULL) return 1;
int ret = read_chunks(ctx, 0);
if(ret) return ret;
if(!ctx->stored.splt) return SPNG_ECHUNKAVAIL;
if(n_splt == NULL) return 1;
if(splt == NULL)
{
*n_splt = ctx->n_splt;
return 0;
}
if(*n_splt < ctx->n_splt) return 1;
memcpy(splt, ctx->splt_list, ctx->n_splt * sizeof(struct spng_splt));
return 0;
}
int spng_get_time(spng_ctx *ctx, struct spng_time *time)
{
SPNG_GET_CHUNK_BOILERPLATE(time);
*time = ctx->time;
return 0;
}
int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks)
{
if(ctx == NULL) return 1;
int ret = read_chunks(ctx, 0);
if(ret) return ret;
if(!ctx->stored.unknown) return SPNG_ECHUNKAVAIL;
if(n_chunks == NULL) return 1;
if(chunks == NULL)
{
*n_chunks = ctx->n_chunks;
return 0;
}
if(*n_chunks < ctx->n_chunks) return 1;
memcpy(chunks, ctx->chunk_list, sizeof(struct spng_unknown_chunk));
return 0;
}
int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs)
{
SPNG_GET_CHUNK_BOILERPLATE(offs);
*offs = ctx->offs;
return 0;
}
int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif)
{
SPNG_GET_CHUNK_BOILERPLATE(exif);
*exif = ctx->exif;
return 0;
}
int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr)
{
SPNG_SET_CHUNK_BOILERPLATE(ihdr);
if(ctx->stored.ihdr) return 1;
ret = check_ihdr(ihdr, ctx->max_width, ctx->max_height);
if(ret) return ret;
ctx->ihdr = *ihdr;
ctx->stored.ihdr = 1;
ctx->user.ihdr = 1;
return 0;
}
int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte)
{
SPNG_SET_CHUNK_BOILERPLATE(plte);
if(!ctx->stored.ihdr) return 1;
if(check_plte(plte, &ctx->ihdr)) return 1;
ctx->plte.n_entries = plte->n_entries;
memcpy(ctx->plte.entries, plte->entries, plte->n_entries * sizeof(struct spng_plte_entry));
ctx->stored.plte = 1;
ctx->user.plte = 1;
return 0;
}
int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns)
{
SPNG_SET_CHUNK_BOILERPLATE(trns);
if(!ctx->stored.ihdr) return SPNG_ENOIHDR;
if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)
{
ctx->trns.gray = trns->gray;
}
else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)
{
ctx->trns.red = trns->red;
ctx->trns.green = trns->green;
ctx->trns.blue = trns->blue;
}
else if(ctx->ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
{
if(!ctx->stored.plte) return SPNG_ETRNS_NO_PLTE;
if(trns->n_type3_entries > ctx->plte.n_entries) return 1;
ctx->trns.n_type3_entries = trns->n_type3_entries;
memcpy(ctx->trns.type3_alpha, trns->type3_alpha, trns->n_type3_entries);
}
else return SPNG_ETRNS_COLOR_TYPE;
ctx->stored.trns = 1;
ctx->user.trns = 1;
return 0;
}
int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm)
{
SPNG_SET_CHUNK_BOILERPLATE(chrm);
struct spng_chrm_int chrm_int;
chrm_int.white_point_x = (uint32_t)(chrm->white_point_x * 100000.0);
chrm_int.white_point_y = (uint32_t)(chrm->white_point_y * 100000.0);
chrm_int.red_x = (uint32_t)(chrm->red_x * 100000.0);
chrm_int.red_y = (uint32_t)(chrm->red_y * 100000.0);
chrm_int.green_x = (uint32_t)(chrm->green_x * 100000.0);
chrm_int.green_y = (uint32_t)(chrm->green_y * 100000.0);
chrm_int.blue_x = (uint32_t)(chrm->blue_x * 100000.0);
chrm_int.blue_y = (uint32_t)(chrm->blue_y * 100000.0);
if(check_chrm_int(&chrm_int)) return SPNG_ECHRM;
ctx->chrm_int = chrm_int;
ctx->stored.chrm = 1;
ctx->user.chrm = 1;
return 0;
}
int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int)
{
SPNG_SET_CHUNK_BOILERPLATE(chrm_int);
if(check_chrm_int(chrm_int)) return SPNG_ECHRM;
ctx->chrm_int = *chrm_int;
ctx->stored.chrm = 1;
ctx->user.chrm = 1;
return 0;
}
int spng_set_gama(spng_ctx *ctx, double gamma)
{
SPNG_SET_CHUNK_BOILERPLATE(ctx);
uint32_t gama = gamma * 100000.0;
if(!gama) return 1;
if(gama > spng_u32max) return 1;
ctx->gama = gama;
ctx->stored.gama = 1;
ctx->user.gama = 1;
return 0;
}
int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma)
{
SPNG_SET_CHUNK_BOILERPLATE(ctx);
if(!gamma) return 1;
if(gamma > spng_u32max) return 1;
ctx->gama = gamma;
ctx->stored.gama = 1;
ctx->user.gama = 1;
return 0;
}
int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp)
{
SPNG_SET_CHUNK_BOILERPLATE(iccp);
if(check_png_keyword(iccp->profile_name)) return SPNG_EICCP_NAME;
if(!iccp->profile_len) return SPNG_ECHUNK_SIZE;
if(iccp->profile_len > spng_u32max) return SPNG_ECHUNK_STDLEN;
if(ctx->iccp.profile && !ctx->user.iccp) spng__free(ctx, ctx->iccp.profile);
ctx->iccp = *iccp;
ctx->stored.iccp = 1;
ctx->user.iccp = 1;
return 0;
}
int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit)
{
SPNG_SET_CHUNK_BOILERPLATE(sbit);
if(check_sbit(sbit, &ctx->ihdr)) return 1;
if(!ctx->stored.ihdr) return 1;
ctx->sbit = *sbit;
ctx->stored.sbit = 1;
ctx->user.sbit = 1;
return 0;
}
int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent)
{
SPNG_SET_CHUNK_BOILERPLATE(ctx);
if(rendering_intent > 3) return 1;
ctx->srgb_rendering_intent = rendering_intent;
ctx->stored.srgb = 1;
ctx->user.srgb = 1;
return 0;
}
int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text)
{
if(!n_text) return 1;
SPNG_SET_CHUNK_BOILERPLATE(text);
uint32_t i;
for(i=0; i < n_text; i++)
{
if(check_png_keyword(text[i].keyword)) return SPNG_ETEXT_KEYWORD;
if(!text[i].length) return 1;
if(text[i].length > UINT_MAX) return 1;
if(text[i].text == NULL) return 1;
if(text[i].type == SPNG_TEXT)
{
if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1;
}
else if(text[i].type == SPNG_ZTXT)
{
if(ctx->strict && check_png_text(text[i].text, text[i].length)) return 1;
if(text[i].compression_method != 0) return SPNG_EZTXT_COMPRESSION_METHOD;
}
else if(text[i].type == SPNG_ITXT)
{
if(text[i].compression_flag > 1) return SPNG_EITXT_COMPRESSION_FLAG;
if(text[i].compression_method != 0) return SPNG_EITXT_COMPRESSION_METHOD;
if(text[i].language_tag == NULL) return SPNG_EITXT_LANG_TAG;
if(text[i].translated_keyword == NULL) return SPNG_EITXT_TRANSLATED_KEY;
}
else return 1;
}
struct spng_text2 *text_list = spng__calloc(ctx, sizeof(struct spng_text2), n_text);
if(!text_list) return SPNG_EMEM;
if(ctx->text_list != NULL)
{
for(i=0; i < ctx->n_text; i++)
{
if(ctx->user.text) break;
spng__free(ctx, ctx->text_list[i].keyword);
if(ctx->text_list[i].compression_flag) spng__free(ctx, ctx->text_list[i].text);
}
spng__free(ctx, ctx->text_list);
}
for(i=0; i < n_text; i++)
{
text_list[i].type = text[i].type;
/* Prevent issues with spng_text.keyword[80] going out of scope */
text_list[i].keyword = text_list[i].user_keyword_storage;
memcpy(text_list[i].user_keyword_storage, text[i].keyword, strlen(text[i].keyword));
text_list[i].text = text[i].text;
text_list[i].text_length = text[i].length;
if(text[i].type == SPNG_ZTXT)
{
text_list[i].compression_flag = 1;
}
else if(text[i].type == SPNG_ITXT)
{
text_list[i].compression_flag = text[i].compression_flag;
text_list[i].language_tag = text[i].language_tag;
text_list[i].translated_keyword = text[i].translated_keyword;
}
}
ctx->text_list = text_list;
ctx->n_text = n_text;
ctx->stored.text = 1;
ctx->user.text = 1;
return 0;
}
int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd)
{
SPNG_SET_CHUNK_BOILERPLATE(bkgd);
if(!ctx->stored.ihdr) return 1;
if(ctx->ihdr.color_type == 0 || ctx->ihdr.color_type == 4)
{
ctx->bkgd.gray = bkgd->gray;
}
else if(ctx->ihdr.color_type == 2 || ctx->ihdr.color_type == 6)
{
ctx->bkgd.red = bkgd->red;
ctx->bkgd.green = bkgd->green;
ctx->bkgd.blue = bkgd->blue;
}
else if(ctx->ihdr.color_type == 3)
{
if(!ctx->stored.plte) return SPNG_EBKGD_NO_PLTE;
if(bkgd->plte_index >= ctx->plte.n_entries) return SPNG_EBKGD_PLTE_IDX;
ctx->bkgd.plte_index = bkgd->plte_index;
}
ctx->stored.bkgd = 1;
ctx->user.bkgd = 1;
return 0;
}
int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist)
{
SPNG_SET_CHUNK_BOILERPLATE(hist);
if(!ctx->stored.plte) return SPNG_EHIST_NO_PLTE;
ctx->hist = *hist;
ctx->stored.hist = 1;
ctx->user.hist = 1;
return 0;
}
int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys)
{
SPNG_SET_CHUNK_BOILERPLATE(phys);
if(check_phys(phys)) return SPNG_EPHYS;
ctx->phys = *phys;
ctx->stored.phys = 1;
ctx->user.phys = 1;
return 0;
}
int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt)
{
if(!n_splt) return 1;
SPNG_SET_CHUNK_BOILERPLATE(splt);
uint32_t i;
for(i=0; i < n_splt; i++)
{
if(check_png_keyword(splt[i].name)) return SPNG_ESPLT_NAME;
if( !(splt[i].sample_depth == 8 || splt[i].sample_depth == 16) ) return SPNG_ESPLT_DEPTH;
}
if(ctx->stored.splt && !ctx->user.splt)
{
for(i=0; i < ctx->n_splt; i++)
{
if(ctx->splt_list[i].entries != NULL) spng__free(ctx, ctx->splt_list[i].entries);
}
spng__free(ctx, ctx->splt_list);
}
ctx->splt_list = splt;
ctx->n_splt = n_splt;
ctx->stored.splt = 1;
ctx->user.splt = 1;
return 0;
}
int spng_set_time(spng_ctx *ctx, struct spng_time *time)
{
SPNG_SET_CHUNK_BOILERPLATE(time);
if(check_time(time)) return SPNG_ETIME;
ctx->time = *time;
ctx->stored.time = 1;
ctx->user.time = 1;
return 0;
}
int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks)
{
if(!n_chunks) return 1;
SPNG_SET_CHUNK_BOILERPLATE(chunks);
uint32_t i;
for(i=0; i < n_chunks; i++)
{
if(chunks[i].length > spng_u32max) return SPNG_ECHUNK_STDLEN;
if(chunks[i].length && chunks[i].data == NULL) return 1;
switch(chunks[i].location)
{
case SPNG_AFTER_IHDR:
case SPNG_AFTER_PLTE:
case SPNG_AFTER_IDAT:
break;
default: return SPNG_ECHUNK_POS;
}
}
if(ctx->stored.unknown && !ctx->user.unknown)
{
for(i=0; i < ctx->n_chunks; i++)
{
spng__free(ctx, ctx->chunk_list[i].data);
}
spng__free(ctx, ctx->chunk_list);
}
ctx->chunk_list = chunks;
ctx->n_chunks = n_chunks;
ctx->stored.unknown = 1;
ctx->user.unknown = 1;
return 0;
}
int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs)
{
SPNG_SET_CHUNK_BOILERPLATE(offs);
if(check_offs(offs)) return SPNG_EOFFS;
ctx->offs = *offs;
ctx->stored.offs = 1;
ctx->user.offs = 1;
return 0;
}
int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif)
{
SPNG_SET_CHUNK_BOILERPLATE(exif);
if(check_exif(exif)) return SPNG_EEXIF;
if(ctx->exif.data != NULL && !ctx->user.exif) spng__free(ctx, ctx->exif.data);
ctx->exif = *exif;
ctx->stored.exif = 1;
ctx->user.exif = 1;
return 0;
}
const char *spng_strerror(int err)
{
switch(err)
{
case SPNG_IO_EOF: return "end of stream";
case SPNG_IO_ERROR: return "stream error";
case SPNG_OK: return "success";
case SPNG_EINVAL: return "invalid argument";
case SPNG_EMEM: return "out of memory";
case SPNG_EOVERFLOW: return "arithmetic overflow";
case SPNG_ESIGNATURE: return "invalid signature";
case SPNG_EWIDTH: return "invalid image width";
case SPNG_EHEIGHT: return "invalid image height";
case SPNG_EUSER_WIDTH: return "image width exceeds user limit";
case SPNG_EUSER_HEIGHT: return "image height exceeds user limit";
case SPNG_EBIT_DEPTH: return "invalid bit depth";
case SPNG_ECOLOR_TYPE: return "invalid color type";
case SPNG_ECOMPRESSION_METHOD: return "invalid compression method";
case SPNG_EFILTER_METHOD: return "invalid filter method";
case SPNG_EINTERLACE_METHOD: return "invalid interlace method";
case SPNG_EIHDR_SIZE: return "invalid IHDR chunk size";
case SPNG_ENOIHDR: return "missing IHDR chunk";
case SPNG_ECHUNK_POS: return "invalid chunk position";
case SPNG_ECHUNK_SIZE: return "invalid chunk length";
case SPNG_ECHUNK_CRC: return "invalid chunk checksum";
case SPNG_ECHUNK_TYPE: return "invalid chunk type";
case SPNG_ECHUNK_UNKNOWN_CRITICAL: return "unknown critical chunk";
case SPNG_EDUP_PLTE: return "duplicate PLTE chunk";
case SPNG_EDUP_CHRM: return "duplicate cHRM chunk";
case SPNG_EDUP_GAMA: return "duplicate gAMA chunk";
case SPNG_EDUP_ICCP: return "duplicate iCCP chunk";
case SPNG_EDUP_SBIT: return "duplicate sBIT chunk";
case SPNG_EDUP_SRGB: return "duplicate sRGB chunk";
case SPNG_EDUP_BKGD: return "duplicate bKGD chunk";
case SPNG_EDUP_HIST: return "duplicate hIST chunk";
case SPNG_EDUP_TRNS: return "duplicate tRNS chunk";
case SPNG_EDUP_PHYS: return "duplicate pHYs chunk";
case SPNG_EDUP_TIME: return "duplicate tIME chunk";
case SPNG_EDUP_OFFS: return "duplicate oFFs chunk";
case SPNG_EDUP_EXIF: return "duplicate eXIf chunk";
case SPNG_ECHRM: return "invalid cHRM chunk";
case SPNG_EPLTE_IDX: return "invalid palette (PLTE) index";
case SPNG_ETRNS_COLOR_TYPE: return "tRNS chunk with incompatible color type";
case SPNG_ETRNS_NO_PLTE: return "missing palette (PLTE) for tRNS chunk";
case SPNG_EGAMA: return "invalid gAMA chunk";
case SPNG_EICCP_NAME: return "invalid iCCP profile name";
case SPNG_EICCP_COMPRESSION_METHOD: return "invalid iCCP compression method";
case SPNG_ESBIT: return "invalid sBIT chunk";
case SPNG_ESRGB: return "invalid sRGB chunk";
case SPNG_ETEXT: return "invalid tEXt chunk";
case SPNG_ETEXT_KEYWORD: return "invalid tEXt keyword";
case SPNG_EZTXT: return "invalid zTXt chunk";
case SPNG_EZTXT_COMPRESSION_METHOD: return "invalid zTXt compression method";
case SPNG_EITXT: return "invalid iTXt chunk";
case SPNG_EITXT_COMPRESSION_FLAG: return "invalid iTXt compression flag";
case SPNG_EITXT_COMPRESSION_METHOD: return "invalid iTXt compression method";
case SPNG_EITXT_LANG_TAG: return "invalid iTXt language tag";
case SPNG_EITXT_TRANSLATED_KEY: return "invalid iTXt translated key";
case SPNG_EBKGD_NO_PLTE: return "missing palette for bKGD chunk";
case SPNG_EBKGD_PLTE_IDX: return "invalid palette index for bKGD chunk";
case SPNG_EHIST_NO_PLTE: return "missing palette for hIST chunk";
case SPNG_EPHYS: return "invalid pHYs chunk";
case SPNG_ESPLT_NAME: return "invalid suggested palette name";
case SPNG_ESPLT_DUP_NAME: return "duplicate suggested palette (sPLT) name";
case SPNG_ESPLT_DEPTH: return "invalid suggested palette (sPLT) sample depth";
case SPNG_ETIME: return "invalid tIME chunk";
case SPNG_EOFFS: return "invalid oFFs chunk";
case SPNG_EEXIF: return "invalid eXIf chunk";
case SPNG_EIDAT_TOO_SHORT: return "IDAT stream too short";
case SPNG_EIDAT_STREAM: return "IDAT stream error";
case SPNG_EZLIB: return "zlib error";
case SPNG_EFILTER: return "invalid scanline filter";
case SPNG_EBUFSIZ: return "invalid buffer size";
case SPNG_EIO: return "i/o error";
case SPNG_EOF: return "end of file";
case SPNG_EBUF_SET: return "buffer already set";
case SPNG_EBADSTATE: return "non-recoverable state";
case SPNG_EFMT: return "invalid format";
case SPNG_EFLAGS: return "invalid flags";
case SPNG_ECHUNKAVAIL: return "chunk not available";
case SPNG_ENCODE_ONLY: return "encode only context";
case SPNG_EOI: return "reached end-of-image state";
case SPNG_ENOPLTE: return "missing PLTE for indexed image";
case SPNG_ECHUNK_LIMITS: return "reached chunk/cache limits";
case SPNG_EZLIB_INIT: return "zlib init error";
case SPNG_ECHUNK_STDLEN: return "chunk exceeds maximum standard length";
case SPNG_EINTERNAL: return "internal error";
case SPNG_ECTXTYPE: return "invalid operation for context type";
case SPNG_ENOSRC: return "source PNG not set";
case SPNG_ENODST: return "PNG output not set";
case SPNG_EOPSTATE: return "invalid operation for state";
case SPNG_ENOTFINAL: return "PNG not finalized";
default: return "unknown error";
}
}
const char *spng_version_string(void)
{
return SPNG_VERSION_STRING;
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/* The following SIMD optimizations are derived from libpng source code. */
/*
* PNG Reference Library License version 2
*
* Copyright (c) 1995-2019 The PNG Reference Library Authors.
* Copyright (c) 2018-2019 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
*
* The software is supplied "as is", without warranty of any kind,
* express or implied, including, without limitation, the warranties
* of merchantability, fitness for a particular purpose, title, and
* non-infringement. In no event shall the Copyright owners, or
* anyone distributing the software, be liable for any damages or
* other liability, whether in contract, tort or otherwise, arising
* from, out of, or in connection with the software, or the use or
* other dealings in the software, even if advised of the possibility
* of such damage.
*
* Permission is hereby granted to use, copy, modify, and distribute
* this software, or portions hereof, for any purpose, without fee,
* subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you
* use this software in a product, an acknowledgment in the product
* documentation would be appreciated, but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must
* not be misrepresented as being the original software.
*
* 3. This Copyright notice may not be removed or altered from any
* source or altered source distribution.
*/
#if defined(SPNG_X86)
#ifndef SPNG_SSE
#define SPNG_SSE 1
#endif
#if defined(__GNUC__) && !defined(__clang__)
#if SPNG_SSE == 3
#pragma GCC target("ssse3")
#elif SPNG_SSE == 4
#pragma GCC target("sse4.1")
#else
#pragma GCC target("sse2")
#endif
#endif
/* SSE2 optimised filter functions
* Derived from filter_neon_intrinsics.c
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2016-2017 Glenn Randers-Pehrson
* Written by Mike Klein and Matt Sarett
* Derived from arm/filter_neon_intrinsics.c
*
* This code is derived from libpng source code.
* For conditions of distribution and use, see the disclaimer
* and license above.
*/
#include
#include
#include
/* Functions in this file look at most 3 pixels (a,b,c) to predict the 4th (d).
* They're positioned like this:
* prev: c b
* row: a d
* The Sub filter predicts d=a, Avg d=(a+b)/2, and Paeth predicts d to be
* whichever of a, b, or c is closest to p=a+b-c.
*/
static __m128i load4(const void* p)
{
int tmp;
memcpy(&tmp, p, sizeof(tmp));
return _mm_cvtsi32_si128(tmp);
}
static void store4(void* p, __m128i v)
{
int tmp = _mm_cvtsi128_si32(v);
memcpy(p, &tmp, sizeof(int));
}
static __m128i load3(const void* p)
{
uint32_t tmp = 0;
memcpy(&tmp, p, 3);
return _mm_cvtsi32_si128(tmp);
}
static void store3(void* p, __m128i v)
{
int tmp = _mm_cvtsi128_si32(v);
memcpy(p, &tmp, 3);
}
static void defilter_sub3(size_t rowbytes, unsigned char *row)
{
/* The Sub filter predicts each pixel as the previous pixel, a.
* There is no pixel to the left of the first pixel. It's encoded directly.
* That works with our main loop if we just say that left pixel was zero.
*/
size_t rb = rowbytes;
__m128i a, d = _mm_setzero_si128();
while(rb >= 4)
{
a = d; d = load4(row);
d = _mm_add_epi8(d, a);
store3(row, d);
row += 3;
rb -= 3;
}
if(rb > 0)
{
a = d; d = load3(row);
d = _mm_add_epi8(d, a);
store3(row, d);
}
}
static void defilter_sub4(size_t rowbytes, unsigned char *row)
{
/* The Sub filter predicts each pixel as the previous pixel, a.
* There is no pixel to the left of the first pixel. It's encoded directly.
* That works with our main loop if we just say that left pixel was zero.
*/
size_t rb = rowbytes+4;
__m128i a, d = _mm_setzero_si128();
while(rb > 4)
{
a = d; d = load4(row);
d = _mm_add_epi8(d, a);
store4(row, d);
row += 4;
rb -= 4;
}
}
static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev)
{
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
* There's no pixel to the left of the first pixel. Luckily, it's
* predicted to be half of the pixel above it. So again, this works
* perfectly with our loop if we make sure a starts at zero.
*/
size_t rb = rowbytes;
const __m128i zero = _mm_setzero_si128();
__m128i b;
__m128i a, d = zero;
while(rb >= 4)
{
__m128i avg;
b = load4(prev);
a = d; d = load4(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a,b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store3(row, d);
prev += 3;
row += 3;
rb -= 3;
}
if(rb > 0)
{
__m128i avg;
b = load3(prev);
a = d; d = load3(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a, b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store3(row, d);
}
}
static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev)
{
/* The Avg filter predicts each pixel as the (truncated) average of a and b.
* There's no pixel to the left of the first pixel. Luckily, it's
* predicted to be half of the pixel above it. So again, this works
* perfectly with our loop if we make sure a starts at zero.
*/
size_t rb = rowbytes+4;
const __m128i zero = _mm_setzero_si128();
__m128i b;
__m128i a, d = zero;
while(rb > 4)
{
__m128i avg;
b = load4(prev);
a = d; d = load4(row );
/* PNG requires a truncating average, so we can't just use _mm_avg_epu8 */
avg = _mm_avg_epu8(a,b);
/* ...but we can fix it up by subtracting off 1 if it rounded up. */
avg = _mm_sub_epi8(avg, _mm_and_si128(_mm_xor_si128(a, b),
_mm_set1_epi8(1)));
d = _mm_add_epi8(d, avg);
store4(row, d);
prev += 4;
row += 4;
rb -= 4;
}
}
/* Returns |x| for 16-bit lanes. */
#if (SPNG_SSE >= 3) && !defined(_MSC_VER)
__attribute__((target("ssse3")))
#endif
static __m128i abs_i16(__m128i x)
{
#if SPNG_SSE >= 3
return _mm_abs_epi16(x);
#else
/* Read this all as, return x<0 ? -x : x.
* To negate two's complement, you flip all the bits then add 1.
*/
__m128i is_negative = _mm_cmplt_epi16(x, _mm_setzero_si128());
/* Flip negative lanes. */
x = _mm_xor_si128(x, is_negative);
/* +1 to negative lanes, else +0. */
x = _mm_sub_epi16(x, is_negative);
return x;
#endif
}
/* Bytewise c ? t : e. */
static __m128i if_then_else(__m128i c, __m128i t, __m128i e)
{
#if SPNG_SSE >= 4
return _mm_blendv_epi8(e, t, c);
#else
return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e));
#endif
}
static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev)
{
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
* and two pixels from the previous row, b and c:
* prev: c b
* row: a d
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
* p=a+b-c.
*
* The first pixel has no left context, and so uses an Up filter, p = b.
* This works naturally with our main loop's p = a+b-c if we force a and c
* to zero.
* Here we zero b and d, which become c and a respectively at the start of
* the loop.
*/
size_t rb = rowbytes;
const __m128i zero = _mm_setzero_si128();
__m128i c, b = zero,
a, d = zero;
while(rb >= 4)
{
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
__m128i pa,pb,pc,smallest,nearest;
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b, c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a, c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa, pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store3(row, _mm_packus_epi16(d, d));
prev += 3;
row += 3;
rb -= 3;
}
if(rb > 0)
{
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
__m128i pa, pb, pc, smallest, nearest;
c = b; b = _mm_unpacklo_epi8(load3(prev), zero);
a = d; d = _mm_unpacklo_epi8(load3(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b, c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a, c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa, pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store3(row, _mm_packus_epi16(d, d));
}
}
static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev)
{
/* Paeth tries to predict pixel d using the pixel to the left of it, a,
* and two pixels from the previous row, b and c:
* prev: c b
* row: a d
* The Paeth function predicts d to be whichever of a, b, or c is nearest to
* p=a+b-c.
*
* The first pixel has no left context, and so uses an Up filter, p = b.
* This works naturally with our main loop's p = a+b-c if we force a and c
* to zero.
* Here we zero b and d, which become c and a respectively at the start of
* the loop.
*/
size_t rb = rowbytes+4;
const __m128i zero = _mm_setzero_si128();
__m128i pa, pb, pc, smallest, nearest;
__m128i c, b = zero,
a, d = zero;
while(rb > 4)
{
/* It's easiest to do this math (particularly, deal with pc) with 16-bit
* intermediates.
*/
c = b; b = _mm_unpacklo_epi8(load4(prev), zero);
a = d; d = _mm_unpacklo_epi8(load4(row ), zero);
/* (p-a) == (a+b-c - a) == (b-c) */
pa = _mm_sub_epi16(b, c);
/* (p-b) == (a+b-c - b) == (a-c) */
pb = _mm_sub_epi16(a, c);
/* (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) */
pc = _mm_add_epi16(pa, pb);
pa = abs_i16(pa); /* |p-a| */
pb = abs_i16(pb); /* |p-b| */
pc = abs_i16(pc); /* |p-c| */
smallest = _mm_min_epi16(pc, _mm_min_epi16(pa, pb));
/* Paeth breaks ties favoring a over b over c. */
nearest = if_then_else(_mm_cmpeq_epi16(smallest, pa), a,
if_then_else(_mm_cmpeq_epi16(smallest, pb), b, c));
/* Note `_epi8`: we need addition to wrap modulo 255. */
d = _mm_add_epi8(d, nearest);
store4(row, _mm_packus_epi16(d, d));
prev += 4;
row += 4;
rb -= 4;
}
}
#endif /* SPNG_X86 */
#if defined(SPNG_ARM)
/* NEON optimised filter functions
* Derived from filter_neon_intrinsics.c
*
* Copyright (c) 2018 Cosmin Truta
* Copyright (c) 2014,2016 Glenn Randers-Pehrson
* Written by James Yu , October 2013.
* Based on filter_neon.S, written by Mans Rullgard, 2011.
*
* This code is derived from libpng source code.
* For conditions of distribution and use, see the disclaimer
* and license in this file.
*/
#define png_aligncast(type, value) ((void*)(value))
#define png_aligncastconst(type, value) ((const void*)(value))
/* libpng row pointers are not necessarily aligned to any particular boundary,
* however this code will only work with appropriate alignment. mips/mips_init.c
* checks for this (and will not compile unless it is done). This code uses
* variants of png_aligncast to avoid compiler warnings.
*/
#define png_ptr(type,pointer) png_aligncast(type *,pointer)
#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer)
/* The following relies on a variable 'temp_pointer' being declared with type
* 'type'. This is written this way just to hide the GCC strict aliasing
* warning; note that the code is safe because there never is an alias between
* the input and output pointers.
*/
#define png_ldr(type,pointer)\
(temp_pointer = png_ptr(type,pointer), *temp_pointer)
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_ARM64)
#include
#else
#include
#endif
static void defilter_sub3(size_t rowbytes, unsigned char *row)
{
unsigned char *rp = row;
unsigned char *rp_stop = row + rowbytes;
uint8x16_t vtmp = vld1q_u8(rp);
uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp);
uint8x8x2_t vrp = *vrpt;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
for (; rp < rp_stop;)
{
uint8x8_t vtmp1, vtmp2;
uint32x2_t *temp_pointer;
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6);
vdest.val[1] = vadd_u8(vdest.val[0], vtmp1);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vdest.val[2] = vadd_u8(vdest.val[1], vtmp2);
vdest.val[3] = vadd_u8(vdest.val[2], vtmp1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t, &vtmp);
vrp = *vrpt;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
}
static void defilter_sub4(size_t rowbytes, unsigned char *row)
{
unsigned char *rp = row;
unsigned char *rp_stop = row + rowbytes;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
for (; rp < rp_stop; rp += 16)
{
uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp));
uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp);
uint8x8x4_t vrp = *vrpt;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]);
vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]);
vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]);
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
}
static void defilter_avg3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)
{
unsigned char *rp = row;
const unsigned char *pp = prev_row;
unsigned char *rp_stop = row + rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
uint8x8x2_t vrp;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
vtmp = vld1q_u8(rp);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
for (; rp < rp_stop; pp += 12)
{
uint8x8_t vtmp1, vtmp2, vtmp3;
uint8x8x2_t *vppt;
uint8x8x2_t vpp;
uint32x2_t *temp_pointer;
vtmp = vld1q_u8(pp);
vppt = png_ptr(uint8x8x2_t,&vtmp);
vpp = *vppt;
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
vtmp3 = vext_u8(vrp.val[0], vrp.val[1], 6);
vdest.val[1] = vhadd_u8(vdest.val[0], vtmp2);
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 6);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2);
vdest.val[2] = vadd_u8(vdest.val[2], vtmp3);
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2);
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
}
static void defilter_avg4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)
{
unsigned char *rp = row;
unsigned char *rp_stop = row + rowbytes;
const unsigned char *pp = prev_row;
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
for (; rp < rp_stop; rp += 16, pp += 16)
{
uint32x2x4_t vtmp;
uint8x8x4_t *vrpt, *vppt;
uint8x8x4_t vrp, vpp;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vtmp = vld4_u32(png_ptr(uint32_t,rp));
vrpt = png_ptr(uint8x8x4_t,&vtmp);
vrp = *vrpt;
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
vppt = png_ptr(uint8x8x4_t,&vtmp);
vpp = *vppt;
vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vdest.val[1] = vhadd_u8(vdest.val[0], vpp.val[1]);
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
vdest.val[2] = vhadd_u8(vdest.val[1], vpp.val[2]);
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]);
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
}
static uint8x8_t paeth_arm(uint8x8_t a, uint8x8_t b, uint8x8_t c)
{
uint8x8_t d, e;
uint16x8_t p1, pa, pb, pc;
p1 = vaddl_u8(a, b); /* a + b */
pc = vaddl_u8(c, c); /* c * 2 */
pa = vabdl_u8(b, c); /* pa */
pb = vabdl_u8(a, c); /* pb */
pc = vabdq_u16(p1, pc); /* pc */
p1 = vcleq_u16(pa, pb); /* pa <= pb */
pa = vcleq_u16(pa, pc); /* pa <= pc */
pb = vcleq_u16(pb, pc); /* pb <= pc */
p1 = vandq_u16(p1, pa); /* pa <= pb && pa <= pc */
d = vmovn_u16(pb);
e = vmovn_u16(p1);
d = vbsl_u8(d, b, c);
e = vbsl_u8(e, a, d);
return e;
}
static void defilter_paeth3(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)
{
unsigned char *rp = row;
const unsigned char *pp = prev_row;
unsigned char *rp_stop = row + rowbytes;
uint8x16_t vtmp;
uint8x8x2_t *vrpt;
uint8x8x2_t vrp;
uint8x8_t vlast = vdup_n_u8(0);
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
vtmp = vld1q_u8(rp);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
for (; rp < rp_stop; pp += 12)
{
uint8x8x2_t *vppt;
uint8x8x2_t vpp;
uint8x8_t vtmp1, vtmp2, vtmp3;
uint32x2_t *temp_pointer;
vtmp = vld1q_u8(pp);
vppt = png_ptr(uint8x8x2_t,&vtmp);
vpp = *vppt;
vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3);
vtmp2 = vext_u8(vpp.val[0], vpp.val[1], 3);
vdest.val[1] = paeth_arm(vdest.val[0], vtmp2, vpp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[1], vtmp1);
vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 6);
vtmp3 = vext_u8(vpp.val[0], vpp.val[1], 6);
vdest.val[2] = paeth_arm(vdest.val[1], vtmp3, vtmp2);
vdest.val[2] = vadd_u8(vdest.val[2], vtmp1);
vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1);
vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1);
vtmp = vld1q_u8(rp + 12);
vrpt = png_ptr(uint8x8x2_t,&vtmp);
vrp = *vrpt;
vdest.val[3] = paeth_arm(vdest.val[2], vtmp2, vtmp3);
vdest.val[3] = vadd_u8(vdest.val[3], vtmp1);
vlast = vtmp2;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0);
rp += 3;
vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0);
rp += 3;
}
}
static void defilter_paeth4(size_t rowbytes, unsigned char *row, const unsigned char *prev_row)
{
unsigned char *rp = row;
unsigned char *rp_stop = row + rowbytes;
const unsigned char *pp = prev_row;
uint8x8_t vlast = vdup_n_u8(0);
uint8x8x4_t vdest;
vdest.val[3] = vdup_n_u8(0);
for (; rp < rp_stop; rp += 16, pp += 16)
{
uint32x2x4_t vtmp;
uint8x8x4_t *vrpt, *vppt;
uint8x8x4_t vrp, vpp;
uint32x2x4_t *temp_pointer;
uint32x2x4_t vdest_val;
vtmp = vld4_u32(png_ptr(uint32_t,rp));
vrpt = png_ptr(uint8x8x4_t,&vtmp);
vrp = *vrpt;
vtmp = vld4_u32(png_ptrc(uint32_t,pp));
vppt = png_ptr(uint8x8x4_t,&vtmp);
vpp = *vppt;
vdest.val[0] = paeth_arm(vdest.val[3], vpp.val[0], vlast);
vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]);
vdest.val[1] = paeth_arm(vdest.val[0], vpp.val[1], vpp.val[0]);
vdest.val[1] = vadd_u8(vdest.val[1], vrp.val[1]);
vdest.val[2] = paeth_arm(vdest.val[1], vpp.val[2], vpp.val[1]);
vdest.val[2] = vadd_u8(vdest.val[2], vrp.val[2]);
vdest.val[3] = paeth_arm(vdest.val[2], vpp.val[3], vpp.val[2]);
vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]);
vlast = vpp.val[3];
vdest_val = png_ldr(uint32x2x4_t, &vdest);
vst4_lane_u32(png_ptr(uint32_t,rp), vdest_val, 0);
}
}
/* NEON optimised palette expansion functions
* Derived from palette_neon_intrinsics.c
*
* Copyright (c) 2018-2019 Cosmin Truta
* Copyright (c) 2017-2018 Arm Holdings. All rights reserved.
* Written by Richard Townsend , February 2017.
*
* This code is derived from libpng source code.
* For conditions of distribution and use, see the disclaimer
* and license in this file.
*
* Related: https://developer.arm.com/documentation/101964/latest/Color-palette-expansion
*
* The functions were refactored to iterate forward.
*
*/
/* Expands a palettized row into RGBA8. */
static uint32_t expand_palette_rgba8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width)
{
const uint32_t scanline_stride = 4;
const uint32_t row_stride = scanline_stride * 4;
const uint32_t count = width / scanline_stride;
const uint32_t *palette = (const uint32_t*)plte;
if(!count) return 0;
uint32_t i;
uint32x4_t cur;
for(i=0; i < count; i++, scanline += scanline_stride)
{
cur = vld1q_dup_u32 (palette + scanline[0]);
cur = vld1q_lane_u32(palette + scanline[1], cur, 1);
cur = vld1q_lane_u32(palette + scanline[2], cur, 2);
cur = vld1q_lane_u32(palette + scanline[3], cur, 3);
vst1q_u32((uint32_t*)(row + i * row_stride), cur);
}
return count * scanline_stride;
}
/* Expands a palettized row into RGB8. */
static uint32_t expand_palette_rgb8_neon(unsigned char *row, const unsigned char *scanline, const unsigned char *plte, uint32_t width)
{
const uint32_t scanline_stride = 8;
const uint32_t row_stride = scanline_stride * 3;
const uint32_t count = width / scanline_stride;
if(!count) return 0;
uint32_t i;
uint8x8x3_t cur;
for(i=0; i < count; i++, scanline += scanline_stride)
{
cur = vld3_dup_u8 (plte + 3 * scanline[0]);
cur = vld3_lane_u8(plte + 3 * scanline[1], cur, 1);
cur = vld3_lane_u8(plte + 3 * scanline[2], cur, 2);
cur = vld3_lane_u8(plte + 3 * scanline[3], cur, 3);
cur = vld3_lane_u8(plte + 3 * scanline[4], cur, 4);
cur = vld3_lane_u8(plte + 3 * scanline[5], cur, 5);
cur = vld3_lane_u8(plte + 3 * scanline[6], cur, 6);
cur = vld3_lane_u8(plte + 3 * scanline[7], cur, 7);
vst3_u8(row + i * row_stride, cur);
}
return count * scanline_stride;
}
#endif /* SPNG_ARM */
================================================
FILE: jni/libspng/spng.h
================================================
/* SPDX-License-Identifier: BSD-2-Clause */
#ifndef SPNG_H
#define SPNG_H
#ifdef __cplusplus
extern "C" {
#endif
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(SPNG_STATIC)
#if defined(SPNG__BUILD)
#define SPNG_API __declspec(dllexport)
#else
#define SPNG_API __declspec(dllimport)
#endif
#else
#define SPNG_API
#endif
#if defined(_MSC_VER)
#define SPNG_CDECL __cdecl
#else
#define SPNG_CDECL
#endif
#include
#include
#include
#define SPNG_VERSION_MAJOR 0
#define SPNG_VERSION_MINOR 7
#define SPNG_VERSION_PATCH 3
enum spng_errno
{
SPNG_IO_ERROR = -2,
SPNG_IO_EOF = -1,
SPNG_OK = 0,
SPNG_EINVAL,
SPNG_EMEM,
SPNG_EOVERFLOW,
SPNG_ESIGNATURE,
SPNG_EWIDTH,
SPNG_EHEIGHT,
SPNG_EUSER_WIDTH,
SPNG_EUSER_HEIGHT,
SPNG_EBIT_DEPTH,
SPNG_ECOLOR_TYPE,
SPNG_ECOMPRESSION_METHOD,
SPNG_EFILTER_METHOD,
SPNG_EINTERLACE_METHOD,
SPNG_EIHDR_SIZE,
SPNG_ENOIHDR,
SPNG_ECHUNK_POS,
SPNG_ECHUNK_SIZE,
SPNG_ECHUNK_CRC,
SPNG_ECHUNK_TYPE,
SPNG_ECHUNK_UNKNOWN_CRITICAL,
SPNG_EDUP_PLTE,
SPNG_EDUP_CHRM,
SPNG_EDUP_GAMA,
SPNG_EDUP_ICCP,
SPNG_EDUP_SBIT,
SPNG_EDUP_SRGB,
SPNG_EDUP_BKGD,
SPNG_EDUP_HIST,
SPNG_EDUP_TRNS,
SPNG_EDUP_PHYS,
SPNG_EDUP_TIME,
SPNG_EDUP_OFFS,
SPNG_EDUP_EXIF,
SPNG_ECHRM,
SPNG_EPLTE_IDX,
SPNG_ETRNS_COLOR_TYPE,
SPNG_ETRNS_NO_PLTE,
SPNG_EGAMA,
SPNG_EICCP_NAME,
SPNG_EICCP_COMPRESSION_METHOD,
SPNG_ESBIT,
SPNG_ESRGB,
SPNG_ETEXT,
SPNG_ETEXT_KEYWORD,
SPNG_EZTXT,
SPNG_EZTXT_COMPRESSION_METHOD,
SPNG_EITXT,
SPNG_EITXT_COMPRESSION_FLAG,
SPNG_EITXT_COMPRESSION_METHOD,
SPNG_EITXT_LANG_TAG,
SPNG_EITXT_TRANSLATED_KEY,
SPNG_EBKGD_NO_PLTE,
SPNG_EBKGD_PLTE_IDX,
SPNG_EHIST_NO_PLTE,
SPNG_EPHYS,
SPNG_ESPLT_NAME,
SPNG_ESPLT_DUP_NAME,
SPNG_ESPLT_DEPTH,
SPNG_ETIME,
SPNG_EOFFS,
SPNG_EEXIF,
SPNG_EIDAT_TOO_SHORT,
SPNG_EIDAT_STREAM,
SPNG_EZLIB,
SPNG_EFILTER,
SPNG_EBUFSIZ,
SPNG_EIO,
SPNG_EOF,
SPNG_EBUF_SET,
SPNG_EBADSTATE,
SPNG_EFMT,
SPNG_EFLAGS,
SPNG_ECHUNKAVAIL,
SPNG_ENCODE_ONLY,
SPNG_EOI,
SPNG_ENOPLTE,
SPNG_ECHUNK_LIMITS,
SPNG_EZLIB_INIT,
SPNG_ECHUNK_STDLEN,
SPNG_EINTERNAL,
SPNG_ECTXTYPE,
SPNG_ENOSRC,
SPNG_ENODST,
SPNG_EOPSTATE,
SPNG_ENOTFINAL,
};
enum spng_text_type
{
SPNG_TEXT = 1,
SPNG_ZTXT = 2,
SPNG_ITXT = 3
};
enum spng_color_type
{
SPNG_COLOR_TYPE_GRAYSCALE = 0,
SPNG_COLOR_TYPE_TRUECOLOR = 2,
SPNG_COLOR_TYPE_INDEXED = 3,
SPNG_COLOR_TYPE_GRAYSCALE_ALPHA = 4,
SPNG_COLOR_TYPE_TRUECOLOR_ALPHA = 6
};
enum spng_filter
{
SPNG_FILTER_NONE = 0,
SPNG_FILTER_SUB = 1,
SPNG_FILTER_UP = 2,
SPNG_FILTER_AVERAGE = 3,
SPNG_FILTER_PAETH = 4
};
enum spng_filter_choice
{
SPNG_DISABLE_FILTERING = 0,
SPNG_FILTER_CHOICE_NONE = 8,
SPNG_FILTER_CHOICE_SUB = 16,
SPNG_FILTER_CHOICE_UP = 32,
SPNG_FILTER_CHOICE_AVG = 64,
SPNG_FILTER_CHOICE_PAETH = 128,
SPNG_FILTER_CHOICE_ALL = (8|16|32|64|128)
};
enum spng_interlace_method
{
SPNG_INTERLACE_NONE = 0,
SPNG_INTERLACE_ADAM7 = 1
};
/* Channels are always in byte-order */
enum spng_format
{
SPNG_FMT_RGBA8 = 1,
SPNG_FMT_RGBA16 = 2,
SPNG_FMT_RGB8 = 4,
/* Partially implemented, see documentation */
SPNG_FMT_GA8 = 16,
SPNG_FMT_GA16 = 32,
SPNG_FMT_G8 = 64,
/* No conversion or scaling */
SPNG_FMT_PNG = 256,
SPNG_FMT_RAW = 512 /* big-endian (everything else is host-endian) */
};
enum spng_ctx_flags
{
SPNG_CTX_IGNORE_ADLER32 = 1, /* Ignore checksum in DEFLATE streams */
SPNG_CTX_ENCODER = 2 /* Create an encoder context */
};
enum spng_decode_flags
{
SPNG_DECODE_USE_TRNS = 1, /* Deprecated */
SPNG_DECODE_USE_GAMA = 2, /* Deprecated */
SPNG_DECODE_USE_SBIT = 8, /* Undocumented */
SPNG_DECODE_TRNS = 1, /* Apply transparency */
SPNG_DECODE_GAMMA = 2, /* Apply gamma correction */
SPNG_DECODE_PROGRESSIVE = 256 /* Initialize for progressive reads */
};
enum spng_crc_action
{
/* Default for critical chunks */
SPNG_CRC_ERROR = 0,
/* Discard chunk, invalid for critical chunks.
Since v0.6.2: default for ancillary chunks */
SPNG_CRC_DISCARD = 1,
/* Ignore and don't calculate checksum.
Since v0.6.2: also ignores checksums in DEFLATE streams */
SPNG_CRC_USE = 2
};
enum spng_encode_flags
{
SPNG_ENCODE_PROGRESSIVE = 1, /* Initialize for progressive writes */
SPNG_ENCODE_FINALIZE = 2, /* Finalize PNG after encoding image */
};
struct spng_ihdr
{
uint32_t width;
uint32_t height;
uint8_t bit_depth;
uint8_t color_type;
uint8_t compression_method;
uint8_t filter_method;
uint8_t interlace_method;
};
struct spng_plte_entry
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha; /* Reserved for internal use */
};
struct spng_plte
{
uint32_t n_entries;
struct spng_plte_entry entries[256];
};
struct spng_trns
{
uint16_t gray;
uint16_t red;
uint16_t green;
uint16_t blue;
uint32_t n_type3_entries;
uint8_t type3_alpha[256];
};
struct spng_chrm_int
{
uint32_t white_point_x;
uint32_t white_point_y;
uint32_t red_x;
uint32_t red_y;
uint32_t green_x;
uint32_t green_y;
uint32_t blue_x;
uint32_t blue_y;
};
struct spng_chrm
{
double white_point_x;
double white_point_y;
double red_x;
double red_y;
double green_x;
double green_y;
double blue_x;
double blue_y;
};
struct spng_iccp
{
char profile_name[80];
size_t profile_len;
char *profile;
};
struct spng_sbit
{
uint8_t grayscale_bits;
uint8_t red_bits;
uint8_t green_bits;
uint8_t blue_bits;
uint8_t alpha_bits;
};
struct spng_text
{
char keyword[80];
int type;
size_t length;
char *text;
uint8_t compression_flag; /* iTXt only */
uint8_t compression_method; /* iTXt, ztXt only */
char *language_tag; /* iTXt only */
char *translated_keyword; /* iTXt only */
};
struct spng_bkgd
{
uint16_t gray; /* Only for gray/gray alpha */
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t plte_index; /* Only for indexed color */
};
struct spng_hist
{
uint16_t frequency[256];
};
struct spng_phys
{
uint32_t ppu_x, ppu_y;
uint8_t unit_specifier;
};
struct spng_splt_entry
{
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t alpha;
uint16_t frequency;
};
struct spng_splt
{
char name[80];
uint8_t sample_depth;
uint32_t n_entries;
struct spng_splt_entry *entries;
};
struct spng_time
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
struct spng_offs
{
int32_t x, y;
uint8_t unit_specifier;
};
struct spng_exif
{
size_t length;
char *data;
};
struct spng_chunk
{
size_t offset;
uint32_t length;
uint8_t type[4];
uint32_t crc;
};
enum spng_location
{
SPNG_AFTER_IHDR = 1,
SPNG_AFTER_PLTE = 2,
SPNG_AFTER_IDAT = 8,
};
struct spng_unknown_chunk
{
uint8_t type[4];
size_t length;
void *data;
enum spng_location location;
};
enum spng_option
{
SPNG_KEEP_UNKNOWN_CHUNKS = 1,
SPNG_IMG_COMPRESSION_LEVEL,
SPNG_IMG_WINDOW_BITS,
SPNG_IMG_MEM_LEVEL,
SPNG_IMG_COMPRESSION_STRATEGY,
SPNG_TEXT_COMPRESSION_LEVEL,
SPNG_TEXT_WINDOW_BITS,
SPNG_TEXT_MEM_LEVEL,
SPNG_TEXT_COMPRESSION_STRATEGY,
SPNG_FILTER_CHOICE,
SPNG_CHUNK_COUNT_LIMIT,
SPNG_ENCODE_TO_BUFFER,
};
typedef void* SPNG_CDECL spng_malloc_fn(size_t size);
typedef void* SPNG_CDECL spng_realloc_fn(void* ptr, size_t size);
typedef void* SPNG_CDECL spng_calloc_fn(size_t count, size_t size);
typedef void SPNG_CDECL spng_free_fn(void* ptr);
struct spng_alloc
{
spng_malloc_fn *malloc_fn;
spng_realloc_fn *realloc_fn;
spng_calloc_fn *calloc_fn;
spng_free_fn *free_fn;
};
struct spng_row_info
{
uint32_t scanline_idx;
uint32_t row_num; /* deinterlaced row index */
int pass;
uint8_t filter;
};
typedef struct spng_ctx spng_ctx;
typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length);
typedef int spng_write_fn(spng_ctx *ctx, void *user, void *src, size_t length);
typedef int spng_rw_fn(spng_ctx *ctx, void *user, void *dst_src, size_t length);
SPNG_API spng_ctx *spng_ctx_new(int flags);
SPNG_API spng_ctx *spng_ctx_new2(struct spng_alloc *alloc, int flags);
SPNG_API void spng_ctx_free(spng_ctx *ctx);
SPNG_API int spng_set_png_buffer(spng_ctx *ctx, const void *buf, size_t size);
SPNG_API int spng_set_png_stream(spng_ctx *ctx, spng_rw_fn *rw_func, void *user);
SPNG_API int spng_set_png_file(spng_ctx *ctx, FILE *file);
SPNG_API void *spng_get_png_buffer(spng_ctx *ctx, size_t *len, int *error);
SPNG_API int spng_set_image_limits(spng_ctx *ctx, uint32_t width, uint32_t height);
SPNG_API int spng_get_image_limits(spng_ctx *ctx, uint32_t *width, uint32_t *height);
SPNG_API int spng_set_chunk_limits(spng_ctx *ctx, size_t chunk_size, size_t cache_size);
SPNG_API int spng_get_chunk_limits(spng_ctx *ctx, size_t *chunk_size, size_t *cache_size);
SPNG_API int spng_set_crc_action(spng_ctx *ctx, int critical, int ancillary);
SPNG_API int spng_set_option(spng_ctx *ctx, enum spng_option option, int value);
SPNG_API int spng_get_option(spng_ctx *ctx, enum spng_option option, int *value);
SPNG_API int spng_decoded_image_size(spng_ctx *ctx, int fmt, size_t *len);
/* Decode */
SPNG_API int spng_decode_image(spng_ctx *ctx, void *out, size_t len, int fmt, int flags);
/* Progressive decode */
SPNG_API int spng_decode_scanline(spng_ctx *ctx, void *out, size_t len);
SPNG_API int spng_decode_row(spng_ctx *ctx, void *out, size_t len);
SPNG_API int spng_decode_chunks(spng_ctx *ctx);
/* Encode/decode */
SPNG_API int spng_get_row_info(spng_ctx *ctx, struct spng_row_info *row_info);
/* Encode */
SPNG_API int spng_encode_image(spng_ctx *ctx, const void *img, size_t len, int fmt, int flags);
/* Progressive encode */
SPNG_API int spng_encode_scanline(spng_ctx *ctx, const void *scanline, size_t len);
SPNG_API int spng_encode_row(spng_ctx *ctx, const void *row, size_t len);
SPNG_API int spng_encode_chunks(spng_ctx *ctx);
SPNG_API int spng_get_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_get_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_get_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_get_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_get_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_get_gama(spng_ctx *ctx, double *gamma);
SPNG_API int spng_get_gama_int(spng_ctx *ctx, uint32_t *gama_int);
SPNG_API int spng_get_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_get_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_get_srgb(spng_ctx *ctx, uint8_t *rendering_intent);
SPNG_API int spng_get_text(spng_ctx *ctx, struct spng_text *text, uint32_t *n_text);
SPNG_API int spng_get_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_get_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_get_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_get_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t *n_splt);
SPNG_API int spng_get_time(spng_ctx *ctx, struct spng_time *time);
SPNG_API int spng_get_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t *n_chunks);
/* Official extensions */
SPNG_API int spng_get_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_get_exif(spng_ctx *ctx, struct spng_exif *exif);
SPNG_API int spng_set_ihdr(spng_ctx *ctx, struct spng_ihdr *ihdr);
SPNG_API int spng_set_plte(spng_ctx *ctx, struct spng_plte *plte);
SPNG_API int spng_set_trns(spng_ctx *ctx, struct spng_trns *trns);
SPNG_API int spng_set_chrm(spng_ctx *ctx, struct spng_chrm *chrm);
SPNG_API int spng_set_chrm_int(spng_ctx *ctx, struct spng_chrm_int *chrm_int);
SPNG_API int spng_set_gama(spng_ctx *ctx, double gamma);
SPNG_API int spng_set_gama_int(spng_ctx *ctx, uint32_t gamma);
SPNG_API int spng_set_iccp(spng_ctx *ctx, struct spng_iccp *iccp);
SPNG_API int spng_set_sbit(spng_ctx *ctx, struct spng_sbit *sbit);
SPNG_API int spng_set_srgb(spng_ctx *ctx, uint8_t rendering_intent);
SPNG_API int spng_set_text(spng_ctx *ctx, struct spng_text *text, uint32_t n_text);
SPNG_API int spng_set_bkgd(spng_ctx *ctx, struct spng_bkgd *bkgd);
SPNG_API int spng_set_hist(spng_ctx *ctx, struct spng_hist *hist);
SPNG_API int spng_set_phys(spng_ctx *ctx, struct spng_phys *phys);
SPNG_API int spng_set_splt(spng_ctx *ctx, struct spng_splt *splt, uint32_t n_splt);
SPNG_API int spng_set_time(spng_ctx *ctx, struct spng_time *time);
SPNG_API int spng_set_unknown_chunks(spng_ctx *ctx, struct spng_unknown_chunk *chunks, uint32_t n_chunks);
/* Official extensions */
SPNG_API int spng_set_offs(spng_ctx *ctx, struct spng_offs *offs);
SPNG_API int spng_set_exif(spng_ctx *ctx, struct spng_exif *exif);
SPNG_API const char *spng_strerror(int err);
SPNG_API const char *spng_version_string(void);
#ifdef __cplusplus
}
#endif
#endif /* SPNG_H */
================================================
FILE: jni/lz4/lz4.c
================================================
/*
LZ4 - Fast LZ compression algorithm
Copyright (C) 2011-2020, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
/*-************************************
* Tuning parameters
**************************************/
/*
* LZ4_HEAPMODE :
* Select how stateless compression functions like `LZ4_compress_default()`
* allocate memory for their hash table,
* in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
*/
#ifndef LZ4_HEAPMODE
# define LZ4_HEAPMODE 0
#endif
/*
* LZ4_ACCELERATION_DEFAULT :
* Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
*/
#define LZ4_ACCELERATION_DEFAULT 1
/*
* LZ4_ACCELERATION_MAX :
* Any "acceleration" value higher than this threshold
* get treated as LZ4_ACCELERATION_MAX instead (fix #876)
*/
#define LZ4_ACCELERATION_MAX 65537
/*-************************************
* CPU Feature Detection
**************************************/
/* LZ4_FORCE_MEMORY_ACCESS
* By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
* Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
* The below switch allow to select different access method for improved performance.
* Method 0 (default) : use `memcpy()`. Safe and portable.
* Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
* This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
* Method 2 : direct access. This method is portable but violate C standard.
* It can generate buggy code on targets which assembly generation depends on alignment.
* But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
* See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
* Prefer these methods in priority order (0 > 1 > 2)
*/
#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */
# if defined(__GNUC__) && \
( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \
|| defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
# define LZ4_FORCE_MEMORY_ACCESS 2
# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)
# define LZ4_FORCE_MEMORY_ACCESS 1
# endif
#endif
/*
* LZ4_FORCE_SW_BITCOUNT
* Define this parameter if your target system or compiler does not support hardware bit count
*/
#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */
# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */
# define LZ4_FORCE_SW_BITCOUNT
#endif
/*-************************************
* Dependency
**************************************/
/*
* LZ4_SRC_INCLUDED:
* Amalgamation flag, whether lz4.c is included
*/
#ifndef LZ4_SRC_INCLUDED
# define LZ4_SRC_INCLUDED 1
#endif
#ifndef LZ4_STATIC_LINKING_ONLY
#define LZ4_STATIC_LINKING_ONLY
#endif
#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS
#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */
#endif
#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */
#include "lz4.h"
/* see also "memory routines" below */
/*-************************************
* Compiler Options
**************************************/
#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */
# include /* only present in VS2005+ */
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */
#endif /* _MSC_VER */
#ifndef LZ4_FORCE_INLINE
# ifdef _MSC_VER /* Visual Studio */
# define LZ4_FORCE_INLINE static __forceinline
# else
# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
# ifdef __GNUC__
# define LZ4_FORCE_INLINE static inline __attribute__((always_inline))
# else
# define LZ4_FORCE_INLINE static inline
# endif
# else
# define LZ4_FORCE_INLINE static
# endif /* __STDC_VERSION__ */
# endif /* _MSC_VER */
#endif /* LZ4_FORCE_INLINE */
/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE
* gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,
* together with a simple 8-byte copy loop as a fall-back path.
* However, this optimization hurts the decompression speed by >30%,
* because the execution does not go to the optimized loop
* for typical compressible data, and all of the preamble checks
* before going to the fall-back path become useless overhead.
* This optimization happens only with the -O3 flag, and -O2 generates
* a simple 8-byte copy loop.
* With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8
* functions are annotated with __attribute__((optimize("O2"))),
* and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute
* of LZ4_wildCopy8 does not affect the compression speed.
*/
#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)
# define LZ4_FORCE_O2 __attribute__((optimize("O2")))
# undef LZ4_FORCE_INLINE
# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline))
#else
# define LZ4_FORCE_O2
#endif
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
# define expect(expr,value) (__builtin_expect ((expr),(value)) )
#else
# define expect(expr,value) (expr)
#endif
#ifndef likely
#define likely(expr) expect((expr) != 0, 1)
#endif
#ifndef unlikely
#define unlikely(expr) expect((expr) != 0, 0)
#endif
/* Should the alignment test prove unreliable, for some reason,
* it can be disabled by setting LZ4_ALIGN_TEST to 0 */
#ifndef LZ4_ALIGN_TEST /* can be externally provided */
# define LZ4_ALIGN_TEST 1
#endif
/*-************************************
* Memory routines
**************************************/
/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :
* Disable relatively high-level LZ4/HC functions that use dynamic memory
* allocation functions (malloc(), calloc(), free()).
*
* Note that this is a compile-time switch. And since it disables
* public/stable LZ4 v1 API functions, we don't recommend using this
* symbol to generate a library for distribution.
*
* The following public functions are removed when this symbol is defined.
* - lz4 : LZ4_createStream, LZ4_freeStream,
* LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)
* - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,
* LZ4_createHC (deprecated), LZ4_freeHC (deprecated)
* - lz4frame, lz4file : All LZ4F_* functions
*/
#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
# define ALLOC(s) lz4_error_memory_allocation_is_disabled
# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled
# define FREEMEM(p) lz4_error_memory_allocation_is_disabled
#elif defined(LZ4_USER_MEMORY_FUNCTIONS)
/* memory management functions can be customized by user project.
* Below functions must exist somewhere in the Project
* and be available at link time */
void* LZ4_malloc(size_t s);
void* LZ4_calloc(size_t n, size_t s);
void LZ4_free(void* p);
# define ALLOC(s) LZ4_malloc(s)
# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)
# define FREEMEM(p) LZ4_free(p)
#else
# include /* malloc, calloc, free */
# define ALLOC(s) malloc(s)
# define ALLOC_AND_ZERO(s) calloc(1,s)
# define FREEMEM(p) free(p)
#endif
#if ! LZ4_FREESTANDING
# include /* memset, memcpy */
#endif
#if !defined(LZ4_memset)
# define LZ4_memset(p,v,s) memset((p),(v),(s))
#endif
#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s))
/*-************************************
* Common Constants
**************************************/
#define MINMATCH 4
#define WILDCOPYLENGTH 8
#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */
#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */
#define FASTLOOP_SAFE_DISTANCE 64
static const int LZ4_minLength = (MFLIMIT+1);
#define KB *(1 <<10)
#define MB *(1 <<20)
#define GB *(1U<<30)
#define LZ4_DISTANCE_ABSOLUTE_MAX 65535
#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */
# error "LZ4_DISTANCE_MAX is too big : must be <= 65535"
#endif
#define ML_BITS 4
#define ML_MASK ((1U<=1)
# include
#else
# ifndef assert
# define assert(condition) ((void)0)
# endif
#endif
#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */
#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)
# include
static int g_debuglog_enable = 1;
# define DEBUGLOG(l, ...) { \
if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \
fprintf(stderr, __FILE__ " %i: ", __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, " \n"); \
} }
#else
# define DEBUGLOG(l, ...) {} /* disabled */
#endif
static int LZ4_isAligned(const void* ptr, size_t alignment)
{
return ((size_t)ptr & (alignment -1)) == 0;
}
/*-************************************
* Types
**************************************/
#include
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# include
typedef uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
typedef int32_t S32;
typedef uint64_t U64;
typedef uintptr_t uptrval;
#else
# if UINT_MAX != 4294967295UL
# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4"
# endif
typedef unsigned char BYTE;
typedef unsigned short U16;
typedef unsigned int U32;
typedef signed int S32;
typedef unsigned long long U64;
typedef size_t uptrval; /* generally true, except OpenVMS-64 */
#endif
#if defined(__x86_64__)
typedef U64 reg_t; /* 64-bits in x32 mode */
#else
typedef size_t reg_t; /* 32-bits in x32 mode */
#endif
typedef enum {
notLimited = 0,
limitedOutput = 1,
fillOutput = 2
} limitedOutput_directive;
/*-************************************
* Reading and writing into memory
**************************************/
/**
* LZ4 relies on memcpy with a constant size being inlined. In freestanding
* environments, the compiler can't assume the implementation of memcpy() is
* standard compliant, so it can't apply its specialized memcpy() inlining
* logic. When possible, use __builtin_memcpy() to tell the compiler to analyze
* memcpy() as if it were standard compliant, so it can inline it in freestanding
* environments. This is needed when decompressing the Linux Kernel, for example.
*/
#if !defined(LZ4_memcpy)
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)
# else
# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)
# endif
#endif
#if !defined(LZ4_memmove)
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define LZ4_memmove __builtin_memmove
# else
# define LZ4_memmove memmove
# endif
#endif
static unsigned LZ4_isLittleEndian(void)
{
const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
return one.c[0];
}
#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
/* lie to the compiler about data alignment; use with caution */
static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
/* currently only defined for gcc and icc */
typedef struct { U16 u16; } __attribute__((packed)) LZ4_unalign16;
typedef struct { U32 u32; } __attribute__((packed)) LZ4_unalign32;
typedef struct { reg_t uArch; } __attribute__((packed)) LZ4_unalignST;
static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign16*)ptr)->u16; }
static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign32*)ptr)->u32; }
static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalignST*)ptr)->uArch; }
static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign16*)memPtr)->u16 = value; }
static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign32*)memPtr)->u32 = value; }
#else /* safe and portable access using memcpy() */
static U16 LZ4_read16(const void* memPtr)
{
U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
}
static U32 LZ4_read32(const void* memPtr)
{
U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
}
static reg_t LZ4_read_ARCH(const void* memPtr)
{
reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;
}
static void LZ4_write16(void* memPtr, U16 value)
{
LZ4_memcpy(memPtr, &value, sizeof(value));
}
static void LZ4_write32(void* memPtr, U32 value)
{
LZ4_memcpy(memPtr, &value, sizeof(value));
}
#endif /* LZ4_FORCE_MEMORY_ACCESS */
static U16 LZ4_readLE16(const void* memPtr)
{
if (LZ4_isLittleEndian()) {
return LZ4_read16(memPtr);
} else {
const BYTE* p = (const BYTE*)memPtr;
return (U16)((U16)p[0] + (p[1]<<8));
}
}
static void LZ4_writeLE16(void* memPtr, U16 value)
{
if (LZ4_isLittleEndian()) {
LZ4_write16(memPtr, value);
} else {
BYTE* p = (BYTE*)memPtr;
p[0] = (BYTE) value;
p[1] = (BYTE)(value>>8);
}
}
/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
LZ4_FORCE_INLINE
void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)
{
BYTE* d = (BYTE*)dstPtr;
const BYTE* s = (const BYTE*)srcPtr;
BYTE* const e = (BYTE*)dstEnd;
do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */
LZ4_FORCE_INLINE void
LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)
{
BYTE* d = (BYTE*)dstPtr;
const BYTE* s = (const BYTE*)srcPtr;
BYTE* const e = (BYTE*)dstEnd;
do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH
* - there is at least 8 bytes available to write after dstEnd */
LZ4_FORCE_INLINE void
LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)
{
BYTE v[8];
assert(dstEnd >= dstPtr + MINMATCH);
switch(offset) {
case 1:
MEM_INIT(v, *srcPtr, 8);
break;
case 2:
LZ4_memcpy(v, srcPtr, 2);
LZ4_memcpy(&v[2], srcPtr, 2);
#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */
# pragma warning(push)
# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */
#endif
LZ4_memcpy(&v[4], v, 4);
#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */
# pragma warning(pop)
#endif
break;
case 4:
LZ4_memcpy(v, srcPtr, 4);
LZ4_memcpy(&v[4], srcPtr, 4);
break;
default:
LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);
return;
}
LZ4_memcpy(dstPtr, v, 8);
dstPtr += 8;
while (dstPtr < dstEnd) {
LZ4_memcpy(dstPtr, v, 8);
dstPtr += 8;
}
}
#endif
/*-************************************
* Common functions
**************************************/
static unsigned LZ4_NbCommonBytes (reg_t val)
{
assert(val != 0);
if (LZ4_isLittleEndian()) {
if (sizeof(val) == 8) {
# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)
/*-*************************************************************************************************
* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.
* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics
* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.
****************************************************************************************************/
# if defined(__clang__) && (__clang_major__ < 10)
/* Avoid undefined clang-cl intrinsics issue.
* See https://github.com/lz4/lz4/pull/1017 for details. */
return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;
# else
/* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */
return (unsigned)_tzcnt_u64(val) >> 3;
# endif
# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
unsigned long r = 0;
_BitScanForward64(&r, (U64)val);
return (unsigned)r >> 3;
# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
!defined(LZ4_FORCE_SW_BITCOUNT)
return (unsigned)__builtin_ctzll((U64)val) >> 3;
# else
const U64 m = 0x0101010101010101ULL;
val ^= val - 1;
return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);
# endif
} else /* 32 bits */ {
# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)
unsigned long r;
_BitScanForward(&r, (U32)val);
return (unsigned)r >> 3;
# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
!defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
return (unsigned)__builtin_ctz((U32)val) >> 3;
# else
const U32 m = 0x01010101;
return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;
# endif
}
} else /* Big Endian CPU */ {
if (sizeof(val)==8) {
# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
!defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)
return (unsigned)__builtin_clzll((U64)val) >> 3;
# else
#if 1
/* this method is probably faster,
* but adds a 128 bytes lookup table */
static const unsigned char ctz7_tab[128] = {
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
};
U64 const mask = 0x0101010101010101ULL;
U64 const t = (((val >> 8) - mask) | val) & mask;
return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];
#else
/* this method doesn't consume memory space like the previous one,
* but it contains several branches,
* that may end up slowing execution */
static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits.
Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.
Note that this code path is never triggered in 32-bits mode. */
unsigned r;
if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }
if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
r += (!val);
return r;
#endif
# endif
} else /* 32 bits */ {
# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \
((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \
!defined(LZ4_FORCE_SW_BITCOUNT)
return (unsigned)__builtin_clz((U32)val) >> 3;
# else
val >>= 8;
val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |
(val + 0x00FF0000)) >> 24;
return (unsigned)val ^ 3;
# endif
}
}
}
#define STEPSIZE sizeof(reg_t)
LZ4_FORCE_INLINE
unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
{
const BYTE* const pStart = pIn;
if (likely(pIn < pInLimit-(STEPSIZE-1))) {
reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
if (!diff) {
pIn+=STEPSIZE; pMatch+=STEPSIZE;
} else {
return LZ4_NbCommonBytes(diff);
} }
while (likely(pIn < pInLimit-(STEPSIZE-1))) {
reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
pIn += LZ4_NbCommonBytes(diff);
return (unsigned)(pIn - pStart);
}
if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
if ((pIn compression run slower on incompressible data */
/*-************************************
* Local Structures and types
**************************************/
typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;
/**
* This enum distinguishes several different modes of accessing previous
* content in the stream.
*
* - noDict : There is no preceding content.
* - withPrefix64k : Table entries up to ctx->dictSize before the current blob
* blob being compressed are valid and refer to the preceding
* content (of length ctx->dictSize), which is available
* contiguously preceding in memory the content currently
* being compressed.
* - usingExtDict : Like withPrefix64k, but the preceding content is somewhere
* else in memory, starting at ctx->dictionary with length
* ctx->dictSize.
* - usingDictCtx : Everything concerning the preceding content is
* in a separate context, pointed to by ctx->dictCtx.
* ctx->dictionary, ctx->dictSize, and table entries
* in the current context that refer to positions
* preceding the beginning of the current compression are
* ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx
* ->dictSize describe the location and size of the preceding
* content, and matches are found by looking in the ctx
* ->dictCtx->hashTable.
*/
typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;
typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
/*-************************************
* Local Utils
**************************************/
int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }
/*-****************************************
* Internal Definitions, used only in Tests
*******************************************/
#if defined (__cplusplus)
extern "C" {
#endif
int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);
int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
int compressedSize, int maxOutputSize,
const void* dictStart, size_t dictSize);
int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
int compressedSize, int targetOutputSize, int dstCapacity,
const void* dictStart, size_t dictSize);
#if defined (__cplusplus)
}
#endif
/*-******************************
* Compression functions
********************************/
LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
{
if (tableType == byU16)
return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
else
return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
}
LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
{
const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
if (LZ4_isLittleEndian()) {
const U64 prime5bytes = 889523592379ULL;
return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
} else {
const U64 prime8bytes = 11400714785074694791ULL;
return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
}
}
LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
{
if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
return LZ4_hash4(LZ4_read32(p), tableType);
}
LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)
{
switch (tableType)
{
default: /* fallthrough */
case clearedTable: { /* illegal! */ assert(0); return; }
case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }
case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }
case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }
}
}
LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)
{
switch (tableType)
{
default: /* fallthrough */
case clearedTable: /* fallthrough */
case byPtr: { /* illegal! */ assert(0); return; }
case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }
case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }
}
}
/* LZ4_putPosition*() : only used in byPtr mode */
LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,
void* tableBase, tableType_t const tableType)
{
const BYTE** const hashTable = (const BYTE**)tableBase;
assert(tableType == byPtr); (void)tableType;
hashTable[h] = p;
}
LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType)
{
U32 const h = LZ4_hashPosition(p, tableType);
LZ4_putPositionOnHash(p, h, tableBase, tableType);
}
/* LZ4_getIndexOnHash() :
* Index of match position registered in hash table.
* hash position must be calculated by using base+index, or dictBase+index.
* Assumption 1 : only valid if tableType == byU32 or byU16.
* Assumption 2 : h is presumed valid (within limits of hash table)
*/
LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)
{
LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);
if (tableType == byU32) {
const U32* const hashTable = (const U32*) tableBase;
assert(h < (1U << (LZ4_MEMORY_USAGE-2)));
return hashTable[h];
}
if (tableType == byU16) {
const U16* const hashTable = (const U16*) tableBase;
assert(h < (1U << (LZ4_MEMORY_USAGE-1)));
return hashTable[h];
}
assert(0); return 0; /* forbidden case */
}
static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType)
{
assert(tableType == byPtr); (void)tableType;
{ const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }
}
LZ4_FORCE_INLINE const BYTE*
LZ4_getPosition(const BYTE* p,
const void* tableBase, tableType_t tableType)
{
U32 const h = LZ4_hashPosition(p, tableType);
return LZ4_getPositionOnHash(h, tableBase, tableType);
}
LZ4_FORCE_INLINE void
LZ4_prepareTable(LZ4_stream_t_internal* const cctx,
const int inputSize,
const tableType_t tableType) {
/* If the table hasn't been used, it's guaranteed to be zeroed out, and is
* therefore safe to use no matter what mode we're in. Otherwise, we figure
* out if it's safe to leave as is or whether it needs to be reset.
*/
if ((tableType_t)cctx->tableType != clearedTable) {
assert(inputSize >= 0);
if ((tableType_t)cctx->tableType != tableType
|| ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)
|| ((tableType == byU32) && cctx->currentOffset > 1 GB)
|| tableType == byPtr
|| inputSize >= 4 KB)
{
DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx);
MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);
cctx->currentOffset = 0;
cctx->tableType = (U32)clearedTable;
} else {
DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)");
}
}
/* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,
* is faster than compressing without a gap.
* However, compressing with currentOffset == 0 is faster still,
* so we preserve that case.
*/
if (cctx->currentOffset != 0 && tableType == byU32) {
DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset");
cctx->currentOffset += 64 KB;
}
/* Finally, clear history */
cctx->dictCtx = NULL;
cctx->dictionary = NULL;
cctx->dictSize = 0;
}
/** LZ4_compress_generic() :
* inlined, to ensure branches are decided at compilation time.
* The following conditions are presumed already validated:
* - source != NULL
* - inputSize > 0
*/
LZ4_FORCE_INLINE int LZ4_compress_generic_validated(
LZ4_stream_t_internal* const cctx,
const char* const source,
char* const dest,
const int inputSize,
int* inputConsumed, /* only written when outputDirective == fillOutput */
const int maxOutputSize,
const limitedOutput_directive outputDirective,
const tableType_t tableType,
const dict_directive dictDirective,
const dictIssue_directive dictIssue,
const int acceleration)
{
int result;
const BYTE* ip = (const BYTE*)source;
U32 const startIndex = cctx->currentOffset;
const BYTE* base = (const BYTE*)source - startIndex;
const BYTE* lowLimit;
const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;
const BYTE* const dictionary =
dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;
const U32 dictSize =
dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;
const U32 dictDelta =
(dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with indexes in current context */
int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);
U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */
const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;
const BYTE* anchor = (const BYTE*) source;
const BYTE* const iend = ip + inputSize;
const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;
const BYTE* const matchlimit = iend - LASTLITERALS;
/* the dictCtx currentOffset is indexed on the start of the dictionary,
* while a dictionary in the current context precedes the currentOffset */
const BYTE* dictBase = (dictionary == NULL) ? NULL :
(dictDirective == usingDictCtx) ?
dictionary + dictSize - dictCtx->currentOffset :
dictionary + dictSize - startIndex;
BYTE* op = (BYTE*) dest;
BYTE* const olimit = op + maxOutputSize;
U32 offset = 0;
U32 forwardH;
DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType);
assert(ip != NULL);
if (tableType == byU16) assert(inputSize= 1);
lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);
/* Update context state */
if (dictDirective == usingDictCtx) {
/* Subsequent linked blocks can't use the dictionary. */
/* Instead, they use the block we just compressed. */
cctx->dictCtx = NULL;
cctx->dictSize = (U32)inputSize;
} else {
cctx->dictSize += (U32)inputSize;
}
cctx->currentOffset += (U32)inputSize;
cctx->tableType = (U32)tableType;
if (inputSizehashTable, byPtr);
} else {
LZ4_putIndexOnHash(startIndex, h, cctx->hashTable, tableType);
} }
ip++; forwardH = LZ4_hashPosition(ip, tableType);
/* Main Loop */
for ( ; ; ) {
const BYTE* match;
BYTE* token;
const BYTE* filledIp;
/* Find a match */
if (tableType == byPtr) {
const BYTE* forwardIp = ip;
int step = 1;
int searchMatchNb = acceleration << LZ4_skipTrigger;
do {
U32 const h = forwardH;
ip = forwardIp;
forwardIp += step;
step = (searchMatchNb++ >> LZ4_skipTrigger);
if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
assert(ip < mflimitPlusOne);
match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType);
forwardH = LZ4_hashPosition(forwardIp, tableType);
LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType);
} while ( (match+LZ4_DISTANCE_MAX < ip)
|| (LZ4_read32(match) != LZ4_read32(ip)) );
} else { /* byU32, byU16 */
const BYTE* forwardIp = ip;
int step = 1;
int searchMatchNb = acceleration << LZ4_skipTrigger;
do {
U32 const h = forwardH;
U32 const current = (U32)(forwardIp - base);
U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
assert(matchIndex <= current);
assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));
ip = forwardIp;
forwardIp += step;
step = (searchMatchNb++ >> LZ4_skipTrigger);
if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;
assert(ip < mflimitPlusOne);
if (dictDirective == usingDictCtx) {
if (matchIndex < startIndex) {
/* there was no match, try the dictionary */
assert(tableType == byU32);
matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
match = dictBase + matchIndex;
matchIndex += dictDelta; /* make dictCtx index comparable with current context */
lowLimit = dictionary;
} else {
match = base + matchIndex;
lowLimit = (const BYTE*)source;
}
} else if (dictDirective == usingExtDict) {
if (matchIndex < startIndex) {
DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex);
assert(startIndex - matchIndex >= MINMATCH);
assert(dictBase);
match = dictBase + matchIndex;
lowLimit = dictionary;
} else {
match = base + matchIndex;
lowLimit = (const BYTE*)source;
}
} else { /* single continuous memory segment */
match = base + matchIndex;
}
forwardH = LZ4_hashPosition(forwardIp, tableType);
LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex);
if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */
assert(matchIndex < current);
if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))
&& (matchIndex+LZ4_DISTANCE_MAX < current)) {
continue;
} /* too far */
assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */
if (LZ4_read32(match) == LZ4_read32(ip)) {
if (maybe_extMem) offset = current - matchIndex;
break; /* match found */
}
} while(1);
}
/* Catch up */
filledIp = ip;
while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
/* Encode Literals */
{ unsigned const litLength = (unsigned)(ip - anchor);
token = op++;
if ((outputDirective == limitedOutput) && /* Check output buffer overflow */
(unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {
return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
}
if ((outputDirective == fillOutput) &&
(unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {
op--;
goto _last_literals;
}
if (litLength >= RUN_MASK) {
int len = (int)(litLength - RUN_MASK);
*token = (RUN_MASK<= 255 ; len-=255) *op++ = 255;
*op++ = (BYTE)len;
}
else *token = (BYTE)(litLength< olimit)) {
/* the match was too close to the end, rewind and go to last literals */
op = token;
goto _last_literals;
}
/* Encode Offset */
if (maybe_extMem) { /* static test */
DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source));
assert(offset <= LZ4_DISTANCE_MAX && offset > 0);
LZ4_writeLE16(op, (U16)offset); op+=2;
} else {
DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match));
assert(ip-match <= LZ4_DISTANCE_MAX);
LZ4_writeLE16(op, (U16)(ip - match)); op+=2;
}
/* Encode MatchLength */
{ unsigned matchCode;
if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)
&& (lowLimit==dictionary) /* match within extDict */ ) {
const BYTE* limit = ip + (dictEnd-match);
assert(dictEnd > match);
if (limit > matchlimit) limit = matchlimit;
matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
ip += (size_t)matchCode + MINMATCH;
if (ip==limit) {
unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);
matchCode += more;
ip += more;
}
DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH);
} else {
matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
ip += (size_t)matchCode + MINMATCH;
DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH);
}
if ((outputDirective) && /* Check output buffer overflow */
(unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {
if (outputDirective == fillOutput) {
/* Match description too long : reduce it */
U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;
ip -= matchCode - newMatchCode;
assert(newMatchCode < matchCode);
matchCode = newMatchCode;
if (unlikely(ip <= filledIp)) {
/* We have already filled up to filledIp so if ip ends up less than filledIp
* we have positions in the hash table beyond the current position. This is
* a problem if we reuse the hash table. So we have to remove these positions
* from the hash table.
*/
const BYTE* ptr;
DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip));
for (ptr = ip; ptr <= filledIp; ++ptr) {
U32 const h = LZ4_hashPosition(ptr, tableType);
LZ4_clearHash(h, cctx->hashTable, tableType);
}
}
} else {
assert(outputDirective == limitedOutput);
return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
}
}
if (matchCode >= ML_MASK) {
*token += ML_MASK;
matchCode -= ML_MASK;
LZ4_write32(op, 0xFFFFFFFF);
while (matchCode >= 4*255) {
op+=4;
LZ4_write32(op, 0xFFFFFFFF);
matchCode -= 4*255;
}
op += matchCode / 255;
*op++ = (BYTE)(matchCode % 255);
} else
*token += (BYTE)(matchCode);
}
/* Ensure we have enough space for the last literals. */
assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));
anchor = ip;
/* Test end of chunk */
if (ip >= mflimitPlusOne) break;
/* Fill table */
{ U32 const h = LZ4_hashPosition(ip-2, tableType);
if (tableType == byPtr) {
LZ4_putPositionOnHash(ip-2, h, cctx->hashTable, byPtr);
} else {
U32 const idx = (U32)((ip-2) - base);
LZ4_putIndexOnHash(idx, h, cctx->hashTable, tableType);
} }
/* Test next position */
if (tableType == byPtr) {
match = LZ4_getPosition(ip, cctx->hashTable, tableType);
LZ4_putPosition(ip, cctx->hashTable, tableType);
if ( (match+LZ4_DISTANCE_MAX >= ip)
&& (LZ4_read32(match) == LZ4_read32(ip)) )
{ token=op++; *token=0; goto _next_match; }
} else { /* byU32, byU16 */
U32 const h = LZ4_hashPosition(ip, tableType);
U32 const current = (U32)(ip-base);
U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);
assert(matchIndex < current);
if (dictDirective == usingDictCtx) {
if (matchIndex < startIndex) {
/* there was no match, try the dictionary */
assert(tableType == byU32);
matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);
match = dictBase + matchIndex;
lowLimit = dictionary; /* required for match length counter */
matchIndex += dictDelta;
} else {
match = base + matchIndex;
lowLimit = (const BYTE*)source; /* required for match length counter */
}
} else if (dictDirective==usingExtDict) {
if (matchIndex < startIndex) {
assert(dictBase);
match = dictBase + matchIndex;
lowLimit = dictionary; /* required for match length counter */
} else {
match = base + matchIndex;
lowLimit = (const BYTE*)source; /* required for match length counter */
}
} else { /* single memory segment */
match = base + matchIndex;
}
LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);
assert(matchIndex < current);
if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)
&& (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))
&& (LZ4_read32(match) == LZ4_read32(ip)) ) {
token=op++;
*token=0;
if (maybe_extMem) offset = current - matchIndex;
DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i",
(int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));
goto _next_match;
}
}
/* Prepare next loop */
forwardH = LZ4_hashPosition(++ip, tableType);
}
_last_literals:
/* Encode Last Literals */
{ size_t lastRun = (size_t)(iend - anchor);
if ( (outputDirective) && /* Check output buffer overflow */
(op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {
if (outputDirective == fillOutput) {
/* adapt lastRun to fill 'dst' */
assert(olimit >= op);
lastRun = (size_t)(olimit-op) - 1/*token*/;
lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/
} else {
assert(outputDirective == limitedOutput);
return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */
}
}
DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun);
if (lastRun >= RUN_MASK) {
size_t accumulator = lastRun - RUN_MASK;
*op++ = RUN_MASK << ML_BITS;
for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
*op++ = (BYTE) accumulator;
} else {
*op++ = (BYTE)(lastRun< 0);
DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result);
return result;
}
/** LZ4_compress_generic() :
* inlined, to ensure branches are decided at compilation time;
* takes care of src == (NULL, 0)
* and forward the rest to LZ4_compress_generic_validated */
LZ4_FORCE_INLINE int LZ4_compress_generic(
LZ4_stream_t_internal* const cctx,
const char* const src,
char* const dst,
const int srcSize,
int *inputConsumed, /* only written when outputDirective == fillOutput */
const int dstCapacity,
const limitedOutput_directive outputDirective,
const tableType_t tableType,
const dict_directive dictDirective,
const dictIssue_directive dictIssue,
const int acceleration)
{
DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i",
srcSize, dstCapacity);
if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */
if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */
if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */
DEBUGLOG(5, "Generating an empty block");
assert(outputDirective == notLimited || dstCapacity >= 1);
assert(dst != NULL);
dst[0] = 0;
if (outputDirective == fillOutput) {
assert (inputConsumed != NULL);
*inputConsumed = 0;
}
return 1;
}
assert(src != NULL);
return LZ4_compress_generic_validated(cctx, src, dst, srcSize,
inputConsumed, /* only written into if outputDirective == fillOutput */
dstCapacity, outputDirective,
tableType, dictDirective, dictIssue, acceleration);
}
int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
{
LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;
assert(ctx != NULL);
if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
if (maxOutputSize >= LZ4_compressBound(inputSize)) {
if (inputSize < LZ4_64Klimit) {
return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
} else {
const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
}
} else {
if (inputSize < LZ4_64Klimit) {
return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
} else {
const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);
}
}
}
/**
* LZ4_compress_fast_extState_fastReset() :
* A variant of LZ4_compress_fast_extState().
*
* Using this variant avoids an expensive initialization step. It is only safe
* to call if the state buffer is known to be correctly initialized already
* (see comment in lz4.h on LZ4_resetStream_fast() for a definition of
* "correctly initialized").
*/
int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)
{
LZ4_stream_t_internal* const ctx = &((LZ4_stream_t*)state)->internal_donotuse;
if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
assert(ctx != NULL);
if (dstCapacity >= LZ4_compressBound(srcSize)) {
if (srcSize < LZ4_64Klimit) {
const tableType_t tableType = byU16;
LZ4_prepareTable(ctx, srcSize, tableType);
if (ctx->currentOffset) {
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);
} else {
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
}
} else {
const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
LZ4_prepareTable(ctx, srcSize, tableType);
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);
}
} else {
if (srcSize < LZ4_64Klimit) {
const tableType_t tableType = byU16;
LZ4_prepareTable(ctx, srcSize, tableType);
if (ctx->currentOffset) {
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);
} else {
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
}
} else {
const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
LZ4_prepareTable(ctx, srcSize, tableType);
return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);
}
}
}
int LZ4_compress_fast(const char* src, char* dest, int srcSize, int dstCapacity, int acceleration)
{
int result;
#if (LZ4_HEAPMODE)
LZ4_stream_t* const ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
if (ctxPtr == NULL) return 0;
#else
LZ4_stream_t ctx;
LZ4_stream_t* const ctxPtr = &ctx;
#endif
result = LZ4_compress_fast_extState(ctxPtr, src, dest, srcSize, dstCapacity, acceleration);
#if (LZ4_HEAPMODE)
FREEMEM(ctxPtr);
#endif
return result;
}
int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity)
{
return LZ4_compress_fast(src, dst, srcSize, dstCapacity, 1);
}
/* Note!: This function leaves the stream in an unclean/broken state!
* It is not safe to subsequently use the same state with a _fastReset() or
* _continue() call without resetting it. */
static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
{
void* const s = LZ4_initStream(state, sizeof (*state));
assert(s != NULL); (void)s;
if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
} else {
if (*srcSizePtr < LZ4_64Klimit) {
return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);
} else {
tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;
return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);
} }
}
int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
{
#if (LZ4_HEAPMODE)
LZ4_stream_t* const ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
if (ctx == NULL) return 0;
#else
LZ4_stream_t ctxBody;
LZ4_stream_t* const ctx = &ctxBody;
#endif
int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
#if (LZ4_HEAPMODE)
FREEMEM(ctx);
#endif
return result;
}
/*-******************************
* Streaming functions
********************************/
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4_stream_t* LZ4_createStream(void)
{
LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));
LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));
DEBUGLOG(4, "LZ4_createStream %p", lz4s);
if (lz4s == NULL) return NULL;
LZ4_initStream(lz4s, sizeof(*lz4s));
return lz4s;
}
#endif
static size_t LZ4_stream_t_alignment(void)
{
#if LZ4_ALIGN_TEST
typedef struct { char c; LZ4_stream_t t; } t_a;
return sizeof(t_a) - sizeof(LZ4_stream_t);
#else
return 1; /* effectively disabled */
#endif
}
LZ4_stream_t* LZ4_initStream (void* buffer, size_t size)
{
DEBUGLOG(5, "LZ4_initStream");
if (buffer == NULL) { return NULL; }
if (size < sizeof(LZ4_stream_t)) { return NULL; }
if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;
MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));
return (LZ4_stream_t*)buffer;
}
/* resetStream is now deprecated,
* prefer initStream() which is more general */
void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
{
DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream);
MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));
}
void LZ4_resetStream_fast(LZ4_stream_t* ctx) {
LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);
}
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
{
if (!LZ4_stream) return 0; /* support free on NULL */
DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream);
FREEMEM(LZ4_stream);
return (0);
}
#endif
#define HASH_UNIT sizeof(reg_t)
int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
{
LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
const tableType_t tableType = byU32;
const BYTE* p = (const BYTE*)dictionary;
const BYTE* const dictEnd = p + dictSize;
U32 idx32;
DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict);
/* It's necessary to reset the context,
* and not just continue it with prepareTable()
* to avoid any risk of generating overflowing matchIndex
* when compressing using this dictionary */
LZ4_resetStream(LZ4_dict);
/* We always increment the offset by 64 KB, since, if the dict is longer,
* we truncate it to the last 64k, and if it's shorter, we still want to
* advance by a whole window length so we can provide the guarantee that
* there are only valid offsets in the window, which allows an optimization
* in LZ4_compress_fast_continue() where it uses noDictIssue even when the
* dictionary isn't a full 64k. */
dict->currentOffset += 64 KB;
if (dictSize < (int)HASH_UNIT) {
return 0;
}
if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
dict->dictionary = p;
dict->dictSize = (U32)(dictEnd - p);
dict->tableType = (U32)tableType;
idx32 = dict->currentOffset - dict->dictSize;
while (p <= dictEnd-HASH_UNIT) {
U32 const h = LZ4_hashPosition(p, tableType);
LZ4_putIndexOnHash(idx32, h, dict->hashTable, tableType);
p+=3; idx32+=3;
}
return (int)dict->dictSize;
}
void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)
{
const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :
&(dictionaryStream->internal_donotuse);
DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)",
workingStream, dictionaryStream,
dictCtx != NULL ? dictCtx->dictSize : 0);
if (dictCtx != NULL) {
/* If the current offset is zero, we will never look in the
* external dictionary context, since there is no value a table
* entry can take that indicate a miss. In that case, we need
* to bump the offset to something non-zero.
*/
if (workingStream->internal_donotuse.currentOffset == 0) {
workingStream->internal_donotuse.currentOffset = 64 KB;
}
/* Don't actually attach an empty dictionary.
*/
if (dictCtx->dictSize == 0) {
dictCtx = NULL;
}
}
workingStream->internal_donotuse.dictCtx = dictCtx;
}
static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)
{
assert(nextSize >= 0);
if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */
/* rescale hash table */
U32 const delta = LZ4_dict->currentOffset - 64 KB;
const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
int i;
DEBUGLOG(4, "LZ4_renormDictT");
for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0;
else LZ4_dict->hashTable[i] -= delta;
}
LZ4_dict->currentOffset = 64 KB;
if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
}
}
int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,
const char* source, char* dest,
int inputSize, int maxOutputSize,
int acceleration)
{
const tableType_t tableType = byU32;
LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;
const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;
DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize);
LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */
if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;
if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;
/* invalidate tiny dictionaries */
if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */
&& (dictEnd != source) /* prefix mode */
&& (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */
&& (streamPtr->dictCtx == NULL) /* usingDictCtx */
) {
DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary);
/* remove dictionary existence from history, to employ faster prefix mode */
streamPtr->dictSize = 0;
streamPtr->dictionary = (const BYTE*)source;
dictEnd = source;
}
/* Check overlapping input/dictionary space */
{ const char* const sourceEnd = source + inputSize;
if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {
streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;
}
}
/* prefix mode : source data follows dictionary */
if (dictEnd == source) {
if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);
else
return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);
}
/* external dictionary mode */
{ int result;
if (streamPtr->dictCtx) {
/* We depend here on the fact that dictCtx'es (produced by
* LZ4_loadDict) guarantee that their tables contain no references
* to offsets between dictCtx->currentOffset - 64 KB and
* dictCtx->currentOffset - dictCtx->dictSize. This makes it safe
* to use noDictIssue even when the dict isn't a full 64 KB.
*/
if (inputSize > 4 KB) {
/* For compressing large blobs, it is faster to pay the setup
* cost to copy the dictionary's tables into the active context,
* so that the compression loop is only looking into one table.
*/
LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));
result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
} else {
result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);
}
} else { /* small data <= 4 KB */
if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);
} else {
result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);
}
}
streamPtr->dictionary = (const BYTE*)source;
streamPtr->dictSize = (U32)inputSize;
return result;
}
}
/* Hidden debug function, to force-test external dictionary mode */
int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)
{
LZ4_stream_t_internal* const streamPtr = &LZ4_dict->internal_donotuse;
int result;
LZ4_renormDictT(streamPtr, srcSize);
if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {
result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);
} else {
result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
}
streamPtr->dictionary = (const BYTE*)source;
streamPtr->dictSize = (U32)srcSize;
return result;
}
/*! LZ4_saveDict() :
* If previously compressed data block is not guaranteed to remain available at its memory location,
* save it into a safer place (char* safeBuffer).
* Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,
* one can therefore call LZ4_compress_fast_continue() right after.
* @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
*/
int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
{
LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer);
if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */
if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }
if (safeBuffer == NULL) assert(dictSize == 0);
if (dictSize > 0) {
const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
assert(dict->dictionary);
LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);
}
dict->dictionary = (const BYTE*)safeBuffer;
dict->dictSize = (U32)dictSize;
return dictSize;
}
/*-*******************************
* Decompression functions
********************************/
typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;
#undef MIN
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
/* variant for decompress_unsafe()
* does not know end of input
* presumes input is well formed
* note : will consume at least one byte */
size_t read_long_length_no_check(const BYTE** pp)
{
size_t b, l = 0;
do { b = **pp; (*pp)++; l += b; } while (b==255);
DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1)
return l;
}
/* core decoder variant for LZ4_decompress_fast*()
* for legacy support only : these entry points are deprecated.
* - Presumes input is correctly formed (no defense vs malformed inputs)
* - Does not know input size (presume input buffer is "large enough")
* - Decompress a full block (only)
* @return : nb of bytes read from input.
* Note : this variant is not optimized for speed, just for maintenance.
* the goal is to remove support of decompress_fast*() variants by v2.0
**/
LZ4_FORCE_INLINE int
LZ4_decompress_unsafe_generic(
const BYTE* const istart,
BYTE* const ostart,
int decompressedSize,
size_t prefixSize,
const BYTE* const dictStart, /* only if dict==usingExtDict */
const size_t dictSize /* note: =0 if dictStart==NULL */
)
{
const BYTE* ip = istart;
BYTE* op = (BYTE*)ostart;
BYTE* const oend = ostart + decompressedSize;
const BYTE* const prefixStart = ostart - prefixSize;
DEBUGLOG(5, "LZ4_decompress_unsafe_generic");
if (dictStart == NULL) assert(dictSize == 0);
while (1) {
/* start new sequence */
unsigned token = *ip++;
/* literals */
{ size_t ll = token >> ML_BITS;
if (ll==15) {
/* long literal length */
ll += read_long_length_no_check(&ip);
}
if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */
LZ4_memmove(op, ip, ll); /* support in-place decompression */
op += ll;
ip += ll;
if ((size_t)(oend-op) < MFLIMIT) {
if (op==oend) break; /* end of block */
DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op);
/* incorrect end of block :
* last match must start at least MFLIMIT==12 bytes before end of output block */
return -1;
} }
/* match */
{ size_t ml = token & 15;
size_t const offset = LZ4_readLE16(ip);
ip+=2;
if (ml==15) {
/* long literal length */
ml += read_long_length_no_check(&ip);
}
ml += MINMATCH;
if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */
{ const BYTE* match = op - offset;
/* out of range */
if (offset > (size_t)(op - prefixStart) + dictSize) {
DEBUGLOG(6, "offset out of range");
return -1;
}
/* check special case : extDict */
if (offset > (size_t)(op - prefixStart)) {
/* extDict scenario */
const BYTE* const dictEnd = dictStart + dictSize;
const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));
size_t const extml = (size_t)(dictEnd - extMatch);
if (extml > ml) {
/* match entirely within extDict */
LZ4_memmove(op, extMatch, ml);
op += ml;
ml = 0;
} else {
/* match split between extDict & prefix */
LZ4_memmove(op, extMatch, extml);
op += extml;
ml -= extml;
}
match = prefixStart;
}
/* match copy - slow variant, supporting overlap copy */
{ size_t u;
for (u=0; u= ipmax before start of loop. Returns initial_error if so.
* @error (output) - error code. Must be set to 0 before call.
**/
typedef size_t Rvl_t;
static const Rvl_t rvl_error = (Rvl_t)(-1);
LZ4_FORCE_INLINE Rvl_t
read_variable_length(const BYTE** ip, const BYTE* ilimit,
int initial_check)
{
Rvl_t s, length = 0;
assert(ip != NULL);
assert(*ip != NULL);
assert(ilimit != NULL);
if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */
return rvl_error;
}
do {
s = **ip;
(*ip)++;
length += s;
if (unlikely((*ip) > ilimit)) { /* read limit reached */
return rvl_error;
}
/* accumulator overflow detection (32-bit mode only) */
if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {
return rvl_error;
}
} while (s==255);
return length;
}
/*! LZ4_decompress_generic() :
* This generic decompression function covers all use cases.
* It shall be instantiated several times, using different sets of directives.
* Note that it is important for performance that this function really get inlined,
* in order to remove useless branches during compilation optimization.
*/
LZ4_FORCE_INLINE int
LZ4_decompress_generic(
const char* const src,
char* const dst,
int srcSize,
int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */
earlyEnd_directive partialDecoding, /* full, partial */
dict_directive dict, /* noDict, withPrefix64k, usingExtDict */
const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */
const BYTE* const dictStart, /* only if dict==usingExtDict */
const size_t dictSize /* note : = 0 if noDict */
)
{
if ((src == NULL) || (outputSize < 0)) { return -1; }
{ const BYTE* ip = (const BYTE*) src;
const BYTE* const iend = ip + srcSize;
BYTE* op = (BYTE*) dst;
BYTE* const oend = op + outputSize;
BYTE* cpy;
const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;
const int checkOffset = (dictSize < (int)(64 KB));
/* Set up the "end" pointers for the shortcut. */
const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;
const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;
const BYTE* match;
size_t offset;
unsigned token;
size_t length;
DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize);
/* Special cases */
assert(lowPrefix <= op);
if (unlikely(outputSize==0)) {
/* Empty output buffer */
if (partialDecoding) return 0;
return ((srcSize==1) && (*ip==0)) ? 0 : -1;
}
if (unlikely(srcSize==0)) { return -1; }
/* LZ4_FAST_DEC_LOOP:
* designed for modern OoO performance cpus,
* where copying reliably 32-bytes is preferable to an unpredictable branch.
* note : fast loop may show a regression for some client arm chips. */
#if LZ4_FAST_DEC_LOOP
if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {
DEBUGLOG(6, "skip fast decode loop");
goto safe_decode;
}
/* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */
DEBUGLOG(6, "using fast decode loop");
while (1) {
/* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */
assert(oend - op >= FASTLOOP_SAFE_DISTANCE);
assert(ip < iend);
token = *ip++;
length = token >> ML_BITS; /* literal length */
/* decode literal length */
if (length == RUN_MASK) {
size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
if (addl == rvl_error) {
DEBUGLOG(6, "error reading long literal length");
goto _output_error;
}
length += addl;
if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
/* copy literals */
cpy = op+length;
LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }
LZ4_wildCopy32(op, ip, cpy);
ip += length; op = cpy;
} else {
cpy = op+length;
DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length);
/* We don't need to check oend, since we check it once for each loop below */
if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }
/* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */
LZ4_memcpy(op, ip, 16);
ip += length; op = cpy;
}
/* get offset */
offset = LZ4_readLE16(ip); ip+=2;
DEBUGLOG(6, " offset = %zu", offset);
match = op - offset;
assert(match <= op); /* overflow check */
/* get matchlength */
length = token & ML_MASK;
if (length == ML_MASK) {
size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
if (addl == rvl_error) {
DEBUGLOG(6, "error reading long match length");
goto _output_error;
}
length += addl;
length += MINMATCH;
if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */
if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) {
DEBUGLOG(6, "Error : offset outside buffers");
goto _output_error;
}
if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
goto safe_match_copy;
}
} else {
length += MINMATCH;
if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {
goto safe_match_copy;
}
/* Fastpath check: skip LZ4_wildCopy32 when true */
if ((dict == withPrefix64k) || (match >= lowPrefix)) {
if (offset >= 8) {
assert(match >= lowPrefix);
assert(match <= op);
assert(op + 18 <= oend);
LZ4_memcpy(op, match, 8);
LZ4_memcpy(op+8, match+8, 8);
LZ4_memcpy(op+16, match+16, 2);
op += length;
continue;
} } }
if ( checkOffset && (unlikely(match + dictSize < lowPrefix)) ) {
DEBUGLOG(6, "Error : pos=%zi, offset=%zi => outside buffers", op-lowPrefix, op-match);
goto _output_error;
}
/* match starting within external dictionary */
if ((dict==usingExtDict) && (match < lowPrefix)) {
assert(dictEnd != NULL);
if (unlikely(op+length > oend-LASTLITERALS)) {
if (partialDecoding) {
DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd");
length = MIN(length, (size_t)(oend-op));
} else {
DEBUGLOG(6, "end-of-block condition violated")
goto _output_error;
} }
if (length <= (size_t)(lowPrefix-match)) {
/* match fits entirely within external dictionary : just copy */
LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
op += length;
} else {
/* match stretches into both external dictionary and current block */
size_t const copySize = (size_t)(lowPrefix - match);
size_t const restSize = length - copySize;
LZ4_memcpy(op, dictEnd - copySize, copySize);
op += copySize;
if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
BYTE* const endOfMatch = op + restSize;
const BYTE* copyFrom = lowPrefix;
while (op < endOfMatch) { *op++ = *copyFrom++; }
} else {
LZ4_memcpy(op, lowPrefix, restSize);
op += restSize;
} }
continue;
}
/* copy match within block */
cpy = op + length;
assert((op <= oend) && (oend-op >= 32));
if (unlikely(offset<16)) {
LZ4_memcpy_using_offset(op, match, cpy, offset);
} else {
LZ4_wildCopy32(op, match, cpy);
}
op = cpy; /* wildcopy correction */
}
safe_decode:
#endif
/* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */
DEBUGLOG(6, "using safe decode loop");
while (1) {
assert(ip < iend);
token = *ip++;
length = token >> ML_BITS; /* literal length */
/* A two-stage shortcut for the most common case:
* 1) If the literal length is 0..14, and there is enough space,
* enter the shortcut and copy 16 bytes on behalf of the literals
* (in the fast mode, only 8 bytes can be safely copied this way).
* 2) Further if the match length is 4..18, copy 18 bytes in a similar
* manner; but we ensure that there's enough space in the output for
* those 18 bytes earlier, upon entering the shortcut (in other words,
* there is a combined check for both stages).
*/
if ( (length != RUN_MASK)
/* strictly "less than" on input, to re-enter the loop with at least one byte */
&& likely((ip < shortiend) & (op <= shortoend)) ) {
/* Copy the literals */
LZ4_memcpy(op, ip, 16);
op += length; ip += length;
/* The second stage: prepare for match copying, decode full info.
* If it doesn't work out, the info won't be wasted. */
length = token & ML_MASK; /* match length */
offset = LZ4_readLE16(ip); ip += 2;
match = op - offset;
assert(match <= op); /* check overflow */
/* Do not deal with overlapping matches. */
if ( (length != ML_MASK)
&& (offset >= 8)
&& (dict==withPrefix64k || match >= lowPrefix) ) {
/* Copy the match. */
LZ4_memcpy(op + 0, match + 0, 8);
LZ4_memcpy(op + 8, match + 8, 8);
LZ4_memcpy(op +16, match +16, 2);
op += length + MINMATCH;
/* Both stages worked, load the next token. */
continue;
}
/* The second stage didn't work out, but the info is ready.
* Propel it right to the point of match copying. */
goto _copy_match;
}
/* decode literal length */
if (length == RUN_MASK) {
size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);
if (addl == rvl_error) { goto _output_error; }
length += addl;
if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */
if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */
}
/* copy literals */
cpy = op+length;
#if LZ4_FAST_DEC_LOOP
safe_literal_copy:
#endif
LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);
if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {
/* We've either hit the input parsing restriction or the output parsing restriction.
* In the normal scenario, decoding a full block, it must be the last sequence,
* otherwise it's an error (invalid input or dimensions).
* In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.
*/
if (partialDecoding) {
/* Since we are partial decoding we may be in this block because of the output parsing
* restriction, which is not valid since the output buffer is allowed to be undersized.
*/
DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end")
DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length);
DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op));
DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip));
/* Finishing in the middle of a literals segment,
* due to lack of input.
*/
if (ip+length > iend) {
length = (size_t)(iend-ip);
cpy = op + length;
}
/* Finishing in the middle of a literals segment,
* due to lack of output space.
*/
if (cpy > oend) {
cpy = oend;
assert(op<=oend);
length = (size_t)(oend-op);
}
} else {
/* We must be on the last sequence (or invalid) because of the parsing limitations
* so check that we exactly consume the input and don't overrun the output buffer.
*/
if ((ip+length != iend) || (cpy > oend)) {
DEBUGLOG(6, "should have been last run of literals")
DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend);
DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend);
goto _output_error;
}
}
LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */
ip += length;
op += length;
/* Necessarily EOF when !partialDecoding.
* When partialDecoding, it is EOF if we've either
* filled the output buffer or
* can't proceed with reading an offset for following match.
*/
if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {
break;
}
} else {
LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */
ip += length; op = cpy;
}
/* get offset */
offset = LZ4_readLE16(ip); ip+=2;
match = op - offset;
/* get matchlength */
length = token & ML_MASK;
_copy_match:
if (length == ML_MASK) {
size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);
if (addl == rvl_error) { goto _output_error; }
length += addl;
if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
}
length += MINMATCH;
#if LZ4_FAST_DEC_LOOP
safe_match_copy:
#endif
if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */
/* match starting within external dictionary */
if ((dict==usingExtDict) && (match < lowPrefix)) {
assert(dictEnd != NULL);
if (unlikely(op+length > oend-LASTLITERALS)) {
if (partialDecoding) length = MIN(length, (size_t)(oend-op));
else goto _output_error; /* doesn't respect parsing restriction */
}
if (length <= (size_t)(lowPrefix-match)) {
/* match fits entirely within external dictionary : just copy */
LZ4_memmove(op, dictEnd - (lowPrefix-match), length);
op += length;
} else {
/* match stretches into both external dictionary and current block */
size_t const copySize = (size_t)(lowPrefix - match);
size_t const restSize = length - copySize;
LZ4_memcpy(op, dictEnd - copySize, copySize);
op += copySize;
if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */
BYTE* const endOfMatch = op + restSize;
const BYTE* copyFrom = lowPrefix;
while (op < endOfMatch) *op++ = *copyFrom++;
} else {
LZ4_memcpy(op, lowPrefix, restSize);
op += restSize;
} }
continue;
}
assert(match >= lowPrefix);
/* copy match within block */
cpy = op + length;
/* partialDecoding : may end anywhere within the block */
assert(op<=oend);
if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
size_t const mlen = MIN(length, (size_t)(oend-op));
const BYTE* const matchEnd = match + mlen;
BYTE* const copyEnd = op + mlen;
if (matchEnd > op) { /* overlap copy */
while (op < copyEnd) { *op++ = *match++; }
} else {
LZ4_memcpy(op, match, mlen);
}
op = copyEnd;
if (op == oend) { break; }
continue;
}
if (unlikely(offset<8)) {
LZ4_write32(op, 0); /* silence msan warning when offset==0 */
op[0] = match[0];
op[1] = match[1];
op[2] = match[2];
op[3] = match[3];
match += inc32table[offset];
LZ4_memcpy(op+4, match, 4);
match -= dec64table[offset];
} else {
LZ4_memcpy(op, match, 8);
match += 8;
}
op += 8;
if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {
BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);
if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
if (op < oCopyLimit) {
LZ4_wildCopy8(op, match, oCopyLimit);
match += oCopyLimit - op;
op = oCopyLimit;
}
while (op < cpy) { *op++ = *match++; }
} else {
LZ4_memcpy(op, match, 8);
if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); }
}
op = cpy; /* wildcopy correction */
}
/* end of decoding */
DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst));
return (int) (((char*)op)-dst); /* Nb of output bytes decoded */
/* Overflow error detected */
_output_error:
return (int) (-(((const char*)ip)-src))-1;
}
}
/*===== Instantiate the API decoding functions. =====*/
LZ4_FORCE_O2
int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,
decode_full_block, noDict,
(BYTE*)dest, NULL, 0);
}
LZ4_FORCE_O2
int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)
{
dstCapacity = MIN(targetOutputSize, dstCapacity);
return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,
partial_decode,
noDict, (BYTE*)dst, NULL, 0);
}
LZ4_FORCE_O2
int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
{
DEBUGLOG(5, "LZ4_decompress_fast");
return LZ4_decompress_unsafe_generic(
(const BYTE*)source, (BYTE*)dest, originalSize,
0, NULL, 0);
}
/*===== Instantiate a few more decoding cases, used more than once. =====*/
LZ4_FORCE_O2 /* Exported, an obsolete API function. */
int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
decode_full_block, withPrefix64k,
(BYTE*)dest - 64 KB, NULL, 0);
}
LZ4_FORCE_O2
static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)
{
dstCapacity = MIN(targetOutputSize, dstCapacity);
return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
partial_decode, withPrefix64k,
(BYTE*)dest - 64 KB, NULL, 0);
}
/* Another obsolete API function, paired with the previous one. */
int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
{
return LZ4_decompress_unsafe_generic(
(const BYTE*)source, (BYTE*)dest, originalSize,
64 KB, NULL, 0);
}
LZ4_FORCE_O2
static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,
size_t prefixSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
decode_full_block, noDict,
(BYTE*)dest-prefixSize, NULL, 0);
}
LZ4_FORCE_O2
static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,
size_t prefixSize)
{
dstCapacity = MIN(targetOutputSize, dstCapacity);
return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
partial_decode, noDict,
(BYTE*)dest-prefixSize, NULL, 0);
}
LZ4_FORCE_O2
int LZ4_decompress_safe_forceExtDict(const char* source, char* dest,
int compressedSize, int maxOutputSize,
const void* dictStart, size_t dictSize)
{
DEBUGLOG(5, "LZ4_decompress_safe_forceExtDict");
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
decode_full_block, usingExtDict,
(BYTE*)dest, (const BYTE*)dictStart, dictSize);
}
LZ4_FORCE_O2
int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,
int compressedSize, int targetOutputSize, int dstCapacity,
const void* dictStart, size_t dictSize)
{
dstCapacity = MIN(targetOutputSize, dstCapacity);
return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,
partial_decode, usingExtDict,
(BYTE*)dest, (const BYTE*)dictStart, dictSize);
}
LZ4_FORCE_O2
static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,
const void* dictStart, size_t dictSize)
{
return LZ4_decompress_unsafe_generic(
(const BYTE*)source, (BYTE*)dest, originalSize,
0, (const BYTE*)dictStart, dictSize);
}
/* The "double dictionary" mode, for use with e.g. ring buffers: the first part
* of the dictionary is passed as prefix, and the second via dictStart + dictSize.
* These routines are used only once, in LZ4_decompress_*_continue().
*/
LZ4_FORCE_INLINE
int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,
size_t prefixSize, const void* dictStart, size_t dictSize)
{
return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
decode_full_block, usingExtDict,
(BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);
}
/*===== streaming decompression functions =====*/
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4_streamDecode_t* LZ4_createStreamDecode(void)
{
LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));
return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));
}
int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
{
if (LZ4_stream == NULL) { return 0; } /* support free on NULL */
FREEMEM(LZ4_stream);
return 0;
}
#endif
/*! LZ4_setStreamDecode() :
* Use this function to instruct where to find the dictionary.
* This function is not necessary if previous data is still available where it was decoded.
* Loading a size of 0 is allowed (same effect as no dictionary).
* @return : 1 if OK, 0 if error
*/
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
{
LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
lz4sd->prefixSize = (size_t)dictSize;
if (dictSize) {
assert(dictionary != NULL);
lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
} else {
lz4sd->prefixEnd = (const BYTE*) dictionary;
}
lz4sd->externalDict = NULL;
lz4sd->extDictSize = 0;
return 1;
}
/*! LZ4_decoderRingBufferSize() :
* when setting a ring buffer for streaming decompression (optional scenario),
* provides the minimum size of this ring buffer
* to be compatible with any source respecting maxBlockSize condition.
* Note : in a ring buffer scenario,
* blocks are presumed decompressed next to each other.
* When not enough space remains for next block (remainingSize < maxBlockSize),
* decoding resumes from beginning of ring buffer.
* @return : minimum ring buffer size,
* or 0 if there is an error (invalid maxBlockSize).
*/
int LZ4_decoderRingBufferSize(int maxBlockSize)
{
if (maxBlockSize < 0) return 0;
if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;
if (maxBlockSize < 16) maxBlockSize = 16;
return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);
}
/*
*_continue() :
These decoding functions allow decompression of multiple blocks in "streaming" mode.
Previously decoded blocks must still be available at the memory position where they were decoded.
If it's not possible, save the relevant part of decoded data into a safe buffer,
and indicate where it stands using LZ4_setStreamDecode()
*/
LZ4_FORCE_O2
int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
{
LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
int result;
if (lz4sd->prefixSize == 0) {
/* The first call, no dictionary yet. */
assert(lz4sd->extDictSize == 0);
result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
if (result <= 0) return result;
lz4sd->prefixSize = (size_t)result;
lz4sd->prefixEnd = (BYTE*)dest + result;
} else if (lz4sd->prefixEnd == (BYTE*)dest) {
/* They're rolling the current segment. */
if (lz4sd->prefixSize >= 64 KB - 1)
result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
else if (lz4sd->extDictSize == 0)
result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,
lz4sd->prefixSize);
else
result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,
lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
lz4sd->prefixSize += (size_t)result;
lz4sd->prefixEnd += result;
} else {
/* The buffer wraps around, or they're switching to another buffer. */
lz4sd->extDictSize = lz4sd->prefixSize;
lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,
lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
lz4sd->prefixSize = (size_t)result;
lz4sd->prefixEnd = (BYTE*)dest + result;
}
return result;
}
LZ4_FORCE_O2 int
LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,
const char* source, char* dest, int originalSize)
{
LZ4_streamDecode_t_internal* const lz4sd =
(assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);
int result;
DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize);
assert(originalSize >= 0);
if (lz4sd->prefixSize == 0) {
DEBUGLOG(5, "first invocation : no prefix nor extDict");
assert(lz4sd->extDictSize == 0);
result = LZ4_decompress_fast(source, dest, originalSize);
if (result <= 0) return result;
lz4sd->prefixSize = (size_t)originalSize;
lz4sd->prefixEnd = (BYTE*)dest + originalSize;
} else if (lz4sd->prefixEnd == (BYTE*)dest) {
DEBUGLOG(5, "continue using existing prefix");
result = LZ4_decompress_unsafe_generic(
(const BYTE*)source, (BYTE*)dest, originalSize,
lz4sd->prefixSize,
lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
lz4sd->prefixSize += (size_t)originalSize;
lz4sd->prefixEnd += originalSize;
} else {
DEBUGLOG(5, "prefix becomes extDict");
lz4sd->extDictSize = lz4sd->prefixSize;
lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
result = LZ4_decompress_fast_extDict(source, dest, originalSize,
lz4sd->externalDict, lz4sd->extDictSize);
if (result <= 0) return result;
lz4sd->prefixSize = (size_t)originalSize;
lz4sd->prefixEnd = (BYTE*)dest + originalSize;
}
return result;
}
/*
Advanced decoding functions :
*_usingDict() :
These decoding functions work the same as "_continue" ones,
the dictionary must be explicitly provided within parameters
*/
int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
{
if (dictSize==0)
return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);
if (dictStart+dictSize == dest) {
if (dictSize >= 64 KB - 1) {
return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);
}
assert(dictSize >= 0);
return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);
}
assert(dictSize >= 0);
return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);
}
int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)
{
if (dictSize==0)
return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);
if (dictStart+dictSize == dest) {
if (dictSize >= 64 KB - 1) {
return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);
}
assert(dictSize >= 0);
return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);
}
assert(dictSize >= 0);
return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);
}
int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
{
if (dictSize==0 || dictStart+dictSize == dest)
return LZ4_decompress_unsafe_generic(
(const BYTE*)source, (BYTE*)dest, originalSize,
(size_t)dictSize, NULL, 0);
assert(dictSize >= 0);
return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);
}
/*=*************************************************
* Obsolete Functions
***************************************************/
/* obsolete compression functions */
int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)
{
return LZ4_compress_default(source, dest, inputSize, maxOutputSize);
}
int LZ4_compress(const char* src, char* dest, int srcSize)
{
return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));
}
int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)
{
return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);
}
int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)
{
return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);
}
int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)
{
return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);
}
int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)
{
return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);
}
/*
These decompression functions are deprecated and should no longer be used.
They are only provided here for compatibility with older user programs.
- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
*/
int LZ4_uncompress (const char* source, char* dest, int outputSize)
{
return LZ4_decompress_fast(source, dest, outputSize);
}
int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)
{
return LZ4_decompress_safe(source, dest, isize, maxOutputSize);
}
/* Obsolete Streaming functions */
int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }
int LZ4_resetStreamState(void* state, char* inputBuffer)
{
(void)inputBuffer;
LZ4_resetStream((LZ4_stream_t*)state);
return 0;
}
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
void* LZ4_create (char* inputBuffer)
{
(void)inputBuffer;
return LZ4_createStream();
}
#endif
char* LZ4_slideInputBuffer (void* state)
{
/* avoid const char * -> char * conversion warning */
return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;
}
#endif /* LZ4_COMMONDEFS_ONLY */
================================================
FILE: jni/lz4/lz4.h
================================================
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-2020, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
#if defined (__cplusplus)
extern "C" {
#endif
#ifndef LZ4_H_2983827168210
#define LZ4_H_2983827168210
/* --- Dependency --- */
#include /* size_t */
/**
Introduction
LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
The LZ4 compression library provides in-memory compression and decompression functions.
It gives full buffer control to user.
Compression can be done in:
- a single step (described as Simple Functions)
- a single step, reusing a context (described in Advanced Functions)
- unbounded multiple steps (described as Streaming compression)
lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
Decompressing such a compressed block requires additional metadata.
Exact metadata depends on exact decompression function.
For the typical case of LZ4_decompress_safe(),
metadata includes block's compressed size, and maximum bound of decompressed size.
Each application is free to encode and pass such metadata in whichever way it wants.
lz4.h only handle blocks, it can not generate Frames.
Blocks are different from Frames (doc/lz4_Frame_format.md).
Frames bundle both blocks and metadata in a specified manner.
Embedding metadata is required for compressed data to be self-contained and portable.
Frame format is delivered through a companion API, declared in lz4frame.h.
The `lz4` CLI can only manage frames.
*/
/*^***************************************************************
* Export parameters
*****************************************************************/
/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
* LZ4LIB_VISIBILITY :
* Control library symbols visibility.
*/
#ifndef LZ4LIB_VISIBILITY
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
# else
# define LZ4LIB_VISIBILITY
# endif
#endif
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
/*! LZ4_FREESTANDING :
* When this macro is set to 1, it enables "freestanding mode" that is
* suitable for typical freestanding environment which doesn't support
* standard C library.
*
* - LZ4_FREESTANDING is a compile-time switch.
* - It requires the following macros to be defined:
* LZ4_memcpy, LZ4_memmove, LZ4_memset.
* - It only enables LZ4/HC functions which don't use heap.
* All LZ4F_* functions are not supported.
* - See tests/freestanding.c to check its basic setup.
*/
#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
# define LZ4_HEAPMODE 0
# define LZ4HC_HEAPMODE 0
# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
# if !defined(LZ4_memcpy)
# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
# endif
# if !defined(LZ4_memset)
# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
# endif
# if !defined(LZ4_memmove)
# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
# endif
#elif ! defined(LZ4_FREESTANDING)
# define LZ4_FREESTANDING 0
#endif
/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
/*-************************************
* Tuning parameter
**************************************/
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )
* Increasing memory usage improves compression ratio, at the cost of speed.
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
#ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
#endif
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
# error "LZ4_MEMORY_USAGE is too small !"
#endif
#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
# error "LZ4_MEMORY_USAGE is too large !"
#endif
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
* Compresses 'srcSize' bytes from buffer 'src'
* into already allocated 'dst' buffer of size 'dstCapacity'.
* Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
* It also runs faster, so it's a recommended setting.
* If the function cannot compress 'src' into a more limited 'dst' budget,
* compression stops *immediately*, and the function result is zero.
* In which case, 'dst' content is undefined (invalid).
* srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
* dstCapacity : size of buffer 'dst' (which must be already allocated)
* @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails
* Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() :
* @compressedSize : is the exact complete size of the compressed block.
* @dstCapacity : is the size of destination buffer (which must be already allocated),
* is an upper bound of decompressed size.
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
* Note 1 : This function is protected against malicious data packets :
* it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
* even if the compressed block is maliciously modified to order the decoder to do these actions.
* In such case, the decoder stops immediately, and considers the compressed block malformed.
* Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
* The implementation is free to send / store / derive this information in whichever way is most beneficial.
* If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
*/
LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
/*-************************************
* Advanced Functions
**************************************/
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
/*! LZ4_compressBound() :
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
This function is primarily useful for memory allocation purposes (destination buffer size).
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
return : maximum output size in a "worst case" scenario
or 0, if input size is incorrect (too large or negative)
*/
LZ4LIB_API int LZ4_compressBound(int inputSize);
/*! LZ4_compress_fast() :
Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
*/
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_fast_extState() :
* Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
* Use LZ4_sizeofState() to know how much memory must be allocated,
* and allocate it on 8-bytes boundaries (using `malloc()` typically).
* Then, provide this buffer as `void* state` to compression function.
*/
LZ4LIB_API int LZ4_sizeofState(void);
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_destSize() :
* Reverse the logic : compresses as much data as possible from 'src' buffer
* into already allocated buffer 'dst', of size >= 'targetDestSize'.
* This function either compresses the entire 'src' content into 'dst' if it's large enough,
* or fill 'dst' buffer completely with as much data as possible from 'src'.
* note: acceleration parameter is fixed to "default".
*
* *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
* New value is necessarily <= input value.
* @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
* or 0 if compression fails.
*
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):
* the produced compressed content could, in specific circumstances,
* require to be decompressed into a destination buffer larger
* by at least 1 byte than the content to decompress.
* If an application uses `LZ4_compress_destSize()`,
* it's highly recommended to update liblz4 to v1.9.2 or better.
* If this can't be done or ensured,
* the receiving decompression function should provide
* a dstCapacity which is > decompressedSize, by at least 1 byte.
* See https://github.com/lz4/lz4/issues/859 for details
*/
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
/*! LZ4_decompress_safe_partial() :
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
* into destination buffer 'dst' of size 'dstCapacity'.
* Up to 'targetOutputSize' bytes will be decoded.
* The function stops decoding on reaching this objective.
* This can be useful to boost performance
* whenever only the beginning of a block is required.
*
* @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
* If source stream is detected malformed, function returns a negative result.
*
* Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
*
* Note 2 : targetOutputSize must be <= dstCapacity
*
* Note 3 : this function effectively stops decoding on reaching targetOutputSize,
* so dstCapacity is kind of redundant.
* This is because in older versions of this function,
* decoding operation would still write complete sequences.
* Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
* it could write more bytes, though only up to dstCapacity.
* Some "margin" used to be required for this operation to work properly.
* Thankfully, this is no longer necessary.
* The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
*
* Note 4 : If srcSize is the exact size of the block,
* then targetOutputSize can be any value,
* including larger than the block's decompressed size.
* The function will, at most, generate block's decompressed size.
*
* Note 5 : If srcSize is _larger_ than block's compressed size,
* then targetOutputSize **MUST** be <= block's decompressed size.
* Otherwise, *silent corruption will occur*.
*/
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
/*-*********************************************
* Streaming Compression Functions
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/**
Note about RC_INVOKED
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
- Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
and reports warning "RC4011: identifier truncated".
- To eliminate the warning, we surround long preprocessor symbol with
"#if !defined(RC_INVOKED) ... #endif" block that means
"skip this block when rc.exe is trying to read it".
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_resetStream_fast() : v1.9.0+
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
* (e.g., LZ4_compress_fast_continue()).
*
* An LZ4_stream_t must be initialized once before usage.
* This is automatically done when created by LZ4_createStream().
* However, should the LZ4_stream_t be simply declared on stack (for example),
* it's necessary to initialize it first, using LZ4_initStream().
*
* After init, start any new stream with LZ4_resetStream_fast().
* A same LZ4_stream_t can be re-used multiple times consecutively
* and compress multiple streams,
* provided that it starts each new stream with LZ4_resetStream_fast().
*
* LZ4_resetStream_fast() is much faster than LZ4_initStream(),
* but is not compatible with memory regions containing garbage data.
*
* Note: it's only useful to call LZ4_resetStream_fast()
* in the context of streaming compression.
* The *extState* functions perform their own resets.
* Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
*/
LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
/*! LZ4_loadDict() :
* Use this function to reference a static dictionary into LZ4_stream_t.
* The dictionary must remain available during compression.
* LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
* The same dictionary will have to be loaded on decompression side for successful decoding.
* Dictionary are useful for better compression of small data (KB range).
* While LZ4 accept any input as dictionary,
* results are generally better when using Zstandard's Dictionary Builder.
* Loading a size of 0 is allowed, and is the same as reset.
* @return : loaded dictionary size, in bytes (necessarily <= 64 KB)
*/
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_compress_fast_continue() :
* Compress 'src' content using data from previously compressed blocks, for better compression ratio.
* 'dst' buffer must be already allocated.
* If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
*
* @return : size of compressed block
* or 0 if there is an error (typically, cannot fit into 'dst').
*
* Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
* Each block has precise boundaries.
* Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
* It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
*
* Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
*
* Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
* Make sure that buffers are separated, by at least one byte.
* This construction ensures that each block only depends on previous block.
*
* Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
*
* Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
*/
LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_saveDict() :
* If last 64KB data cannot be guaranteed to remain available at its current memory location,
* save it into a safer place (char* safeBuffer).
* This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
* but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
* @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
*/
LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
/*-**********************************************
* Streaming Decompression Functions
* Bufferless synchronous API
************************************************/
typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
* creation / destruction of streaming decompression tracking context.
* A tracking context can be re-used multiple times.
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_setStreamDecode() :
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
* Use this function to start decompression of a new stream of blocks.
* A dictionary can optionally be set. Use NULL or size 0 for a reset order.
* Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
* @return : 1 if OK, 0 if error
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
/*! LZ4_decoderRingBufferSize() : v1.8.2+
* Note : in a ring buffer scenario (optional),
* blocks are presumed decompressed next to each other
* up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
* at which stage it resumes from beginning of ring buffer.
* When setting such a ring buffer for streaming decompression,
* provides the minimum size of this ring buffer
* to be compatible with any source respecting maxBlockSize condition.
* @return : minimum ring buffer size,
* or 0 if there is an error (invalid maxBlockSize).
*/
LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
/*! LZ4_decompress_safe_continue() :
* This decoding function allows decompression of consecutive blocks in "streaming" mode.
* The difference with the usual independent blocks is that
* new blocks are allowed to find references into former blocks.
* A block is an unsplittable entity, and must be presented entirely to the decompression function.
* LZ4_decompress_safe_continue() only accepts one block at a time.
* It's modeled after `LZ4_decompress_safe()` and behaves similarly.
*
* @LZ4_streamDecode : decompression state, tracking the position in memory of past data
* @compressedSize : exact complete size of one compressed block.
* @dstCapacity : size of destination buffer (which must be already allocated),
* must be an upper bound of decompressed size.
* @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
*
* The last 64KB of previously decoded data *must* remain available and unmodified
* at the memory position where they were previously decoded.
* If less than 64KB of data has been decoded, all the data must be present.
*
* Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
* - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
* maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
* In which case, encoding and decoding buffers do not need to be synchronized.
* Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
* - Synchronized mode :
* Decompression buffer size is _exactly_ the same as compression buffer size,
* and follows exactly same update rule (block boundaries at same positions),
* and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
* _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
* - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
* In which case, encoding and decoding buffers do not need to be synchronized,
* and encoding ring buffer can have any size, including small ones ( < 64 KB).
*
* Whenever these conditions are not possible,
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
LZ4LIB_API int
LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
const char* src, char* dst,
int srcSize, int dstCapacity);
/*! LZ4_decompress_safe_usingDict() :
* Works the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()
* However, it's stateless: it doesn't need any LZ4_streamDecode_t state.
* Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int
LZ4_decompress_safe_usingDict(const char* src, char* dst,
int srcSize, int dstCapacity,
const char* dictStart, int dictSize);
/*! LZ4_decompress_safe_partial_usingDict() :
* Behaves the same as LZ4_decompress_safe_partial()
* with the added ability to specify a memory segment for past data.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
int compressedSize,
int targetOutputSize, int maxOutputSize,
const char* dictStart, int dictSize);
#endif /* LZ4_H_2983827168210 */
/*^*************************************
* !!!!!! STATIC LINKING ONLY !!!!!!
***************************************/
/*-****************************************************************************
* Experimental section
*
* Symbols declared in this section must be considered unstable. Their
* signatures or semantics may change, or they may be removed altogether in the
* future. They are therefore only safe to depend on when the caller is
* statically linked against the library.
*
* To protect against unsafe usage, not only are the declarations guarded,
* the definitions are hidden by default
* when building LZ4 as a shared/dynamic library.
*
* In order to access these declarations,
* define LZ4_STATIC_LINKING_ONLY in your application
* before including LZ4's headers.
*
* In order to make their implementations accessible dynamically, you must
* define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
******************************************************************************/
#ifdef LZ4_STATIC_LINKING_ONLY
#ifndef LZ4_STATIC_3504398509
#define LZ4_STATIC_3504398509
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
#define LZ4LIB_STATIC_API LZ4LIB_API
#else
#define LZ4LIB_STATIC_API
#endif
/*! LZ4_compress_fast_extState_fastReset() :
* A variant of LZ4_compress_fast_extState().
*
* Using this variant avoids an expensive initialization step.
* It is only safe to call if the state buffer is known to be correctly initialized already
* (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
* From a high level, the difference is that
* this function initializes the provided state with a call to something like LZ4_resetStream_fast()
* while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
*/
LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_attach_dictionary() :
* This is an experimental API that allows
* efficient use of a static dictionary many times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
* in which the working stream references the dictionary stream in-place.
*
* Several assumptions are made about the state of the dictionary stream.
* Currently, only streams which have been prepared by LZ4_loadDict() should
* be expected to work.
*
* Alternatively, the provided dictionaryStream may be NULL,
* in which case any existing dictionary stream is unset.
*
* If a dictionary is provided, it replaces any pre-existing stream history.
* The dictionary contents are the only history that can be referenced and
* logically immediately precede the data compressed in the first subsequent
* compression call.
*
* The dictionary will only remain attached to the working stream through the
* first compression call, at the end of which it is cleared. The dictionary
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the first compression call on the stream.
*/
LZ4LIB_STATIC_API void
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! In-place compression and decompression
*
* It's possible to have input and output sharing the same buffer,
* for highly constrained memory environments.
* In both cases, it requires input to lay at the end of the buffer,
* and decompression to start at beginning of the buffer.
* Buffer size must feature some margin, hence be larger than final size.
*
* |<------------------------buffer--------------------------------->|
* |<-----------compressed data--------->|
* |<-----------decompressed size------------------>|
* |<----margin---->|
*
* This technique is more useful for decompression,
* since decompressed size is typically larger,
* and margin is short.
*
* In-place decompression will work inside any buffer
* which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
* This presumes that decompressedSize > compressedSize.
* Otherwise, it means compression actually expanded data,
* and it would be more efficient to store such data with a flag indicating it's not compressed.
* This can happen when data is not compressible (already compressed, or encrypted).
*
* For in-place compression, margin is larger, as it must be able to cope with both
* history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
* and data expansion, which can happen when input is not compressible.
* As a consequence, buffer size requirements are much higher,
* and memory savings offered by in-place compression are more limited.
*
* There are ways to limit this cost for compression :
* - Reduce history size, by modifying LZ4_DISTANCE_MAX.
* Note that it is a compile-time constant, so all compressions will apply this limit.
* Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
* so it's a reasonable trick when inputs are known to be small.
* - Require the compressor to deliver a "maximum compressed size".
* This is the `dstCapacity` parameter in `LZ4_compress*()`.
* When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
* in which case, the return code will be 0 (zero).
* The caller must be ready for these cases to happen,
* and typically design a backup scheme to send data uncompressed.
* The combination of both techniques can significantly reduce
* the amount of margin required for in-place compression.
*
* In-place compression can work in any buffer
* which size is >= (maxCompressedSize)
* with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
* LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
* so it's possible to reduce memory requirements by playing with them.
*/
#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
#endif
#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
#endif /* LZ4_STATIC_3504398509 */
#endif /* LZ4_STATIC_LINKING_ONLY */
#ifndef LZ4_H_98237428734687
#define LZ4_H_98237428734687
/*-************************************************************
* Private Definitions
**************************************************************
* Do not use these definitions directly.
* They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
* Accessing members will expose user code to API and/or ABI break in future versions of the library.
**************************************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# include
typedef int8_t LZ4_i8;
typedef uint8_t LZ4_byte;
typedef uint16_t LZ4_u16;
typedef uint32_t LZ4_u32;
#else
typedef signed char LZ4_i8;
typedef unsigned char LZ4_byte;
typedef unsigned short LZ4_u16;
typedef unsigned int LZ4_u32;
#endif
/*! LZ4_stream_t :
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_stream_t object.
**/
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
const LZ4_byte* dictionary;
const LZ4_stream_t_internal* dictCtx;
LZ4_u32 currentOffset;
LZ4_u32 tableType;
LZ4_u32 dictSize;
/* Implicit padding to ensure structure is aligned */
};
#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */
union LZ4_stream_u {
char minStateSize[LZ4_STREAM_MINSIZE];
LZ4_stream_t_internal internal_donotuse;
}; /* previously typedef'd to LZ4_stream_t */
/*! LZ4_initStream() : v1.9.0+
* An LZ4_stream_t structure must be initialized at least once.
* This is automatically done when invoking LZ4_createStream(),
* but it's not when the structure is simply declared on stack (for example).
*
* Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
* It can also initialize any arbitrary buffer of sufficient size,
* and will @return a pointer of proper type upon initialization.
*
* Note : initialization fails if size and alignment conditions are not respected.
* In which case, the function will @return NULL.
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead
**/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
/*! LZ4_streamDecode_t :
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
**/
typedef struct {
const LZ4_byte* externalDict;
const LZ4_byte* prefixEnd;
size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#define LZ4_STREAMDECODE_MINSIZE 32
union LZ4_streamDecode_u {
char minStateSize[LZ4_STREAMDECODE_MINSIZE];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */
/*-************************************
* Obsolete Functions
**************************************/
/*! Deprecation warnings
*
* Deprecated functions make the compiler generate a warning when invoked.
* This is meant to invite users to update their source code.
* Should deprecation warnings be a problem, it is generally possible to disable them,
* typically with -Wno-deprecated-declarations for gcc
* or _CRT_SECURE_NO_WARNINGS in Visual.
*
* Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
* before including the header file.
*/
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif defined(_MSC_VER)
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# else
# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
# define LZ4_DEPRECATED(message) /* disabled */
# endif
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/*! Obsolete compression functions (since v1.7.3) */
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/*! Obsolete decompression functions (since v1.8.0) */
LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
/* Obsolete streaming functions (since v1.7.0)
* degraded functionality; do not use!
*
* In order to perform streaming compression, these functions depended on data
* that is no longer tracked in the state. They have been preserved as well as
* possible: using them will still produce a correct output. However, they don't
* actually retain any history between compression calls. The compression ratio
* achieved will therefore be no better than compressing each chunk
* independently.
*/
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
/*! Obsolete streaming decoding functions (since v1.7.0) */
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
* These functions used to be faster than LZ4_decompress_safe(),
* but this is no longer the case. They are now slower.
* This is because LZ4_decompress_fast() doesn't know the input size,
* and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
* On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
* As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
*
* The last remaining LZ4_decompress_fast() specificity is that
* it can decompress a block without knowing its compressed size.
* Such functionality can be achieved in a more secure manner
* by employing LZ4_decompress_safe_partial().
*
* Parameters:
* originalSize : is the uncompressed size to regenerate.
* `dst` must be already allocated, its size must be >= 'originalSize' bytes.
* @return : number of bytes read from source buffer (== compressed size).
* The function expects to finish at block's end exactly.
* If the source stream is detected malformed, the function stops decoding and returns a negative result.
* note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
* However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
* Also, since match offsets are not validated, match reads from 'src' may underflow too.
* These issues never happen if input (compressed) data is correct.
* But they may happen if input data is invalid (error or intentional tampering).
* As a consequence, use these functions in trusted environments with trusted data **only**.
*/
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead")
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
/*! LZ4_resetStream() :
* An LZ4_stream_t structure must be initialized at least once.
* This is done with LZ4_initStream(), or LZ4_resetStream().
* Consider switching to LZ4_initStream(),
* invoking LZ4_resetStream() will trigger deprecation warnings in the future.
*/
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
#endif /* LZ4_H_98237428734687 */
#if defined (__cplusplus)
}
#endif
================================================
FILE: jni/msp/msp.c
================================================
#include
#include
#include
#include "msp.h"
uint16_t msp_data_from_msg(uint8_t message_buffer[], msp_msg_t *msg) {
// return size
construct_msp_command(message_buffer, msg->cmd, msg->payload, msg->size, msg->direction);
return msg->size + 6;
}
msp_error_e construct_msp_command(uint8_t message_buffer[], uint8_t command, uint8_t payload[], uint8_t size, msp_direction_e direction) {
uint8_t checksum;
message_buffer[0] = '$'; // Header
message_buffer[1] = 'M'; // MSP V1
if (direction == MSP_OUTBOUND) {
message_buffer[2] = '<';
} else {
message_buffer[2] = '>';
}
message_buffer[3] = size; // Payload Size
checksum = size;
message_buffer[4] = command; // Command
checksum ^= command;
for(uint8_t i = 0; i < size; i++) {
message_buffer[5 + i] = payload[i];
checksum ^= message_buffer[5 + i];
}
message_buffer[5 + size] = checksum;
return 0;
}
msp_error_e msp_process_data(msp_state_t *msp_state, uint8_t dat)
{
switch (msp_state->state)
{
default:
case MSP_IDLE: // look for begin
if (dat == '$')
{
msp_state->state = MSP_VERSION;
}
else
{
return MSP_ERR_HDR;
}
break;
case MSP_VERSION: // Look for 'M' (MSP V1, we do not support V2 at this time)
if (dat == 'M')
{
msp_state->state = MSP_DIRECTION;
}
else
{ // Got garbage instead, try again
msp_state->state = MSP_IDLE;
return MSP_ERR_HDR;
}
break;
case MSP_DIRECTION: // < for command, > for reply
msp_state->state = MSP_SIZE;
switch (dat)
{
case '<':
msp_state->message.direction = MSP_OUTBOUND;
break;
case '>':
msp_state->message.direction = MSP_INBOUND;
break;
default: // garbage, try again
msp_state->state = MSP_IDLE;
return MSP_ERR_HDR;
break;
}
break;
case MSP_SIZE: // next up is supposed to be size
msp_state->message.checksum = dat;
msp_state->message.size = dat;
msp_state->state = MSP_CMD;
if (msp_state->message.size > 256)
{ // bogus message, too big. this can't actually happen but good to check
msp_state->state = MSP_IDLE;
return MSP_ERR_LEN;
break;
}
break;
case MSP_CMD: // followed by command
msp_state->message.cmd = dat;
msp_state->message.checksum ^= dat;
msp_state->buf_ptr = 0;
if (msp_state->message.size > 0)
{
msp_state->state = MSP_PAYLOAD;
}
else
{
msp_state->state = MSP_CHECKSUM;
}
break;
case MSP_PAYLOAD: // if we had a payload, keep going
msp_state->message.payload[msp_state->buf_ptr] = dat;
msp_state->message.checksum ^= dat;
msp_state->buf_ptr++;
if (msp_state->buf_ptr == msp_state->message.size)
{
msp_state->buf_ptr = 0;
msp_state->state = MSP_CHECKSUM;
}
break;
case MSP_CHECKSUM:
if (msp_state->message.checksum == dat)
{
if (msp_state->cb != 0)
{
msp_state->cb(&msp_state->message);
}
memset(&msp_state->message, 0, sizeof(msp_msg_t));
msp_state->state = MSP_IDLE;
break;
}
else
{
msp_state->state = MSP_IDLE;
return MSP_ERR_CKS;
}
break;
}
return MSP_ERR_NONE;
}
================================================
FILE: jni/msp/msp.h
================================================
#pragma once
#include
#define MSP_CMD_API_VERSION 1
#define MSP_CMD_FC_VARIANT 2
#define MSP_CMD_FC_VERSION 3
#define MSP_CMD_NAME 10
#define MSP_CMD_FILTER_CONFIG 92
#define MSP_CMD_PID_ADVANCED 94
#define MSP_CMD_STATUS 101
#define MSP_CMD_RC 105
#define MSP_CMD_ANALOG 110
#define MSP_CMD_RC_TUNING 111
#define MSP_CMD_PID 112
#define MSP_CMD_BATTERY_STATE 130
#define MSP_CMD_STATUS_EX 150
#define MSP_CMD_DISPLAYPORT 182
#define MSP_CMD_SET_OSD_CANVAS 188
typedef enum {
MSP_ERR_NONE,
MSP_ERR_HDR,
MSP_ERR_LEN,
MSP_ERR_CKS
} msp_error_e;
typedef enum {
MSP_IDLE,
MSP_VERSION,
MSP_DIRECTION,
MSP_SIZE,
MSP_CMD,
MSP_PAYLOAD,
MSP_CHECKSUM,
} msp_state_machine_e;
typedef enum {
MSP_INBOUND,
MSP_OUTBOUND
} msp_direction_e;
typedef struct msp_msg_s {
uint8_t checksum;
uint8_t cmd;
uint8_t size;
msp_direction_e direction;
uint8_t payload[256];
} msp_msg_t;
typedef void (*msp_msg_callback)(msp_msg_t *);
typedef struct msp_state_s {
msp_msg_callback cb;
msp_state_machine_e state;
uint8_t buf_ptr;
msp_msg_t message;
} msp_state_t;
uint16_t msp_data_from_msg(uint8_t message_buffer[], msp_msg_t *msg);
msp_error_e construct_msp_command(uint8_t message_buffer[], uint8_t command, uint8_t payload[], uint8_t size, msp_direction_e direction);
msp_error_e msp_process_data(msp_state_t *msp_state, uint8_t dat);
================================================
FILE: jni/msp/msp_displayport.c
================================================
#include "msp.h"
#include "msp_displayport.h"
static void process_draw_string(displayport_vtable_t *display_driver, uint8_t *payload) {
if(!display_driver || !display_driver->draw_character) return;
uint8_t row = payload[0];
uint8_t col = payload[1];
uint8_t attrs = payload[2]; // INAV and Betaflight use this to specify a higher page number.
uint8_t str_len;
for(str_len = 1; str_len < 255; str_len++) {
if(payload[2 + str_len] == '\0') {
break;
}
}
for(uint8_t idx = 0; idx < (str_len - 1); idx++) {
uint16_t character = payload[3 + idx];
if(attrs & 0x3) {
// shift over by the page number if they were specified
character |= ((attrs & 0x3) * 0x100);
}
display_driver->draw_character(col, row, character);
col++;
}
}
static void process_clear_screen(displayport_vtable_t *display_driver) {
if(!display_driver || !display_driver->clear_screen) return;
display_driver->clear_screen();
}
static void process_draw_complete(displayport_vtable_t *display_driver) {
if(!display_driver || !display_driver->draw_complete) return;
display_driver->draw_complete();
}
static void process_set_options(displayport_vtable_t *display_driver, uint8_t *payload) {
if(!display_driver || !display_driver->set_options) return;
uint8_t font = payload[0];
msp_hd_options_e is_hd = payload[1];
display_driver->set_options(font, is_hd);
}
static void process_open(displayport_vtable_t *display_driver) {
}
static void process_close(displayport_vtable_t *display_driver) {
process_clear_screen(display_driver);
}
int displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t *msg) {
if (msg->direction != MSP_INBOUND) {
return 1;
}
if (msg->cmd != MSP_CMD_DISPLAYPORT) {
return 1;
}
msp_displayport_cmd_e sub_cmd = msg->payload[0];
switch(sub_cmd) {
case MSP_DISPLAYPORT_KEEPALIVE: // 0 -> Open/Keep-Alive DisplayPort
process_open(display_driver);
break;
case MSP_DISPLAYPORT_CLOSE: // 1 -> Close DisplayPort
process_close(display_driver);
break;
case MSP_DISPLAYPORT_CLEAR: // 2 -> Clear Screen
process_clear_screen(display_driver);
break;
case MSP_DISPLAYPORT_DRAW_STRING: // 3 -> Draw String
process_draw_string(display_driver, &msg->payload[1]);
break;
case MSP_DISPLAYPORT_DRAW_SCREEN: // 4 -> Draw Screen
process_draw_complete(display_driver);
break;
case MSP_DISPLAYPORT_SET_OPTIONS: // 5 -> Set Options (HDZero/iNav)
process_set_options(display_driver, &msg->payload[1]);
break;
default:
break;
}
return 0;
}
================================================
FILE: jni/msp/msp_displayport.h
================================================
#pragma once
#include
#include "msp.h"
typedef enum {
MSP_DISPLAYPORT_KEEPALIVE,
MSP_DISPLAYPORT_CLOSE,
MSP_DISPLAYPORT_CLEAR,
MSP_DISPLAYPORT_DRAW_STRING,
MSP_DISPLAYPORT_DRAW_SCREEN,
MSP_DISPLAYPORT_SET_OPTIONS,
MSP_DISPLAYPORT_DRAW_SYSTEM
} msp_displayport_cmd_e;
typedef enum {
MSP_SD_OPTION_30_16,
MSP_HD_OPTION_50_18,
MSP_HD_OPTION_30_16,
MSP_HD_OPTION_60_22
} msp_hd_options_e;
typedef void (*draw_character_func)(uint32_t x, uint32_t y, uint16_t c);
typedef void (*set_options_func)(uint8_t font, msp_hd_options_e is_hd);
typedef void (*clear_screen_func)();
typedef void (*draw_complete_func)();
typedef struct displayport_vtable_s {
draw_character_func draw_character;
clear_screen_func clear_screen;
draw_complete_func draw_complete;
set_options_func set_options;
} displayport_vtable_t;
int displayport_process_message(displayport_vtable_t *display_driver, msp_msg_t *msg);
================================================
FILE: jni/msp_displayport_mux.c
================================================
#include
#include
#include
#include
#include
#include
#include "hw/dji_radio_shm.h"
#include "json/osd_config.h"
#include "lz4/lz4.h"
#include "net/data_protocol.h"
#include "net/network.h"
#include "net/serial.h"
#include "msp/msp.h"
#include "msp/msp_displayport.h"
#include "util/debug.h"
#include "util/time_util.h"
#include "util/fs_util.h"
#define CPU_TEMP_PATH "/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/temp1"
#define AU_VOLTAGE_PATH "/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage4"
#define FAST_SERIAL_KEY "fast_serial"
#define CACHE_SERIAL_KEY "cache_serial"
#define COMPRESS_KEY "compress_osd"
#define UPDATE_RATE_KEY "osd_update_rate_hz"
#define NO_BTFL_HD_KEY "disable_betaflight_hd"
// The MSP_PORT is used to send MSP passthrough messages.
// The DATA_PORT is used to send arbitrary data - for example, bitrate and temperature data.
#define MSP_PORT 7654
#define DATA_PORT 7655
#define COMPRESSED_DATA_PORT 7656
#define COMPRESSED_DATA_VERSION 1
enum {
MAX_DISPLAY_X = 60,
MAX_DISPLAY_Y = 22
};
// The Betaflight MSP minor version in which MSP DisplayPort sizing is supported.
#define MSP_DISPLAY_SIZE_VERSION 45
typedef struct msp_cache_entry_s {
struct timespec time;
msp_msg_t message;
} msp_cache_entry_t;
static msp_cache_entry_t *msp_message_cache[256]; // make a slot for all possible messages
static uint8_t frame_buffer[8192]; // buffer a whole frame of MSP commands until we get a draw command
static uint32_t fb_cursor = 0;
static uint8_t message_buffer[256]; // only needs to be the maximum size of an MSP packet, we only care to fwd MSP
static char current_fc_identifier[4];
/* For compressed full-frame transmission */
static uint16_t msp_character_map_buffer[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static uint16_t msp_character_map_draw[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static msp_hd_options_e msp_hd_option = 0;
static displayport_vtable_t *display_driver = NULL;
LZ4_stream_t *lz4_ref_ctx = NULL;
uint8_t update_rate_hz = 2;
int pty_fd;
int serial_fd;
int socket_fd;
int compressed_fd;
static volatile sig_atomic_t quit = 0;
static uint8_t serial_passthrough = 1;
static uint8_t compress = 0;
static uint8_t no_btfl_hd = 0;
static void sig_handler(int _)
{
quit = 1;
}
static uint8_t cache_msp_message(msp_msg_t *msp_message) {
// 0 -> cache overwritten
// 1 -> freshly cached
uint8_t retval = 0;
msp_cache_entry_t *cache_message = msp_message_cache[msp_message->cmd];
if (cache_message == NULL) {
DEBUG_PRINT("FC -> AU CACHE: no entry for msg %d, allocating\n", msp_message->cmd);
cache_message = calloc(1, sizeof(msp_cache_entry_t));
msp_message_cache[msp_message->cmd] = cache_message;
retval = 1;
}
DEBUG_PRINT ("FC -> AU CACHE: refreshing %d\n", msp_message->cmd);
memcpy(&cache_message->message, msp_message, sizeof(msp_msg_t));
clock_gettime(CLOCK_MONOTONIC, &cache_message->time);
return retval;
}
static int16_t msp_msg_from_cache(uint8_t msg_buffer[], uint8_t cmd_id) {
// returns size of message or -1
msp_cache_entry_t *cache_message = msp_message_cache[cmd_id];
if (cache_message == NULL) {
// cache missed, return -1 to trigger a serial send
return -1;
} else {
// cache hit, let's see if it's too old
// messages we care to expire after second boundary
if(cmd_id == MSP_CMD_ANALOG
|| cmd_id == MSP_CMD_STATUS
|| cmd_id == MSP_CMD_BATTERY_STATE) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if(timespec_subtract_ns(&now, &cache_message->time) > NSEC_PER_SEC) {
// message is too old, invalidate cache and force a resend
DEBUG_PRINT("MSP cache EXPIRED %d\n", cmd_id);
free(cache_message);
msp_message_cache[cmd_id] = 0;
return -1;
}
}
// message existed and was not stale, send it back
return msp_data_from_msg(msg_buffer, &cache_message->message);
}
}
static void send_display_size(int serial_fd) {
uint8_t buffer[8];
uint8_t payload[2] = {MAX_DISPLAY_X, MAX_DISPLAY_Y};
construct_msp_command(buffer, MSP_CMD_SET_OSD_CANVAS, payload, 2, MSP_OUTBOUND);
write(serial_fd, &buffer, sizeof(buffer));
}
static void send_variant_request(int serial_fd) {
uint8_t buffer[6];
construct_msp_command(buffer, MSP_CMD_FC_VARIANT, NULL, 0, MSP_OUTBOUND);
write(serial_fd, &buffer, sizeof(buffer));
}
static void send_version_request(int serial_fd) {
uint8_t buffer[6];
construct_msp_command(buffer, MSP_CMD_API_VERSION, NULL, 0, MSP_OUTBOUND);
write(serial_fd, &buffer, sizeof(buffer));
}
static void copy_to_msp_frame_buffer(void *buffer, uint16_t size) {
memcpy(&frame_buffer[fb_cursor], buffer, size);
fb_cursor += size;
}
static void rx_msp_callback(msp_msg_t *msp_message)
{
// Process a received MSP message from FC and decide whether to send it to the PTY (DJI) or UDP port (MSP-OSD on Goggles)
DEBUG_PRINT("FC->AU MSP msg %d with data len %d \n", msp_message->cmd, msp_message->size);
switch(msp_message->cmd) {
case MSP_CMD_DISPLAYPORT: {
if (compress) {
// This was an MSP DisplayPort message and we're in compressed mode, so pass it off to the DisplayPort handlers.
displayport_process_message(display_driver, msp_message);
} else {
// This was an MSP DisplayPort message and we're in legacy mode, so buffer it until we get a whole frame.
if(fb_cursor > sizeof(frame_buffer)) {
printf("Exhausted frame buffer! Resetting...\n");
fb_cursor = 0;
return;
}
uint16_t size = msp_data_from_msg(message_buffer, msp_message);
copy_to_msp_frame_buffer(message_buffer, size);
if(msp_message->payload[0] == MSP_DISPLAYPORT_DRAW_SCREEN) {
// Once we have a whole frame of data, send it to the goggles.
write(socket_fd, frame_buffer, fb_cursor);
DEBUG_PRINT("DRAW! wrote %d bytes\n", fb_cursor);
fb_cursor = 0;
}
}
break;
}
case MSP_CMD_FC_VARIANT: {
// This is an FC Variant response, so we want to use it to set our FC variant.
DEBUG_PRINT("Got FC Variant response!\n");
if(strncmp(current_fc_identifier, msp_message->payload, 4) != 0) {
// FC variant changed or was updated. Update the current FC identifier and send an MSP version request.
memcpy(current_fc_identifier, msp_message->payload, 4);
send_version_request(serial_fd);
}
break;
}
case MSP_CMD_API_VERSION: {
// Got an MSP API version response. Compare the version if we have Betaflight in order to see if we should send the new display size message.
if(strncmp(current_fc_identifier, "BTFL", 4) == 0) {
uint8_t msp_minor_version = msp_message->payload[2];
DEBUG_PRINT("Got Betaflight minor MSP version %d\n", msp_minor_version);
if(msp_minor_version >= MSP_DISPLAY_SIZE_VERSION) {
if(!no_btfl_hd) {
if(!compress) {
// If compression is disabled, we need to manually inject a canvas-change command into the command stream.
uint8_t displayport_set_size[3] = {MSP_DISPLAYPORT_SET_OPTIONS, 0, MSP_HD_OPTION_60_22};
construct_msp_command(message_buffer, MSP_CMD_DISPLAYPORT, displayport_set_size, 3, MSP_INBOUND);
copy_to_msp_frame_buffer(message_buffer, 9);
DEBUG_PRINT("Sent display size to goggles\n");
}
// Betaflight with HD support. Send our display size and set 60x22.
send_display_size(serial_fd);
msp_hd_option = MSP_HD_OPTION_60_22;
DEBUG_PRINT("Sent display size to FC\n");
}
}
}
break;
}
default: {
uint16_t size = msp_data_from_msg(message_buffer, msp_message);
if(serial_passthrough || cache_msp_message(msp_message)) {
// Either serial passthrough was on, or the cache was enabled but missed (a response was not available).
// Either way, this means we need to send the message through to DJI.
write(pty_fd, message_buffer, size);
}
break;
}
}
}
static void tx_msp_callback(msp_msg_t *msp_message)
{
// We got a valid message from DJI asking for something. See if there's a response in the cache or not.
// We can only get here if serial passthrough is off and caching is on, so no need to check again.
DEBUG_PRINT("DJI->FC MSP msg %d with request len %d \n", msp_message->cmd, msp_message->size);
uint8_t send_buffer[256];
int16_t size;
if(0 < (size = msp_msg_from_cache(send_buffer, msp_message->cmd))) {
// cache hit, so write the cached message straight back to DJI
DEBUG_PRINT("DJI->FC MSP CACHE HIT msg %d with response len %d \n", msp_message->cmd, size);
for(int i = 0; i < size; i++) {
DEBUG_PRINT("%02X ", send_buffer[i]);
}
DEBUG_PRINT("\n");
write(pty_fd, send_buffer, size);
} else {
// cache miss, so write the DJI request to serial and wait for the FC to come back.
DEBUG_PRINT("DJI->FC MSP CACHE MISS msg %d\n",msp_message->cmd);
uint16_t size = msp_data_from_msg(message_buffer, msp_message);
write(serial_fd, message_buffer, size);
}
}
static void send_data_packet(int data_socket_fd, dji_shm_state_t *dji_shm) {
packet_data_t data;
memset(&data, 0, sizeof(data));
data.version_specifier = 0xFFFF;
data.tx_temperature = get_int_from_fs(CPU_TEMP_PATH);
data.tx_voltage = get_int_from_fs(AU_VOLTAGE_PATH);
memcpy(data.fc_variant, current_fc_identifier, sizeof(current_fc_identifier));
DEBUG_PRINT("got voltage %f V temp %d C variant %.4s\n", (float)(data.tx_voltage / 64.0f), data.tx_temperature, data.fc_variant);
write(data_socket_fd, &data, sizeof(data));
}
/* MSP DisplayPort handlers for compressed mode */
static void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {
DEBUG_PRINT("drawing char %d at x %d y %d\n", c, x, y);
msp_character_map_buffer[x][y] = c;
}
static void msp_clear_screen() {
memset(msp_character_map_buffer, 0, sizeof(msp_character_map_buffer));
}
static void msp_draw_complete() {
memcpy(msp_character_map_draw, msp_character_map_buffer, sizeof(msp_character_map_buffer));
}
static void msp_set_options(uint8_t font_num, msp_hd_options_e is_hd) {
DEBUG_PRINT("Got options!\n");
msp_clear_screen();
msp_hd_option = is_hd;
}
static void send_compressed_screen(int compressed_fd) {
LZ4_stream_t current_stream_state;
uint8_t dest_buf[sizeof(compressed_data_header_t) + LZ4_COMPRESSBOUND(sizeof(msp_character_map_draw))];
void *dest = &dest_buf;
memcpy(¤t_stream_state, lz4_ref_ctx, sizeof(LZ4_stream_t));
int size = LZ4_compress_fast_extState_fastReset(¤t_stream_state, msp_character_map_draw, (dest + sizeof(compressed_data_header_t)), sizeof(msp_character_map_draw), LZ4_compressBound(sizeof(msp_character_map_draw)), 1);
compressed_data_header_t *dest_header = (compressed_data_header_t *)dest;
dest_header->hd_options =(uint16_t)msp_hd_option;
dest_header->version = COMPRESSED_DATA_VERSION;
write(compressed_fd, dest, size + sizeof(compressed_data_header_t));
DEBUG_PRINT("COMPRESSED: Sent %d bytes!\n", size);
}
int main(int argc, char *argv[]) {
memset(current_fc_identifier, 0, sizeof(current_fc_identifier));
memset(msp_character_map_buffer, 0, sizeof(msp_character_map_buffer));
memset(msp_character_map_draw, 0, sizeof(msp_character_map_draw));
int compression_dict_size = 0;
void *compression_dict = open_dict(COMPRESSED_DATA_VERSION, &compression_dict_size);
int opt;
uint8_t fast_serial = 0;
uint8_t msp_command_number = 0;
while((opt = getopt(argc, argv, "fsp")) != -1){
switch(opt){
case 'f':
fast_serial = 1;
break;
case 's':
serial_passthrough = 0;
break;
case '?':
printf("unknown option: %c\n", optopt);
break;
}
}
if((argc - optind) < 2) {
printf("usage: msp_displayport_mux [-f] [-s] ipaddr serial_port [pty_target]\n-s : enable serial caching\n-f : 230400 baud serial\n");
return 0;
}
if(get_boolean_config_value(FAST_SERIAL_KEY)) {
fast_serial = 1;
}
if(get_boolean_config_value(CACHE_SERIAL_KEY)) {
serial_passthrough = 0;
}
if(get_boolean_config_value(COMPRESS_KEY)) {
compress = 1;
}
if(get_boolean_config_value(NO_BTFL_HD_KEY)) {
no_btfl_hd = 1;
}
if(fast_serial == 1) {
printf("Configured to use 230400 baud rate. \n");
}
if(serial_passthrough == 0) {
printf("Configured to use serial caching. \n");
}
dji_shm_state_t dji_radio;
memset(&dji_radio, 0, sizeof(dji_radio));
open_dji_radio_shm(&dji_radio);
char *ip_address = argv[optind];
char *serial_port = argv[optind + 1];
signal(SIGINT, sig_handler);
struct pollfd poll_fds[2];
const char *pty_name_ptr;
msp_state_t *rx_msp_state = calloc(1, sizeof(msp_state_t));
msp_state_t *tx_msp_state = calloc(1, sizeof(msp_state_t));
rx_msp_state->cb = &rx_msp_callback;
tx_msp_state->cb = &tx_msp_callback;
serial_fd = open_serial_port(serial_port, fast_serial ? B230400 : B115200);
if (serial_fd <= 0) {
printf("Failed to open serial port!\n");
return 1;
}
pty_fd = open_pty(&pty_name_ptr);
printf("Allocated PTY %s\n", pty_name_ptr);
if ((argc - optind) > 2) {
unlink(argv[optind + 2]);
symlink(pty_name_ptr, argv[optind + 2]);
printf("Relinked %s to %s\n", argv[optind + 2], pty_name_ptr);
}
socket_fd = connect_to_server(ip_address, MSP_PORT);
compressed_fd = connect_to_server(ip_address, COMPRESSED_DATA_PORT);
int data_fd = connect_to_server(ip_address, DATA_PORT);
if (compress) {
update_rate_hz = get_integer_config_value(UPDATE_RATE_KEY);
display_driver = calloc(1, sizeof(displayport_vtable_t));
display_driver->draw_character = &msp_draw_character;
display_driver->clear_screen = &msp_clear_screen;
display_driver->draw_complete = &msp_draw_complete;
display_driver->set_options = &msp_set_options;
lz4_ref_ctx = LZ4_createStream();
LZ4_loadDict(lz4_ref_ctx, compression_dict, compression_dict_size);
}
uint8_t serial_data[256];
ssize_t serial_data_size;
struct timespec now, last_data, last_frame;
clock_gettime(CLOCK_MONOTONIC, &now);
clock_gettime(CLOCK_MONOTONIC, &last_data);
clock_gettime(CLOCK_MONOTONIC, &last_frame);
while (!quit) {
poll_fds[0].fd = serial_fd;
poll_fds[1].fd = pty_fd;
poll_fds[0].events = POLLIN;
poll_fds[1].events = POLLIN;
poll(poll_fds, 2, ((MSEC_PER_SEC / update_rate_hz) / 2));
// We got inbound serial data, process it as MSP data.
if (0 < (serial_data_size = read(serial_fd, serial_data, sizeof(serial_data)))) {
DEBUG_PRINT("RECEIVED data! length %d\n", serial_data_size);
for (ssize_t i = 0; i < serial_data_size; i++) {
msp_process_data(rx_msp_state, serial_data[i]);
}
}
// We got data from DJI (the pty), so see what to do next:
if(0 < (serial_data_size = read(pty_fd, serial_data, sizeof(serial_data)))) {
if (serial_passthrough) {
// If serial passthrough is enabled, send the message through verbatim.
DEBUG_PRINT("SEND data! length %d\n", serial_data_size);
for (ssize_t i= 0; i < serial_data_size; i++) {
DEBUG_PRINT("%02X ", serial_data[i]);
}
DEBUG_PRINT("\n");
write(serial_fd, &serial_data, serial_data_size);
} else {
// Otherwise, queue it up for processing by the MSP layer.
DEBUG_PRINT("SEND data to MSP buffer! length %d\n", serial_data_size);
for (ssize_t i = 0; i < serial_data_size; i++) {
msp_process_data(tx_msp_state, serial_data[i]);
}
}
}
clock_gettime(CLOCK_MONOTONIC, &now);
if(timespec_subtract_ns(&now, &last_data) > (NSEC_PER_SEC / 2)) {
// More than 500ms have elapsed, let's go ahead and send a data frame
clock_gettime(CLOCK_MONOTONIC, &last_data);
send_data_packet(data_fd, &dji_radio);
if(current_fc_identifier[0] == 0) {
send_variant_request(serial_fd);
}
}
if(compress && (timespec_subtract_ns(&now, &last_frame) > (NSEC_PER_SEC / update_rate_hz))) {
send_compressed_screen(compressed_fd);
clock_gettime(CLOCK_MONOTONIC, &last_frame);
}
}
close_dji_radio_shm(&dji_radio);
close(serial_fd);
close(pty_fd);
close(socket_fd);
close(data_fd);
close(compressed_fd);
if (display_driver != NULL) {
free(display_driver);
}
if (lz4_ref_ctx != NULL) {
free(lz4_ref_ctx);
}
free(rx_msp_state);
free(tx_msp_state);
}
================================================
FILE: jni/net/data_protocol.h
================================================
#include
typedef struct packet_data_s {
uint16_t tx_temperature;
uint16_t version_specifier; // Used to be bitrate! Danger! 0xFFFF means V2 (no bitrate) for now.
uint16_t tx_voltage;
char fc_variant[4];
} __attribute__((packed)) packet_data_t;
typedef struct compressed_data_header_s {
uint16_t version;
uint16_t hd_options;
} __attribute__((packed)) compressed_data_header_t;
================================================
FILE: jni/net/network.c
================================================
#include
#include
#include
#include
#include
#include
#include "network.h"
int connect_to_server(char *address, int port)
{
int sockfd;
struct sockaddr_in servaddr;
// socket create and verification
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
printf("socket failed!\n");
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(address);
servaddr.sin_port = htons(port);
if (connect(sockfd, &servaddr, sizeof(servaddr)) != 0)
{
printf("connection failed!\n");
return -1;
}
fcntl(sockfd, F_SETFL, O_NONBLOCK);
return sockfd;
}
int bind_socket(int port)
{
struct sockaddr_in si_me;
int s;
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
printf("Failed to get socket!\n");
return -1;
}
memset((char *)&si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(port);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
// bind socket to port
if (bind(s, (struct sockaddr *)&si_me, sizeof(si_me)) == -1)
{
printf("Failed to get bound!\n");
return -1;
}
return s;
}
================================================
FILE: jni/net/network.h
================================================
#pragma once
int connect_to_server(char *address, int port);
int bind_socket(int port);
================================================
FILE: jni/net/serial.c
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __APPLE__
#include
#endif
#include "serial.h"
int open_serial_port(const char *device, speed_t baudrate)
{
struct termios tio;
int tty_fd;
memset(&tio, 0, sizeof(tio));
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_lflag = 0;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tty_fd = open(device, O_RDWR | O_NONBLOCK);
cfsetospeed(&tio, baudrate);
cfsetispeed(&tio, baudrate);
tcsetattr(tty_fd, TCSANOW, &tio);
return tty_fd;
}
int open_pty(const char **pty_name)
{
int app_pty;
int virtual_serial_pty;
// openpty makes a connection between the first argument, a parent FD which acts as the "receiving" end of the virtual PTY
// and the second argument, a child FD which represents a "virtual serial port."
// using ttyname on the second argument will return a /dev which can be opened as a serial port
pid_t pid = openpty(&app_pty, &virtual_serial_pty, NULL, NULL, NULL);
if (pid != 0)
{
printf("Could not get PTY!");
exit(1);
return pid;
}
(*pty_name) = ttyname(virtual_serial_pty);
struct termios orig_termios;
if (tcgetattr(app_pty, &orig_termios) < 0)
{
printf("Could not get termio for current FD");
return -1;
}
orig_termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
orig_termios.c_oflag &= ~(OPOST);
orig_termios.c_cflag |= (CS8);
orig_termios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
orig_termios.c_cc[VMIN] = 0;
orig_termios.c_cc[VTIME] = 1;
if (tcsetattr(app_pty, TCSANOW, &orig_termios) < 0)
{
perror("Could not set termio for current FD");
return -1;
}
if (tcgetattr(virtual_serial_pty, &orig_termios) < 0)
{
printf("Could not get termio for current FD");
return -1;
}
orig_termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
orig_termios.c_oflag &= ~(OPOST);
orig_termios.c_cflag |= (CS8);
orig_termios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
orig_termios.c_cc[VMIN] = 0;
orig_termios.c_cc[VTIME] = 1;
if (tcsetattr(virtual_serial_pty, TCSANOW, &orig_termios) < 0)
{
perror("Could not set termio for current FD");
return -1;
}
fcntl(app_pty, F_SETFL, O_NONBLOCK);
return app_pty; // Return the file descriptor
}
#ifdef DEBUG_TRACE
void _trace(const char* format, ...);
#endif
#ifdef DEBUG_TRACE
#define TRACE(X) _trace X;
#else /*DEBUG_TRACE*/
#define TRACE(X)
#endif /*DEBUG_TRACE*/
#ifdef __ANDROID__
int
openpty (int *amaster, int *aslave, char *name, struct termios *termp,
struct winsize *winp)
{
int master, slave;
char *name_slave;
master = open("/dev/ptmx", O_RDWR | O_NONBLOCK);
if (master == -1) {
TRACE(("Fail to open master"))
return -1;
}
if (grantpt(master))
goto fail;
if (unlockpt(master))
goto fail;
name_slave = ptsname(master);
TRACE(("openpty: slave name %s", name_slave))
slave = open(name_slave, O_RDWR | O_NOCTTY);
if (slave == -1)
{
goto fail;
}
if(termp)
tcsetattr(slave, TCSAFLUSH, termp);
if (winp)
ioctl (slave, TIOCSWINSZ, winp);
*amaster = master;
*aslave = slave;
if (name != NULL)
strcpy(name, name_slave);
return 0;
fail:
close (master);
return -1;
}
#endif
================================================
FILE: jni/net/serial.h
================================================
#pragma once
#include
int open_serial_port(const char *device, speed_t baudrate);
int open_pty(const char **pty_name);
#ifdef __ANDROID__
int
openpty (int *amaster, int *aslave, char *name, struct termios *termp,
struct winsize *winp);
#endif
================================================
FILE: jni/osd.h
================================================
#pragma once
#include "hw/dji_display.h"
void osd_directfb(duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle);
void osd_disable();
void osd_enable();
================================================
FILE: jni/osd_dji_overlay_udp.c
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "hw/dji_display.h"
#include "hw/dji_radio_shm.h"
#include "hw/dji_services.h"
#include "json/osd_config.h"
#include "fakehd/fakehd.h"
#include "font/font.h"
#include "lz4/lz4.h"
#include "toast/toast.h"
#include "net/network.h"
#include "net/data_protocol.h"
#include "msp/msp.h"
#include "msp/msp_displayport.h"
#include "util/debug.h"
#include "util/display_info.h"
#include "util/fs_util.h"
#include "util/time_util.h"
#include "rec/rec.h"
#include "rec/rec_pb.h"
#define MSP_PORT 7654
#define DATA_PORT 7655
#define COMPRESSED_DATA_PORT 7656
#define DICTIONARY_VERSION 1
#define WIDTH 1440
#define HEIGHT 810
#define BYTES_PER_PIXEL 4
#define PLANE_ID 6
#define GOGGLES_V1_VOFFSET 575
#define GOGGLES_V2_VOFFSET 215
#define INPUT_FILENAME "/dev/input/event0"
#define SPLASH_STRING "OSD WAITING..."
#define SHUTDOWN_STRING "SHUTTING DOWN..."
#define SPLASH_KEY "show_waiting"
#define FORCE_RENDER_HZ 2
#define TOAST_HZ 2
#define GOGGLES_VOLTAGE_PATH "/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage5"
#define EV_CODE_BACK 0xc9
#define BACK_BUTTON_DELAY 4
#define SWAP32(data) \
( (((data) >> 24) & 0x000000FF) | (((data) >> 8) & 0x0000FF00) | \
(((data) << 8) & 0x00FF0000) | (((data) << 24) & 0xFF000000) )
#define MAX_DISPLAY_X 60
#define MAX_DISPLAY_Y 22
static void rec_msp_draw_complete_hook();
static volatile sig_atomic_t quit = 0;
static dji_display_state_t *dji_display;
static uint16_t msp_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static uint16_t msp_render_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static uint16_t overlay_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static displayport_vtable_t *display_driver;
struct timespec last_render;
static char current_fc_variant[5];
static uint8_t frame_waiting = 0;
static display_info_t sd_display_info = {
.char_width = 30,
.char_height = 15,
.font_width = 36,
.font_height = 54,
.x_offset = 180,
.y_offset = 0,
.fonts = {NULL, NULL, NULL, NULL},
};
static display_info_t full_display_info = {
.char_width = 60,
.char_height = 22,
.font_width = 24,
.font_height = 36,
.x_offset = 0,
.y_offset = 0,
.fonts = {NULL, NULL, NULL, NULL},
};
static display_info_t hd_display_info = {
.char_width = 50,
.char_height = 18,
.font_width = 24,
.font_height = 36,
.x_offset = 120,
.y_offset = 80,
.fonts = {NULL, NULL, NULL, NULL},
};
static display_info_t overlay_display_info = {
.char_width = 20,
.char_height = 10,
.font_width = 24,
.font_height = 36,
.x_offset = 960,
.y_offset = 450,
.fonts = {NULL, NULL, NULL, NULL},
};
static enum display_mode_s {
DISPLAY_DISABLED = 0,
DISPLAY_RUNNING = 1,
DISPLAY_WAITING = 2
} display_mode = DISPLAY_RUNNING;
static display_info_t *current_display_info;
int event_fd;
static void draw_character(display_info_t *display_info, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y], uint32_t x, uint32_t y, uint16_t c)
{
if ((x > (display_info->char_width - 1)) || (y > (display_info->char_height - 1))) {
return;
}
character_map[x][y] = c;
}
static void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {
draw_character(current_display_info, msp_character_map, x, y, c);
}
/* Main rendering function: take a character_map and a display_info and draw it into a framebuffer */
static void draw_character_map(display_info_t *display_info, void* restrict fb_addr, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y]) {
if (display_info->fonts[0] == NULL) {
DEBUG_PRINT("No font available, failed to draw.\n");
return;
}
void* restrict font;
for(int y = 0; y < display_info->char_height; y++) {
for(int x = 0; x < display_info->char_width; x++) {
uint16_t c = character_map[x][y];
if (c != 0) {
int page = (c & 0x300) >> 8;
c = c & 0xFF;
font = display_info->fonts[page];
if(font == NULL) {
font = display_info->fonts[0];
}
uint32_t pixel_x = (x * display_info->font_width) + display_info->x_offset;
uint32_t pixel_y = (y * display_info->font_height) + display_info->y_offset;
uint32_t font_offset = (((display_info->font_height * display_info->font_width) * BYTES_PER_PIXEL) * c);
uint32_t target_offset = ((pixel_x * BYTES_PER_PIXEL) + (pixel_y * WIDTH * BYTES_PER_PIXEL));
for(uint8_t gy = 0; gy < display_info->font_height; gy++) {
for(uint8_t gx = 0; gx < display_info->font_width; gx++) {
*((uint8_t *)fb_addr + target_offset) = *(uint8_t *)((uint8_t *)font + font_offset + 2);
*((uint8_t *)fb_addr + target_offset + 1) = *(uint8_t *)((uint8_t *)font + font_offset + 1);
*((uint8_t *)fb_addr + target_offset + 2) = *(uint8_t *)((uint8_t *)font + font_offset);
*((uint8_t *)fb_addr + target_offset + 3) = ~*(uint8_t *)((uint8_t *)font + font_offset + 3);
font_offset += BYTES_PER_PIXEL;
target_offset += BYTES_PER_PIXEL;
}
target_offset += WIDTH * BYTES_PER_PIXEL - (display_info->font_width * BYTES_PER_PIXEL);
}
// DEBUG_PRINT("%c", c > 31 ? c : 20);
}
// DEBUG_PRINT(" ");
}
// DEBUG_PRINT("\n");
}
}
static void clear_framebuffer() {
void *fb_addr = dji_display_get_fb_address(dji_display);
// DJI has a backwards alpha channel - FF is transparent, 00 is opaque.
memset(fb_addr, 0x000000FF, WIDTH * HEIGHT * BYTES_PER_PIXEL);
}
static void draw_screen() {
clear_framebuffer();
void *fb_addr = dji_display_get_fb_address(dji_display);
if (fakehd_is_enabled()) {
fakehd_map_sd_character_map_to_hd(msp_character_map, msp_render_character_map);
draw_character_map(current_display_info, fb_addr, msp_render_character_map);
} else {
draw_character_map(current_display_info, fb_addr, msp_character_map);
}
draw_character_map(&overlay_display_info, fb_addr, overlay_character_map);
}
static void clear_overlay() {
memset(overlay_character_map, 0, sizeof(overlay_character_map));
}
static void msp_clear_screen() {
memset(msp_character_map, 0, sizeof(msp_character_map));
memset(msp_render_character_map, 0, sizeof(msp_render_character_map));
}
static void render_screen() {
draw_screen();
if (display_mode == DISPLAY_DISABLED) {
clear_framebuffer();
}
dji_display_push_frame(dji_display);
DEBUG_PRINT("drew a frame\n");
clock_gettime(CLOCK_MONOTONIC, &last_render);
}
static void msp_draw_complete() {
render_screen();
if (rec_is_enabled()) {
rec_msp_draw_complete_hook();
if (rec_is_osd_recording() == true)
{
rec_write_frame(
fakehd_is_enabled() ? msp_render_character_map : msp_character_map,
MAX_DISPLAY_X * MAX_DISPLAY_Y);
}
}
}
static void msp_callback(msp_msg_t *msp_message)
{
displayport_process_message(display_driver, msp_message);
}
static void load_fonts(char* font_variant) {
char file_path[255];
get_font_path_with_extension(file_path, "font", ".png", 255, 0, font_variant);
toast(file_path);
load_font(&sd_display_info, font_variant);
load_font(&hd_display_info, font_variant);
load_font(&full_display_info, font_variant);
load_font(&overlay_display_info, font_variant);
}
static void close_all_fonts() {
close_font(&sd_display_info);
close_font(&hd_display_info);
close_font(&overlay_display_info);
close_font(&full_display_info);
}
static void msp_set_options(uint8_t font_num, msp_hd_options_e is_hd) {
msp_clear_screen();
switch (is_hd) {
case MSP_HD_OPTION_60_22:
fakehd_disable();
current_display_info = &full_display_info;
break;
case MSP_HD_OPTION_50_18:
case MSP_HD_OPTION_30_16:
fakehd_disable();
current_display_info = &hd_display_info;
break;
default:
current_display_info = &sd_display_info;
break;
}
}
static void display_print_string(uint8_t init_x, uint8_t y, const char *string, uint8_t len) {
for(uint8_t x = 0; x < len; x++) {
draw_character(&overlay_display_info, overlay_character_map, x + init_x, y, string[x]);
}
}
/* Display initialization and deinitialization */
static void start_display(uint8_t is_v2_goggles,duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle) {
memset(msp_character_map, 0, sizeof(msp_character_map));
memset(msp_render_character_map, 0, sizeof(msp_render_character_map));
memset(overlay_character_map, 0, sizeof(overlay_character_map));
dji_display = dji_display_state_alloc(is_v2_goggles);
dji_display_open_framebuffer_injected(dji_display, disp, ion_handle, PLANE_ID);
if(get_boolean_config_value(SPLASH_KEY)) {
display_print_string(0, overlay_display_info.char_height -1, SPLASH_STRING, sizeof(SPLASH_STRING));
}
msp_draw_complete();
}
static void stop_display() {
display_print_string(0, overlay_display_info.char_height -1, SHUTDOWN_STRING, sizeof(SHUTDOWN_STRING));
dji_display_close_framebuffer(dji_display);
dji_display_state_free(dji_display);
}
/* AU Voltage and Temp overlay */
#define SHOW_AU_DATA_KEY "show_au_data"
static int au_overlay_enabled = 0;
static void check_is_au_overlay_enabled()
{
DEBUG_PRINT("Checking for AU overlay enabled: ");
if (get_boolean_config_value(SHOW_AU_DATA_KEY))
{
DEBUG_PRINT("Enabled\n");
au_overlay_enabled = 1;
} else {
DEBUG_PRINT("Disabled\n");
}
}
static void process_data_packet(uint8_t *buf, int len, dji_shm_state_t *radio_shm) {
packet_data_t *packet = (packet_data_t *)buf;
DEBUG_PRINT("got data %04X version spec %d C %f V variant %.4s\n", packet->version_specifier, packet->tx_temperature, packet->tx_voltage / 64.0f, packet->fc_variant);
char str[8];
clear_overlay();
if(au_overlay_enabled) {
snprintf(str, 8, "%d C", packet->tx_temperature);
display_print_string(overlay_display_info.char_width - 5, overlay_display_info.char_height - 8, str, 5);
snprintf(str, 8, "A %2.1fV", packet->tx_voltage / 64.0f);
display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 7, str, 7);
}
if(len > 6) {
DEBUG_PRINT("Got new packet with variant %.4s\n", packet->fc_variant);
// should have FC type
if(strncmp(current_fc_variant, packet->fc_variant, 4) != 0) {
memcpy(current_fc_variant, &packet->fc_variant, 4);
DEBUG_PRINT("Changed current FC variant to %.4s\n", current_fc_variant);
toast("FC %.4s", current_fc_variant);
close_all_fonts();
load_fonts(current_fc_variant);
// This is not a typo - fill in any missing fonts for the current variant with the generic one.
load_fonts(FALLBACK_FONT);
}
}
}
static void process_compressed_data(void *buf, int len, void *dict, int dict_size) {
compressed_data_header_t *header = (compressed_data_header_t*)buf;
if (header->version != DICTIONARY_VERSION) {
return;
}
switch ((msp_hd_options_e)header->hd_options) {
case MSP_HD_OPTION_60_22:
fakehd_disable();
current_display_info = &full_display_info;
break;
case MSP_HD_OPTION_30_16:
case MSP_HD_OPTION_50_18:
fakehd_disable();
current_display_info = &hd_display_info;
break;
default:
if (fakehd_is_enabled()) {
memset(msp_render_character_map, 0, sizeof(msp_render_character_map));
current_display_info = &full_display_info;
} else {
current_display_info = &sd_display_info;
}
break;
}
int decompressed_size = LZ4_decompress_safe_usingDict((buf + sizeof(compressed_data_header_t)), msp_character_map, len - sizeof(compressed_data_header_t), sizeof(msp_character_map), dict, dict_size);
DEBUG_PRINT("Decompressed %d bytes!\n", decompressed_size);
msp_draw_complete();
}
/* Recording hooks */
static void rec_msp_draw_complete_hook()
{
if (rec_is_osd_recording() == false && rec_is_gls_recording() == true)
{
if (current_fc_variant[0] == '\0')
{
DEBUG_PRINT("msp_osd: gls started recording, but no fc variant yet!?\n");
return;
}
DEBUG_PRINT("msp_osd: gls started recording, start osd rec\n");
rec_config_t config = {
.char_width = current_display_info->char_width,
.char_height = current_display_info->char_height,
.font_width = current_display_info->font_width,
.font_height = current_display_info->font_height,
.x_offset = current_display_info->x_offset,
.y_offset = current_display_info->y_offset
};
strcpy(config.font_variant, current_fc_variant);
rec_start(&config);
}
else if (rec_is_osd_recording() == true && rec_is_gls_recording() == false)
{
DEBUG_PRINT("msp_osd: gls stopped recording, stop osd rec\n");
rec_stop();
}
}
static void rec_pb_play_loop()
{
struct timespec last;
clock_gettime(CLOCK_MONOTONIC, &last);
int64_t target_diff = NSEC_PER_SEC / 15; // 15 fps
int64_t next_diff = target_diff;
while (rec_pb_gls_is_playing())
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int64_t diff_ns = timespec_subtract_ns(&now, &last);
if (diff_ns >= next_diff)
{
rec_pb_do_next_frame((uint16_t *) msp_character_map);
render_screen();
next_diff = target_diff + (target_diff - diff_ns);
last = now;
}
}
rec_pb_stop();
}
static void rec_pb_timeout_hook()
{
uint8_t is_playing = rec_pb_gls_is_playing();
// Only try start once per playback.
if (is_playing == true && rec_pb_start_attempted == true) {
return;
} else if (is_playing == true) {
rec_pb_start_attempted = true;
} else {
rec_pb_start_attempted = false;
}
if (is_playing == true) {
DEBUG_PRINT("msp_osd: gls playing dvr, let's try too!\n");
if (rec_pb_start() != 0)
{
DEBUG_PRINT("msp_osd: failed to start playback!\n");
return;
}
rec_config_t *rec_config = rec_pb_get_config();
display_info_t *osd_display_info = malloc(sizeof(display_info_t));
memset(osd_display_info, 0, sizeof(display_info_t));
osd_display_info->char_width = rec_config->char_width;
osd_display_info->char_height = rec_config->char_height;
osd_display_info->font_width = rec_config->font_width;
osd_display_info->font_height = rec_config->font_height;
osd_display_info->x_offset = rec_config->x_offset;
osd_display_info->y_offset = rec_config->y_offset;
DEBUG_PRINT("msp_osd: playback config, char_width: %d\n", osd_display_info->char_width);
DEBUG_PRINT("msp_osd: playback config, char_height: %d\n", osd_display_info->char_height);
DEBUG_PRINT("msp_osd: playback config, font_width: %d\n", osd_display_info->font_width);
DEBUG_PRINT("msp_osd: playback config, font_height: %d\n", osd_display_info->font_height);
DEBUG_PRINT("msp_osd: playback config, x_offset: %d\n", osd_display_info->x_offset);
DEBUG_PRINT("msp_osd: playback config, y_offset: %d\n", osd_display_info->y_offset);
DEBUG_PRINT("msp_osd: gls playing dvr, loading font variant %s\n", rec_config->font_variant);
uint8_t is_hd = osd_display_info->font_width != sd_display_info.font_width;
load_font(osd_display_info, rec_config->font_variant);
// TODO: Sketchy swap here?
// Might end playback after swapping channel, maybe? So back on live channel but with
// DISPLAY_DISABLED which would be bad. :(
current_display_info = osd_display_info;
clear_overlay();
display_mode = DISPLAY_RUNNING;
uint8_t fakehd_was_enabled = fakehd_is_enabled();
if (fakehd_was_enabled == true) {
fakehd_disable();
}
rec_pb_play_loop();
current_display_info = &sd_display_info;
memset(msp_character_map, 0, sizeof(msp_character_map));
display_mode = DISPLAY_DISABLED;
if (fakehd_was_enabled == true) {
fakehd_enable();
}
close_font(osd_display_info);
free(osd_display_info);
}
}
/* Public OSD enable/disable methods */
void osd_disable() {
uint64_t disable = 1;
write(event_fd, &disable, sizeof(uint64_t));
}
void osd_enable() {
uint64_t enable = 2;
write(event_fd, &enable, sizeof(uint64_t));
}
/* Entry point and main loop */
void osd_directfb(duss_disp_instance_handle_t *disp, duss_hal_obj_handle_t ion_handle)
{
memset(current_fc_variant, 0, sizeof(current_fc_variant));
toast_load_config();
load_fakehd_config();
rec_load_config();
rec_pb_load_config();
check_is_au_overlay_enabled();
uint8_t is_v2_goggles = dji_goggles_are_v2();
DEBUG_PRINT("Detected DJI goggles %s\n", is_v2_goggles ? "V2" : "V1");
if (fakehd_is_enabled())
{
current_display_info = &full_display_info;
} else {
current_display_info = &sd_display_info;
}
display_driver = calloc(1, sizeof(displayport_vtable_t));
display_driver->draw_character = &msp_draw_character;
display_driver->clear_screen = &msp_clear_screen;
display_driver->draw_complete = &msp_draw_complete;
display_driver->set_options = &msp_set_options;
msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));
msp_state->cb = &msp_callback;
event_fd = eventfd(0, 0);
assert(event_fd > 0);
dji_shm_state_t radio_shm;
memset(&radio_shm, 0, sizeof(radio_shm));
int msp_socket_fd = bind_socket(MSP_PORT);
int data_socket_fd = bind_socket(DATA_PORT);
int compressed_socket_fd = bind_socket(COMPRESSED_DATA_PORT);
printf("*** MSP-OSD: MSP-OSD started up, listening on port %d\n", MSP_PORT);
struct pollfd poll_fds[4];
int recv_len = 0;
uint8_t byte = 0;
uint8_t buffer[8192];
struct sockaddr_storage src_addr;
socklen_t src_addr_len=sizeof(src_addr);
struct input_event ev;
struct timespec now, last_toast;
memset(&last_toast, 0, sizeof(last_toast));
memset(&last_render, 0, sizeof(last_render));
memset(&now, 0, sizeof(now));
load_fonts(FALLBACK_FONT);
open_dji_radio_shm(&radio_shm);
start_display(is_v2_goggles, disp, ion_handle);
int compression_dict_size = 0;
void *compression_dict = open_dict(DICTIONARY_VERSION, &compression_dict_size);
uint64_t event_number;
while (!quit)
{
poll_fds[0].fd = msp_socket_fd;
poll_fds[0].events = POLLIN;
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
poll_fds[2].fd = data_socket_fd;
poll_fds[2].events = POLLIN;
poll_fds[3].fd = compressed_socket_fd;
poll_fds[3].events = POLLIN;
// spin every 250ms if we didn't get a packet, then check and see if we need to do the toast/overlay logic
poll(poll_fds, 4, 250);
clock_gettime(CLOCK_MONOTONIC, &now);
if(poll_fds[0].revents) {
// Got MSP UDP packet
if (0 < (recv_len = recvfrom(msp_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))
{
DEBUG_PRINT("got MSP packet len %d\n", recv_len);
if(display_mode == DISPLAY_RUNNING) {
for (int i=0; i 1) {
DEBUG_PRINT("Display running transition\n");
display_mode = DISPLAY_RUNNING;
} else {
DEBUG_PRINT("Display disabled transition\n");
display_mode = DISPLAY_DISABLED;
}
}
}
if(poll_fds[3].revents) {
// Got compressed data
if (0 < (recv_len = recvfrom(compressed_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))
{
DEBUG_PRINT("got COMPRESSED data packet len %d\n", recv_len);
if(display_mode == DISPLAY_RUNNING) {
process_compressed_data(buffer, recv_len, compression_dict, compression_dict_size);
}
}
}
if(timespec_subtract_ns(&now, &last_toast) > (NSEC_PER_SEC / TOAST_HZ)) {
// lets toast run + update any notices
do_toast(display_print_string);
clock_gettime(CLOCK_MONOTONIC, &last_toast);
}
if(timespec_subtract_ns(&now, &last_render) > (NSEC_PER_SEC / FORCE_RENDER_HZ)) {
// More than 500ms have elapsed without a render, let's go ahead and manually render
render_screen();
}
if (rec_pb_is_enabled()) {
rec_pb_timeout_hook();
}
}
free(display_driver);
free(msp_state);
free(compression_dict);
close(msp_socket_fd);
close(data_socket_fd);
close(compressed_socket_fd);
close(event_fd);
return;
}
================================================
FILE: jni/osd_dji_udp.c
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "hw/dji_display.h"
#include "hw/dji_radio_shm.h"
#include "hw/dji_services.h"
#include "net/network.h"
#include "net/data_protocol.h"
#include "msp/msp.h"
#include "msp/msp_displayport.h"
#include "util/debug.h"
#include "util/fs_util.h"
#define MSP_PORT 7654
#define DATA_PORT 7655
#define WIDTH 1440
#define HEIGHT 810
#define BYTES_PER_PIXEL 4
#define PLANE_ID 6
#define NUM_CHARS 256
#define INPUT_FILENAME "/dev/input/event0"
#define SPLASH_STRING "OSD WAITING..."
#define SHUTDOWN_STRING "SHUTTING DOWN..."
#define FALLBACK_FONT_PATH "/blackbox/font"
#define ENTWARE_FONT_PATH "/opt/fonts/font"
#define SDCARD_FONT_PATH "/storage/sdcard0/font"
#define GOGGLES_VOLTAGE_PATH "/sys/devices/platform/soc/f0a00000.apb/f0a71000.omc/voltage5"
#define EV_CODE_BACK 0xc9
#define BACK_BUTTON_DELAY 4
#define SWAP32(data) \
( (((data) >> 24) & 0x000000FF) | (((data) >> 8) & 0x0000FF00) | \
(((data) << 8) & 0x00FF0000) | (((data) << 24) & 0xFF000000) )
#define MAX_DISPLAY_X 50
#define MAX_DISPLAY_Y 18
typedef struct display_info_s {
uint8_t char_width;
uint8_t char_height;
uint8_t font_width;
uint8_t font_height;
uint16_t x_offset;
uint16_t y_offset;
void *font_page_1;
void *font_page_2;
} display_info_t;
static volatile sig_atomic_t quit = 0;
static dji_display_state_t *dji_display;
static uint16_t msp_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static uint16_t overlay_character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y];
static displayport_vtable_t *display_driver;
static uint8_t which_fb = 0;
static display_info_t sd_display_info = {
.char_width = 31,
.char_height = 15,
.font_width = 36,
.font_height = 54,
.x_offset = 180,
.y_offset = 0,
.font_page_1 = NULL,
.font_page_2 = NULL,
};
static display_info_t hd_display_info = {
.char_width = 50,
.char_height = 18,
.font_width = 24,
.font_height = 36,
.x_offset = 120,
.y_offset = 80,
.font_page_1 = NULL,
.font_page_2 = NULL,
};
static display_info_t overlay_display_info = {
.char_width = 20,
.char_height = 10,
.font_width = 24,
.font_height = 36,
.x_offset = 960,
.y_offset = 450,
.font_page_1 = NULL,
.font_page_2 = NULL,
};
static display_info_t *current_display_info;
static void sig_handler(int _)
{
quit = 1;
}
static void draw_character(display_info_t *display_info, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y], uint32_t x, uint32_t y, uint16_t c)
{
if ((x > (display_info->char_width - 1)) || (y > (display_info->char_height - 1))) {
return;
}
character_map[x][y] = c;
}
static void msp_draw_character(uint32_t x, uint32_t y, uint16_t c) {
draw_character(current_display_info, msp_character_map, x, y, c);
}
static void draw_character_map(display_info_t *display_info, void *fb_addr, uint16_t character_map[MAX_DISPLAY_X][MAX_DISPLAY_Y]) {
if (display_info->font_page_1 == NULL) {
// give up if we don't have a font loaded
return;
}
void *font;
for(int y = 0; y < display_info->char_height; y++) {
for(int x = 0; x < display_info->char_width; x++) {
uint16_t c = character_map[x][y];
if (c != 0) {
font = display_info->font_page_1;
if (c > 255) {
c = c & 0xFF;
if (display_info->font_page_2 != NULL) {
// fall back to writing page 1 chars if we don't have a page 2 font
font = display_info->font_page_2;
}
}
uint32_t pixel_x = (x * display_info->font_width) + display_info->x_offset;
uint32_t pixel_y = (y * display_info->font_height) + display_info->y_offset;
uint32_t character_offset = (((display_info->font_height * display_info->font_width) * BYTES_PER_PIXEL) * c);
for(uint8_t gx = 0; gx < display_info->font_width; gx++) {
for(uint8_t gy = 0; gy < display_info->font_height; gy++) {
uint32_t font_offset = character_offset + (gy * display_info->font_width * BYTES_PER_PIXEL) + (gx * BYTES_PER_PIXEL);
uint32_t target_offset = ((((pixel_x + gx) * BYTES_PER_PIXEL) + ((pixel_y + gy) * WIDTH * BYTES_PER_PIXEL)));
*((uint8_t *)fb_addr + target_offset) = *(uint8_t *)((uint8_t *)font + font_offset + 2);
*((uint8_t *)fb_addr + target_offset + 1) = *(uint8_t *)((uint8_t *)font + font_offset + 1);
*((uint8_t *)fb_addr + target_offset + 2) = *(uint8_t *)((uint8_t *)font + font_offset);
*((uint8_t *)fb_addr + target_offset + 3) = ~*(uint8_t *)((uint8_t *)font + font_offset + 3);
}
}
DEBUG_PRINT("%c", c > 31 ? c : 20);
}
DEBUG_PRINT(" ");
}
DEBUG_PRINT("\n");
}
}
static void draw_screen() {
void *fb_addr = dji_display_get_fb_address(dji_display, which_fb);
// DJI has a backwards alpha channel - FF is transparent, 00 is opaque.
memset(fb_addr, 0x000000FF, WIDTH * HEIGHT * BYTES_PER_PIXEL);
draw_character_map(current_display_info, fb_addr, msp_character_map);
draw_character_map(&overlay_display_info, fb_addr, overlay_character_map);
}
static void msp_clear_screen()
{
memset(msp_character_map, 0, sizeof(msp_character_map));
}
static void msp_draw_complete() {
draw_screen();
dji_display_push_frame(dji_display, which_fb);
which_fb = !which_fb;
DEBUG_PRINT("drew a frame\n");
}
static void msp_callback(msp_msg_t *msp_message)
{
displayport_process_message(display_driver, msp_message);
}
static void get_font_path_with_prefix(char *font_path_dest, const char *font_path, uint8_t len, uint8_t is_hd, uint8_t page) {
char name_buf[len];
if (is_hd) {
snprintf(name_buf, len, "%s_hd", font_path);
} else {
snprintf(name_buf, len, "%s", font_path);
}
if (page > 0) {
snprintf(font_path_dest, len, "%s_%d.bin", name_buf, page + 1);
} else {
snprintf(font_path_dest, len, "%s.bin", name_buf);
}
}
static int open_font(const char *filename, void** font, uint8_t page, uint8_t is_hd) {
char file_path[255];
get_font_path_with_prefix(file_path, filename, 255, is_hd, page);
printf("Opening font: %s\n", file_path);
struct stat st;
memset(&st, 0, sizeof(st));
stat(file_path, &st);
size_t filesize = st.st_size;
display_info_t display_info = is_hd ? hd_display_info : sd_display_info;
size_t desired_filesize = display_info.font_height * display_info.font_width * NUM_CHARS * BYTES_PER_PIXEL;
if(filesize != desired_filesize) {
if (filesize != 0) {
printf("Font was wrong size: %s %d != %d\n", file_path, filesize, desired_filesize);
}
return -1;
}
int fd = open(file_path, O_RDONLY, 0);
if (!fd) {
printf("Could not open file %s\n", file_path);
return -1;
}
void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
// there is no need to keep an FD open after mmap
close(fd);
if (mmappedData != MAP_FAILED) {
*font = mmappedData;
} else {
printf("Could not map font %s\n", file_path);
*font = 0;
}
return 0;
}
static void load_font() {
if (open_font(SDCARD_FONT_PATH, &sd_display_info.font_page_1, 0, 0) < 0) {
if (open_font(ENTWARE_FONT_PATH, &sd_display_info.font_page_1, 0, 0) < 0) {
open_font(FALLBACK_FONT_PATH, &sd_display_info.font_page_1, 0, 0);
}
}
if (open_font(SDCARD_FONT_PATH, &sd_display_info.font_page_2, 1, 0) < 0) {
if (open_font(ENTWARE_FONT_PATH, &sd_display_info.font_page_2, 1, 0) < 0) {
open_font(FALLBACK_FONT_PATH, &sd_display_info.font_page_2, 1, 0);
}
}
if (open_font(SDCARD_FONT_PATH, &hd_display_info.font_page_1, 0, 1) < 0) {
if (open_font(ENTWARE_FONT_PATH, &hd_display_info.font_page_1, 0, 1) < 0) {
open_font(FALLBACK_FONT_PATH, &hd_display_info.font_page_1, 0, 1);
}
}
if (open_font(SDCARD_FONT_PATH, &hd_display_info.font_page_2, 1, 1) < 0) {
if (open_font(ENTWARE_FONT_PATH, &hd_display_info.font_page_2, 1, 1) < 0) {
open_font(FALLBACK_FONT_PATH, &hd_display_info.font_page_2, 1, 1);
}
}
if (open_font(SDCARD_FONT_PATH, &overlay_display_info.font_page_1, 0, 1) < 0) {
if (open_font(ENTWARE_FONT_PATH, &overlay_display_info.font_page_1, 0, 1) < 0) {
open_font(FALLBACK_FONT_PATH, &overlay_display_info.font_page_1, 0, 1);
}
}
if (open_font(SDCARD_FONT_PATH, &overlay_display_info.font_page_2, 1, 1) < 0) {
if (open_font(ENTWARE_FONT_PATH, &overlay_display_info.font_page_2, 1, 1) < 0) {
open_font(FALLBACK_FONT_PATH, &overlay_display_info.font_page_2, 1, 1);
}
}
}
static void close_fonts(display_info_t *display_info) {
if (display_info->font_page_1 != NULL)
{
munmap(display_info->font_page_1, display_info->font_height * display_info->font_width * NUM_CHARS * 4);
}
if (display_info->font_page_2 != NULL)
{
munmap(display_info->font_page_2, display_info->font_height * display_info->font_width * NUM_CHARS * 4);
}
}
static void msp_set_options(uint8_t font_num, uint8_t is_hd) {
msp_clear_screen();
if(is_hd) {
current_display_info = &hd_display_info;
} else {
current_display_info = &sd_display_info;
}
}
static void display_print_string(uint8_t init_x, uint8_t y, const char *string, uint8_t len) {
for(uint8_t x = 0; x < len; x++) {
draw_character(&overlay_display_info, overlay_character_map, x + init_x, y, string[x]);
}
}
static void start_display(uint8_t is_v2_goggles) {
memset(msp_character_map, 0, sizeof(msp_character_map));
memset(overlay_character_map, 0, sizeof(overlay_character_map));
dji_display = dji_display_state_alloc(is_v2_goggles);
dji_display_open_framebuffer(dji_display, PLANE_ID);
display_print_string(0, overlay_display_info.char_height -1, SPLASH_STRING, sizeof(SPLASH_STRING));
msp_draw_complete();
}
static void stop_display() {
display_print_string(0, overlay_display_info.char_height -1, SHUTDOWN_STRING, sizeof(SHUTDOWN_STRING));
dji_display_close_framebuffer(dji_display);
dji_display_state_free(dji_display);
}
static void process_data_packet(uint8_t *buf, int len, dji_shm_state_t *radio_shm) {
packet_data_t *packet = (packet_data_t *)buf;
DEBUG_PRINT("got data %f mbit %d C %f V\n", packet->tx_bitrate / 1000.0f, packet->tx_temperature, packet->tx_voltage / 64.0f);
memset(overlay_character_map, 0, sizeof(overlay_character_map));
char str[8];
snprintf(str, 8, "%2.1fMB ", packet->tx_bitrate / 1000.0f);
display_print_string(overlay_display_info.char_width - 6, overlay_display_info.char_height - 5, str, 6);
uint16_t latency = dji_radio_latency_ms(radio_shm);
snprintf(str, 8, "%d MS", latency);
display_print_string(overlay_display_info.char_width - 6, overlay_display_info.char_height - 4, str, 6);
snprintf(str, 8, "%d C", packet->tx_temperature);
display_print_string(overlay_display_info.char_width - 5, overlay_display_info.char_height - 3, str, 5);
snprintf(str, 8, "A %2.1fV", packet->tx_voltage / 64.0f);
display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 2, str, 7);
uint16_t goggle_voltage = get_int_from_fs(GOGGLES_VOLTAGE_PATH);
snprintf(str, 8, "G %2.1fV", (goggle_voltage / 45.0f) - 0.65f);
display_print_string(overlay_display_info.char_width - 7, overlay_display_info.char_height - 1, str, 7);
}
int main(int argc, char *argv[])
{
signal(SIGINT, sig_handler);
current_display_info = &sd_display_info;
uint8_t is_v2_goggles = dji_goggles_are_v2();
printf("Detected DJI goggles %s\n", is_v2_goggles ? "V2" : "V1");
display_driver = calloc(1, sizeof(displayport_vtable_t));
display_driver->draw_character = &msp_draw_character;
display_driver->clear_screen = &msp_clear_screen;
display_driver->draw_complete = &msp_draw_complete;
display_driver->set_options = &msp_set_options;
msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));
msp_state->cb = &msp_callback;
int event_fd = open(INPUT_FILENAME, O_RDONLY);
assert(event_fd > 0);
dji_shm_state_t radio_shm;
memset(&radio_shm, 0, sizeof(radio_shm));
int msp_socket_fd = bind_socket(MSP_PORT);
int data_socket_fd = bind_socket(DATA_PORT);
printf("started up, listening on port %d\n", MSP_PORT);
struct pollfd poll_fds[3];
int recv_len = 0;
uint8_t byte = 0;
uint8_t buffer[4096];
struct sockaddr_storage src_addr;
socklen_t src_addr_len=sizeof(src_addr);
struct input_event ev;
struct timespec button_start, display_start, now;
memset(&display_start, 0, sizeof(display_start));
memset(&button_start, 0, sizeof(button_start));
enum display_mode_s {
DISPLAY_DISABLED = 0,
DISPLAY_RUNNING = 1,
DISPLAY_WAITING = 2
} display_mode = DISPLAY_DISABLED;
while (!quit)
{
clock_gettime(CLOCK_MONOTONIC, &now);
if(display_mode == DISPLAY_WAITING && display_start.tv_sec > 0 && ((now.tv_sec - display_start.tv_sec) > 1)) {
// Wait 1 second between stopping Glasses service and trying to start OSD.
memset(&display_start, 0, sizeof(display_start));
load_font();
open_dji_radio_shm(&radio_shm);
start_display(is_v2_goggles);
display_mode = DISPLAY_RUNNING;
}
if(button_start.tv_sec > 0 && ((now.tv_sec - button_start.tv_sec) > BACK_BUTTON_DELAY)) {
// We held the back button down for 5 seconds.
memset(&button_start, 0, sizeof(button_start));
if (display_mode == DISPLAY_DISABLED) {
printf("Switching Disabled -> Enabled!\n");
dji_stop_goggles(is_v2_goggles);
clock_gettime(CLOCK_MONOTONIC, &display_start);
display_mode = DISPLAY_WAITING;
} else {
printf("Switching Enabled/Waiting -> Disabled!\n");
if(display_mode == DISPLAY_RUNNING) {
stop_display();
close_dji_radio_shm(&radio_shm);
}
close_fonts(&sd_display_info);
close_fonts(&hd_display_info);
close_fonts(&overlay_display_info);
display_mode = DISPLAY_DISABLED;
dji_start_goggles(is_v2_goggles);
}
}
poll_fds[0].fd = msp_socket_fd;
poll_fds[0].events = POLLIN;
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
poll_fds[2].fd = data_socket_fd;
poll_fds[2].events = POLLIN;
poll(poll_fds, 3, 100);
if(poll_fds[1].revents) {
read(event_fd, &ev, sizeof(struct input_event));
if(ev.code == EV_CODE_BACK) {
if(ev.value == 1) {
clock_gettime(CLOCK_MONOTONIC, &button_start);
} else {
memset(&button_start, 0, sizeof(button_start));
}
}
DEBUG_PRINT("input type: %i, code: %i, value: %i\n", ev.type, ev.code, ev.value);
}
if(poll_fds[0].revents) {
// Got MSP UDP packet
if (0 < (recv_len = recvfrom(msp_socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))
{
DEBUG_PRINT("got MSP packet len %d\n", recv_len);
if(display_mode == DISPLAY_RUNNING) {
for (int i=0; i
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "msp/msp.h"
#include "msp/msp_displayport.h"
#include "net/serial.h"
#include "net/network.h"
#include "util/debug.h"
#define X_OFFSET 120
#define PORT 7654
#define WIDTH 1440
#define HEIGHT 810
typedef struct display_info_s {
uint8_t char_width;
uint8_t char_height;
uint8_t font_width;
uint8_t font_height;
uint16_t num_chars;
} display_info_t;
#define SD_DISPLAY_INFO {.char_width = 31, .char_height = 15, .font_width = 36, .font_height = 54, .num_chars = 256}
static const display_info_t sd_display_info = SD_DISPLAY_INFO;
static const display_info_t hd_display_info = {
.char_width = 50,
.char_height = 18,
.font_width = 24,
.font_height = 36,
.num_chars = 512,
};
#define MAX_OSD_WIDTH 50
#define MAX_OSD_HEIGHT 18
static display_info_t current_display_info = SD_DISPLAY_INFO;
static volatile sig_atomic_t quit = 0;
sfTexture *font_1;
sfTexture *font_2;
sfSprite *font_sprite_1;
sfSprite *font_sprite_2;
sfRenderWindow *window;
uint16_t character_map[MAX_OSD_WIDTH][MAX_OSD_HEIGHT];
displayport_vtable_t *display_driver;
static void sig_handler(int _)
{
quit = 1;
}
static void draw_character(uint32_t x, uint32_t y, uint16_t c)
{
if (x > current_display_info.char_width - 1 || y > current_display_info.char_height - 1)
{
return;
}
character_map[x][y] = c;
}
static void draw_screen()
{
sfRenderWindow_clear(window, sfColor_fromRGB(55, 55, 55));
for (int y = 0; y < current_display_info.char_height; y++)
{
for (int x = 0; x < current_display_info.char_width; x++)
{
uint16_t c = character_map[x][y];
if (c != 0)
{
uint8_t page = 0;
if (c > 255) {
page = 1;
c = c & 0xFF;
}
DEBUG_PRINT("%02X", c);
sfIntRect r = {0, current_display_info.font_height * c, current_display_info.font_width, current_display_info.font_height};
sfVector2f dest = {(x * current_display_info.font_width) + X_OFFSET, y * current_display_info.font_height};
sfSprite *font_sprite = page ? font_sprite_2 : font_sprite_1;
sfSprite_setTextureRect(font_sprite, r);
sfSprite_setPosition(font_sprite, dest);
sfRenderWindow_drawSprite(window, font_sprite, NULL);
}
DEBUG_PRINT(" ");
}
DEBUG_PRINT("\n");
}
}
static void clear_screen()
{
DEBUG_PRINT("clear\n");
memset(character_map, 0, sizeof(character_map));
}
static void draw_complete()
{
draw_screen();
sfRenderWindow_display(window);
DEBUG_PRINT("draw complete!\n");
}
static void msp_callback(msp_msg_t *msp_message)
{
displayport_process_message(display_driver, msp_message);
}
static void set_options(uint8_t font, uint8_t is_hd) {
if(is_hd) {
current_display_info = hd_display_info;
} else {
current_display_info = sd_display_info;
}
}
int main(int argc, char *argv[])
{
struct pollfd poll_fds[1];
signal(SIGINT, sig_handler);
memset(character_map, 0, sizeof(character_map));
sfVideoMode videoMode = {1440, 810, 32};
window = sfRenderWindow_create(videoMode, "MSP OSD", 0, NULL);
sfRenderWindow_display(window);
char *font_name;
if (argc > 1) {
font_name = argv[1];
} else {
font_name = "bold.png";
}
char font_load_name[255];
snprintf(font_load_name, 255, "%s.png", font_name);
font_1 = sfTexture_createFromFile(font_name, NULL);
font_sprite_1 = sfSprite_create();
sfSprite_setTexture(font_sprite_1, font_1, 0);
snprintf(font_2_name, 255, "%s_2.png", font_name);
font_2 = sfTexture_createFromFile(font_2_name, NULL);
font_sprite_2 = sfSprite_create();
sfSprite_setTexture(font_sprite_2, font_2, 0);
display_driver = calloc(1, sizeof(displayport_vtable_t));
display_driver->draw_character = &draw_character;
display_driver->clear_screen = &clear_screen;
display_driver->draw_complete = &draw_complete;
display_driver->set_options = &set_options;
msp_state_t *msp_state = calloc(1, sizeof(msp_state_t));
msp_state->cb = &msp_callback;
int socket_fd = bind_socket(PORT);
int recv_len = 0;
uint8_t buffer[4096];
struct timespec fps_start, now;
uint32_t message_counter = 0;
clock_gettime(CLOCK_MONOTONIC, &fps_start);
printf("started up, listening on port %d\n", PORT);
while (!quit)
{
clock_gettime(CLOCK_MONOTONIC, &now);
if(now.tv_sec > fps_start.tv_sec) {
clock_gettime(CLOCK_MONOTONIC, &fps_start);
printf("Got %d messages in the last second\n", message_counter);
message_counter = 0;
}
sfEvent event;
sfRenderWindow_pollEvent(window, &event);
// Close window: exit
if (event.type == sfEvtMouseButtonReleased)
{
sfRenderWindow_close(window);
quit = 1;
}
poll_fds[0].fd = socket_fd;
poll_fds[0].events = POLLIN;
if(poll(poll_fds, 1, 50) > 0) // poll every 50ms so we also go back through the SFML loop
{
struct sockaddr_storage src_addr;
socklen_t src_addr_len=sizeof(src_addr);
if (0 < (recv_len = recvfrom(socket_fd,&buffer,sizeof(buffer),0,(struct sockaddr*)&src_addr,&src_addr_len)))
{
message_counter++;
for (int i=0; i
#include
#include
#include "../json/osd_config.h"
#include "rec.h"
#include "rec_shim.h"
#include "rec_util.h"
#define REC_CONFIG_ENABLED_KEY "rec_enabled"
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) fprintf(stderr, "msp_osd.rec: " fmt "\n", ##args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
static bool rec_is_ready();
static uint32_t rec_get_frame_idx();
static bool rec_enabled = false;
gs_lv_transcode_t *rec_lv_transcode = NULL;
static FILE *rec_fd = NULL;
static bool rec_recording = false;
void rec_start(rec_config_t *config)
{
if (!rec_is_ready())
return;
if (rec_fd != NULL)
{
fflush(rec_fd);
fclose(rec_fd);
}
char rec_file_name[256];
rec_util_osd_path_from_video_path(
rec_lv_transcode->file_name,
rec_file_name,
sizeof(rec_file_name));
DEBUG_PRINT("rec_file_name: %s", rec_file_name);
DEBUG_PRINT("Config:\n");
DEBUG_PRINT(" Char Width: %u\n", config->char_width);
DEBUG_PRINT(" Char Height: %u\n", config->char_height);
DEBUG_PRINT(" Font Width: %u\n", config->font_width);
DEBUG_PRINT(" Font Height: %u\n", config->font_height);
DEBUG_PRINT(" X Offset: %u\n", config->x_offset);
DEBUG_PRINT(" Y Offset: %u\n", config->y_offset);
DEBUG_PRINT(" Font Variant: %.5s\n", config->font_variant);
rec_fd = fopen(rec_file_name, "wb");
if (rec_fd == NULL)
{
DEBUG_PRINT("Failed to open file: %s", rec_file_name);
return;
}
rec_file_header_t file_header = {
.magic = REC_MAGIC,
.version = REC_VERSION,
};
memcpy(&file_header.config, config, sizeof(rec_config_t));
fwrite(&file_header, sizeof(rec_file_header_t), 1, rec_fd);
rec_recording = true;
}
void rec_stop()
{
if (rec_fd != NULL)
{
fflush(rec_fd);
fclose(rec_fd);
rec_fd = NULL;
}
rec_recording = false;
}
void rec_write_frame(uint16_t *frame_data, size_t frame_size)
{
if (rec_fd == NULL)
return;
rec_frame_header_t frame_header = {
.frame_idx = rec_get_frame_idx(),
.size = frame_size,
};
fwrite(&frame_header, sizeof(frame_header), 1, rec_fd);
fwrite(frame_data, sizeof(uint16_t), frame_size, rec_fd);
}
void rec_load_config()
{
rec_enabled = get_boolean_config_value(REC_CONFIG_ENABLED_KEY);
DEBUG_PRINT("rec_enabled: %d", rec_enabled);
}
bool rec_is_enabled()
{
return rec_enabled;
}
bool rec_is_osd_recording()
{
return rec_recording;
}
bool rec_is_gls_recording()
{
if (rec_is_ready() == false)
return false;
return rec_lv_transcode->cur_state == RECORD_STATE_RECORDING;
}
static bool rec_is_ready()
{
return rec_lv_transcode != NULL;
}
static uint32_t rec_get_frame_idx()
{
return rec_lv_transcode->last_frame_idx;
}
================================================
FILE: jni/rec/rec.h
================================================
#pragma once
#include
#include
#define REC_MAGIC "MSPOSD"
#define REC_VERSION 2
typedef struct rec_config_t
{
uint8_t char_width;
uint8_t char_height;
uint8_t font_width;
uint8_t font_height;
uint16_t x_offset;
uint16_t y_offset;
char font_variant[5];
} __attribute__((packed)) rec_config_t;
typedef struct rec_file_header_t
{
char magic[7];
uint16_t version;
rec_config_t config;
} __attribute__((packed)) rec_file_header_t;
typedef struct rec_config_v1_t
{
uint8_t char_width;
uint8_t char_height;
uint8_t font_width;
uint8_t font_height;
uint16_t x_offset;
uint16_t y_offset;
uint8_t font_variant;
} __attribute__((packed)) rec_config_v1_t;
typedef struct rec_file_header_v1_t
{
char magic[7];
uint16_t version;
rec_config_v1_t config;
} __attribute__((packed)) rec_file_header_v1_t;
typedef struct rec_frame_header_t
{
uint32_t frame_idx;
uint32_t size;
} __attribute__((packed)) rec_frame_header_t;
void rec_start();
void rec_stop();
void rec_load_config();
void rec_write_frame(uint16_t *frame_data, size_t frame_size);
bool rec_is_enabled();
bool rec_is_osd_recording();
bool rec_is_gls_recording();
================================================
FILE: jni/rec/rec_pb.c
================================================
#include
#include
#include
#include
#include "../json/osd_config.h"
#include "rec.h"
#include "rec_shim.h"
#include "rec_util.h"
#include "rec_pb.h"
#include "../font/font.h"
#define REC_PB_CONFIG_ENABLED_KEY "rec_pb_enabled"
#define MAX_X 60
#define MAX_Y 22
#define MAX_T (MAX_X * MAX_Y)
#define FRAME_SIZE (sizeof(rec_frame_header_t) + (sizeof(uint16_t) * MAX_T))
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) fprintf(stderr, "msp_osd.rec_pb: " fmt "\n", ##args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
cp_vdec_t *rec_pb_cp_vdec = NULL;
vdec_local_player_t *rec_pb_vdec_local_player = NULL;
uint8_t rec_pb_start_attempted = false;
static bool rec_pb_enabled = false;
static FILE *osd_fd = NULL;
static rec_config_t osd_config = {0};
static uint32_t header_size = 0;
static int64_t frame_counter = 0;
static uint32_t *frame_idxs;
static uint32_t frame_idx_len = 0;
static uint32_t current_frame_idx = 0;
void rec_pb_load_config()
{
rec_pb_enabled = get_boolean_config_value(REC_PB_CONFIG_ENABLED_KEY);
DEBUG_PRINT("rec_pb_enabled: %d", rec_pb_enabled);
}
bool rec_pb_is_enabled()
{
return rec_pb_enabled;
}
int rec_pb_start()
{
if (rec_pb_is_ready() == false)
{
return 1;
}
if (osd_fd != NULL)
{
return 1;
}
// TODO: Mutex needed here? Can cause a crash if placback stops as we're trying to read info.
DEBUG_PRINT("playback on: %s", rec_pb_vdec_local_player->fmt_ctx->filename);
char osd_path[256];
rec_util_osd_path_from_video_path(rec_pb_vdec_local_player->fmt_ctx->filename, osd_path, sizeof(osd_path));
DEBUG_PRINT("osd path: %s", osd_path);
osd_fd = fopen(osd_path, "rb");
if (osd_fd == NULL)
{
DEBUG_PRINT("osd file not found");
return 1;
}
DEBUG_PRINT("osd file found");
rec_file_header_t file_header;
fread(&file_header, sizeof(rec_file_header_t), 1, osd_fd);
if (strncmp(file_header.magic, REC_MAGIC, sizeof(REC_MAGIC)) != 0)
{
DEBUG_PRINT("invalid osd file");
fclose(osd_fd);
osd_fd = NULL;
return 1;
}
if (file_header.version == REC_VERSION)
{
DEBUG_PRINT("header ok!");
memcpy(&osd_config, &file_header.config, sizeof(rec_config_t));
header_size = sizeof(rec_file_header_t);
}
else if (file_header.version == 1)
{
DEBUG_PRINT("header is v1");
rec_file_header_v1_t file_header_v1;
fseek(osd_fd, 0, SEEK_SET);
fread(&file_header_v1, sizeof(rec_file_header_v1_t), 1, osd_fd);
if (strncmp(file_header_v1.magic, REC_MAGIC, sizeof(REC_MAGIC)) != 0)
{
DEBUG_PRINT("invalid osd file");
fclose(osd_fd);
osd_fd = NULL;
return 1;
}
switch (file_header_v1.config.font_variant)
{
case FONT_VARIANT_BETAFLIGHT:
strcpy(file_header.config.font_variant, "BTFL");
break;
case FONT_VARIANT_INAV:
strcpy(file_header.config.font_variant, "INAV");
break;
case FONT_VARIANT_ARDUPILOT:
strcpy(file_header.config.font_variant, "ARDU");
break;
case FONT_VARIANT_KISS_ULTRA:
strcpy(file_header.config.font_variant, "ULTR");
break;
case FONT_VARIANT_QUICKSILVER:
strcpy(file_header.config.font_variant, "QUIC");
break;
default:
file_header.config.font_variant[0] = '\0'; // Empty string
}
memcpy(&osd_config, &file_header.config, sizeof(rec_config_t));
header_size = sizeof(rec_file_header_v1_t);
}
else
{
DEBUG_PRINT("invalid osd file version! expected: %d, got: %d", REC_VERSION, file_header.version);
fclose(osd_fd);
osd_fd = NULL;
return 1;
}
DEBUG_PRINT("loading frame indexes");
fseek(osd_fd, 0, SEEK_END);
uint32_t file_size = ftell(osd_fd);
fseek(osd_fd, header_size, SEEK_SET);
DEBUG_PRINT("file size: %d", file_size);
frame_idx_len = file_size / FRAME_SIZE;
DEBUG_PRINT("frame_idx_len: %d", frame_idx_len);
frame_idxs = malloc(sizeof(uint32_t) * frame_idx_len);
for (uint32_t i = 0; i < frame_idx_len; i++)
{
rec_frame_header_t frame_header;
fread(&frame_header, sizeof(rec_frame_header_t), 1, osd_fd);
frame_idxs[i] = frame_header.frame_idx;
DEBUG_PRINT("frame_idx: %d = %d", i, frame_idxs[i]);
fseek(osd_fd, sizeof(uint16_t) * MAX_T, SEEK_CUR);
}
fseek(osd_fd, header_size, SEEK_SET);
current_frame_idx = 0;
frame_counter = rec_pb_cp_vdec->frames_sent;
return 0;
}
void rec_pb_stop()
{
if (osd_fd != NULL)
{
fclose(osd_fd);
osd_fd = NULL;
}
rec_pb_cp_vdec = NULL;
rec_pb_vdec_local_player = NULL;
free(frame_idxs);
frame_idxs = NULL;
DEBUG_PRINT("playback stopped");
}
rec_config_t *rec_pb_get_config()
{
if (rec_pb_is_ready() == false)
{
return NULL;
}
return &osd_config;
}
int rec_pb_do_next_frame(uint16_t *map_out)
{
// -45 is absolutely a magic number based on testing, seems play_tm_ms lags by about that much.
uint64_t frame_counter = ((rec_pb_cp_vdec->play_tm_ms) * 60 / 1000) - 45;
uint32_t closest_frame_idx = 0;
for (uint32_t i = 0; i < frame_idx_len; i++)
{
if (frame_idxs[i] > frame_counter)
{
break;
}
closest_frame_idx = i;
}
if (closest_frame_idx == current_frame_idx && current_frame_idx != 0)
{
return 0;
}
fseek(
osd_fd,
header_size + (closest_frame_idx * FRAME_SIZE) + sizeof(rec_frame_header_t),
SEEK_SET);
fread(map_out, sizeof(uint16_t), MAX_T, osd_fd);
current_frame_idx = closest_frame_idx;
return 0;
}
bool rec_pb_gls_is_playing()
{
if (rec_pb_is_ready() == false)
{
return false;
}
// state == 5 is stopped (i.e., when gs_player_stop is called)
return rec_pb_vdec_local_player->b_running && !rec_pb_vdec_local_player->b_v_eos && rec_pb_vdec_local_player->state != 5;
}
bool rec_pb_is_ready()
{
return rec_pb_cp_vdec != NULL && rec_pb_vdec_local_player != NULL;
}
================================================
FILE: jni/rec/rec_pb.h
================================================
#pragma once
#include
#include
#include
extern uint8_t rec_pb_start_attempted;
void rec_pb_load_config();
bool rec_pb_is_enabled();
int rec_pb_start();
void rec_pb_stop();
int rec_pb_do_next_frame(uint16_t *map_out);
rec_config_t *rec_pb_get_config();
bool rec_pb_is_ready();
bool rec_pb_gls_is_playing();
================================================
FILE: jni/rec/rec_shim.c
================================================
#include
#include
#include
#include "../hw/dji_services.h"
#include "rec_shim.h"
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) fprintf(stderr, "msp_osd.rec_shim: " fmt "\n", ##args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
duss_osal_priority_t (*duss_osal_task_create_orig)(
duss_osal_task_attrib_t *task_attrib,
duss_osal_task_handle_t **task_handle) = NULL;
extern cp_vdec_t *rec_pb_cp_vdec;
extern vdec_local_player_t *rec_pb_vdec_local_player;
extern gs_lv_transcode_t *rec_lv_transcode;
duss_osal_priority_t duss_osal_task_create(
duss_osal_task_attrib_t *task_attrib,
duss_osal_task_handle_t **task_handle)
{
if (duss_osal_task_create_orig == NULL)
{
duss_osal_task_create_orig = dlsym(RTLD_NEXT, "duss_osal_task_create");
}
if (strcmp(task_attrib->name, "record_thread") == 0)
{
rec_lv_transcode = task_attrib->param;
DEBUG_PRINT("got lv_transcode_t from record_thread: %p", rec_lv_transcode);
}
else if (strcmp(task_attrib->name, "player_thread") == 0)
{
gs_info_t *ctx = task_attrib->param;
if (dji_goggles_are_v2())
{
rec_pb_vdec_local_player = *(vdec_local_player_t **)((void *)ctx + 0x8b8);
}
else
{
rec_pb_vdec_local_player = *(vdec_local_player_t **)((void *)ctx + 0x8b4);
}
DEBUG_PRINT("got vdec_local_player_t from player_thread: %p", rec_pb_vdec_local_player);
}
else if (strcmp(task_attrib->name, "vdec_thread") == 0)
{
rec_pb_cp_vdec = task_attrib->param;
DEBUG_PRINT("got cp_vdec_t from vdec_thread: %p", rec_pb_cp_vdec);
}
return duss_osal_task_create_orig(task_attrib, task_handle);
}
================================================
FILE: jni/rec/rec_shim.h
================================================
#pragma once
#include
typedef unsigned char undefined;
typedef unsigned char byte;
typedef unsigned int dword;
typedef long long longlong;
typedef unsigned long long qword;
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned long long ulonglong;
typedef unsigned char undefined1;
typedef unsigned short undefined2;
typedef unsigned int undefined4;
typedef unsigned long long undefined8;
typedef unsigned short ushort;
typedef unsigned short word;
typedef struct __gs_info __gs_info;
typedef struct __gs_info gs_info_t;
typedef struct duss_event_client duss_event_client;
typedef struct duss_event_client *duss_event_client_handle_t;
typedef struct duss_hal_obj duss_hal_obj;
typedef struct duss_hal_obj *duss_hal_obj_handle_t;
typedef int __int32_t;
typedef __int32_t int32_t;
typedef struct gs_debug_ctrl gs_debug_ctrl;
typedef struct gs_debug_ctrl gs_debug_ctrl_t;
typedef struct duss_osal_task_handle_t duss_osal_task_handle_t;
typedef struct __gs_local_sd_info __gs_local_sd_info;
typedef struct __gs_local_sd_info gs_local_sd_info_t;
typedef struct duss_osal_timer_handle_t duss_osal_timer_handle_t;
typedef struct _DUSS_MSG_RC_BAT_INFO_t _DUSS_MSG_RC_BAT_INFO_t;
typedef struct _DUSS_MSG_RC_BAT_INFO_t DUSS_MSG_RC_BAT_INFO_t;
typedef struct gs_battery_info gs_battery_info;
typedef struct gs_battery_info gs_battery_info_t;
typedef uchar __uint8_t;
typedef __uint8_t uint8_t;
typedef struct gs_modem_ctrl gs_modem_ctrl;
typedef struct gs_modem_ctrl gs_modem_ctrl_t;
typedef struct _DUSS_MSG_RC_MS_LINK_STATUS _DUSS_MSG_RC_MS_LINK_STATUS;
typedef struct _DUSS_MSG_RC_MS_LINK_STATUS DUSS_MSG_RC_MS_LINK_STATUS_t;
typedef struct __gs_use_times_info __gs_use_times_info;
typedef struct __gs_use_times_info gs_use_times_info_t;
typedef struct gs_rc_ctrl gs_rc_ctrl;
typedef struct gs_rc_ctrl gs_rc_ctrl_t;
typedef struct keys_pack_to_racing_glass_t keys_pack_to_racing_glass_t;
typedef struct duss_osal_timer_attrib_t duss_osal_timer_attrib_t;
typedef struct pack_for_factory_test_t pack_for_factory_test_t;
typedef struct gs_buzzer_info gs_buzzer_info;
typedef struct gs_buzzer_info gs_buzzer_info_t;
typedef struct gs_fan_info gs_fan_info;
typedef struct gs_fan_info gs_fan_info_t;
typedef struct __gs_common_cmd_ctrl __gs_common_cmd_ctrl;
typedef struct __gs_common_cmd_ctrl gs_common_cmd_ctrl_t;
typedef struct gs_camera_cmd_ctrl_t gs_camera_cmd_ctrl_t;
typedef struct _DUSS_MSG_CAMERA_STATUS_PUSH_t _DUSS_MSG_CAMERA_STATUS_PUSH_t;
typedef struct _DUSS_MSG_CAMERA_STATUS_PUSH_t DUSS_MSG_CAMERA_STATUS_PUSH_t;
typedef struct gs_video_channel_manager gs_video_channel_manager;
typedef struct gs_video_channel_manager gs_video_channel_manager_t;
typedef struct gs_wl_ctrl gs_wl_ctrl;
typedef struct gs_wl_ctrl gs_wl_ctrl_t;
typedef struct gs_av_in_ctrl gs_av_in_ctrl;
typedef struct gs_av_in_ctrl gs_av_in_ctrl_t;
typedef struct vdec_local_player vdec_local_player;
typedef struct vdec_local_player vdec_local_player_t;
typedef struct metadata_retriever metadata_retriever;
typedef struct metadata_retriever metadata_retriever_t;
typedef struct gs_lv_src gs_lv_src;
typedef struct gs_lv_src gs_lv_src_t;
typedef struct gs_lv_rec_ctrl gs_lv_rec_ctrl;
typedef struct gs_lv_rec_ctrl gs_lv_rec_ctrl_t;
typedef struct gs_usb_gadget_vt gs_usb_gadget_vt;
typedef struct gs_usb_gadget_vt gs_usb_gadget_vt_t;
typedef struct gs_lv_transcode gs_lv_transcode;
typedef struct gs_lv_transcode gs_lv_transcode_t;
typedef struct gs_aout gs_aout;
typedef struct gs_aout gs_aout_t;
typedef struct gs_audio_wl gs_audio_wl;
typedef struct gs_audio_wl gs_audio_wl_t;
typedef struct gs_media_cmd_chnl gs_media_cmd_chnl;
typedef struct gs_media_cmd_chnl gs_media_cmd_chnl_t;
typedef struct gs_bl gs_bl;
typedef struct gs_bl gs_bl_t;
typedef struct timeval timeval;
typedef uint __uint32_t;
typedef __uint32_t uint32_t;
typedef struct __gs_gui __gs_gui;
typedef struct __gs_gui_config __gs_gui_config;
typedef struct debug_osd_info debug_osd_info;
typedef struct debug_osd_info debug_codec_osd_info_t;
typedef struct debug_cam_osd_info debug_cam_osd_info;
typedef struct debug_cam_osd_info debug_cam_osd_info_t;
typedef struct debug_temp_osd_info debug_temp_osd_info;
typedef struct debug_temp_osd_info debug_temp_osd_info_t;
typedef struct debug_cp_osd_info debug_cp_osd_info;
typedef struct debug_cp_osd_info debug_cp_osd_info_t;
typedef ulonglong __uint64_t;
typedef __uint64_t uint64_t;
typedef enum record_mode
{
RECORD_MODE_MANNUAL = 0,
RECORD_MODE_LOOPING = 1
} record_mode;
typedef enum record_mode record_mode_t;
typedef enum Record_sender
{
RECORD_BUTTON = 0,
RECORD_DISARM = 1
} Record_sender;
typedef ushort __uint16_t;
typedef __uint16_t uint16_t;
typedef struct gs_battery_voltage gs_battery_voltage;
typedef struct gs_battery_voltage gs_battery_voltage_t;
typedef struct __gs_gui_config gs_gui_config_t;
typedef struct gs_gui_event_t gs_gui_event_t;
typedef struct __gs_gui gs_gui_t;
typedef struct gs_ext_fc gs_ext_fc;
typedef struct gs_ext_fc gs_ext_fc_t;
typedef struct gs_shram gs_shram;
typedef struct gs_shram gs_shram_t;
typedef struct __gs_queue __gs_queue;
typedef struct __gs_queue gs_queue_t;
typedef struct gs_user_json_root gs_user_json_root;
typedef struct gs_user_json_root gs_user_json_root_t;
typedef struct duss_osal_mutex_handle_t duss_osal_mutex_handle_t;
typedef struct __gs_avin_test_ctrl __gs_avin_test_ctrl;
typedef struct __gs_avin_test_ctrl gs_avin_test_ctrl_t;
typedef struct gs_watermarker_ctrl gs_watermarker_ctrl;
typedef struct gs_watermarker_ctrl gs_watermarker_ctrl_t;
typedef struct duss_mb_client duss_mb_client;
typedef int32_t duss_result_t;
typedef uint16_t duss_msg_attrib_t;
typedef uint32_t duss_msg_id_t;
typedef struct duss_mb_route_item_t duss_mb_route_item_t;
typedef struct duss_mb_client *duss_mb_client_handle_t;
typedef struct duss_osal_msgq_handle_t duss_osal_msgq_handle_t;
typedef struct duss_osal_event_handle_t duss_osal_event_handle_t;
typedef struct duss_event_ack_identify duss_event_ack_identify;
typedef struct duss_event_ack_identify duss_event_ack_identify_t;
typedef struct duss_event_cmd_desc duss_event_cmd_desc;
typedef struct duss_event duss_event;
typedef struct duss_event duss_event_t;
typedef struct duss_event_cmd_desc duss_event_cmd_desc_t;
typedef struct duss_hal_obj_dev_t duss_hal_obj_dev_t;
typedef struct product_shm_info product_shm_info;
typedef struct product_shm_info product_shm_info_t;
typedef uint size_t;
typedef struct modem_shmem_info_t modem_shmem_info_t;
typedef struct racing_debug_info_t racing_debug_info_t;
typedef struct duss_osal_task_attrib_t duss_osal_task_attrib_t;
typedef long pthread_t;
typedef struct duss_storage_client duss_storage_client;
typedef struct duss_storage_client duss_storage_client_t;
typedef enum sd_file_type
{
SD_FILE_TYPE_UNKNOWN = 0,
SD_FILE_TYPE_FAT32 = 1,
SD_FILE_TYPE_EXFAT = 2,
SD_FILE_TYPE_MAX = 3
} sd_file_type;
typedef enum sd_file_type sd_file_type_t;
typedef struct gs_storage_listener gs_storage_listener;
typedef enum storage_event
{
STORAGE_EVENT_NONE = 0,
STORAGE_EVENT_SPACE_LOW = 1
} storage_event;
typedef enum storage_event storage_event_t;
typedef struct gs_storage_listener gs_storage_listener_t;
typedef struct gs_sd_listener gs_sd_listener;
typedef enum sd_event
{
SD_EVENT_NONE = 0,
SD_EVENT_SPACE_LOW = 1,
SD_EVENT_SD_INSERT = 2,
SD_EVENT_SD_REMOVE = 3
} sd_event;
typedef enum sd_event sd_event_t;
typedef struct gs_sd_listener gs_sd_listener_t;
typedef struct gs_meta_listener gs_meta_listener;
typedef enum meta_event
{
META_EVENT_NONE = 0,
META_EVENT_SD_INSERT = 1,
META_EVENT_SD_REMOVE = 2,
META_EVENT_SD_FORMAT = 3
} meta_event;
typedef enum meta_event meta_event_t;
typedef struct gs_meta_listener gs_meta_listener_t;
typedef struct gs_playback_listener gs_playback_listener;
typedef enum playback_event
{
PB_EVENT_NONE = 0,
PB_EVENT_SD_INSERT = 1,
PB_EVENT_SD_REMOVE = 2,
PB_EVENT_SD_FORMAT = 3
} playback_event;
typedef enum playback_event playback_event_t;
typedef struct gs_playback_listener gs_playback_listener_t;
typedef struct sem_t sem_t;
typedef struct timespec timespec;
typedef struct gs_modem_link_listener gs_modem_link_listener;
typedef struct gs_modem_link_listener gs_modem_link_listener_t;
typedef struct glass_signal_quality_t glass_signal_quality_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t DUSS_MSG_RACING_CHANNEL_ROB_INFO_t;
typedef struct _DUSS_MSG_RACING_PHY_CHECK_REQ_t _DUSS_MSG_RACING_PHY_CHECK_REQ_t;
typedef struct _DUSS_MSG_RACING_PHY_CHECK_REQ_t DUSS_MSG_RACING_PHY_CHECK_REQ_t;
typedef struct _DUSS_MSG_RACING_PHY_CHECK_INFO_t _DUSS_MSG_RACING_PHY_CHECK_INFO_t;
typedef struct _DUSS_MSG_RACING_PHY_CHECK_INFO_t DUSS_MSG_RACING_PHY_CHECK_INFO_t;
typedef enum gs_modem_scan_type_t
{
GS_MODEM_SCAN_ISM = 0,
GS_MODEM_SCAN_HAM = 1,
GS_MODEM_SCAN_JAPAN_HAM_5G = 2,
GS_MODEM_SCAN_HAM_MATCH = 3,
GS_MODEM_SCAN_HAM_40MHz = 4,
GS_MODEM_SCAN_HAM_40MHz_EXTRA = 5,
GS_MODEM_SCAN_UKNOWN = 6
} gs_modem_scan_type_t;
typedef union rc_set_reverse_t rc_set_reverse_t;
typedef struct rc_set_all_st_t rc_set_all_st_t;
typedef struct rc_set_all_ep_t rc_set_all_ep_t;
typedef struct rc_set_all_st_and_rev_t rc_set_all_st_and_rev_t;
typedef struct dummy_ui_ctx_t dummy_ui_ctx_t;
typedef void (*duss_osal_timer_entry_t)(void *);
typedef enum duss_osal_priority_t
{
DUSS_OSAL_PRI_NORMAL = 0,
DUSS_OSAL_PRI_LOWEST = 1,
DUSS_OSAL_PRI_LOW1 = 3,
DUSS_OSAL_PRI_LOW2 = 6,
DUSS_OSAL_PRI_LOW3 = 9,
DUSS_OSAL_PRI_MID1 = 13,
DUSS_OSAL_PRI_MID2 = 16,
DUSS_OSAL_PRI_MID3 = 19,
DUSS_OSAL_PRI_HIGH1 = 23,
DUSS_OSAL_PRI_HIGH2 = 26,
DUSS_OSAL_PRI_HIGH3 = 29,
DUSS_OSAL_PRI_TIM1 = 33,
DUSS_OSAL_PRI_TIM2 = 36,
DUSS_OSAL_PRI_TIM3 = 39,
DUSS_OSAL_PRI_INT = 50,
DUSS_OSAL_PRI_HIGHEST = 99
} duss_osal_priority_t;
typedef struct factory_check factory_check;
typedef struct anon_struct_conflictc3fb_for_sw anon_struct_conflictc3fb_for_sw;
typedef struct anon_struct_conflictc431_for_key anon_struct_conflictc431_for_key;
typedef struct duss_osal_mutex_attrib_t duss_osal_mutex_attrib_t;
typedef enum gs_fan_level_t
{
GS_FAN_LEVEL_0 = 0,
GS_FAN_LEVEL_1 = 1,
GS_FAN_LEVEL_2 = 2,
GS_FAN_LEVEL_3 = 3,
GS_FAN_LEVEL_4 = 4,
GS_FAN_LEVEL_5 = 5,
GS_FAN_LEVEL_MAX = 6
} gs_fan_level_t;
typedef struct _DUSS_MSG_SYSTEM_STATE_t _DUSS_MSG_SYSTEM_STATE_t;
typedef struct _DUSS_MSG_SYSTEM_STATE_t DUSS_MSG_SYSTEM_STATE_t;
typedef struct _DUSS_MSG_F_INDEX_MODE_t _DUSS_MSG_F_INDEX_MODE_t;
typedef struct _DUSS_MSG_F_INDEX_MODE_t DUSS_MSG_F_INDEX_MODE_t;
typedef struct _DUSS_MSG_QUICKVIEW_t _DUSS_MSG_QUICKVIEW_t;
typedef struct _DUSS_MSG_QUICKVIEW_t DUSS_MSG_QUICKVIEW_t;
typedef struct _DUSS_MSG_PHOTO_OSD_PARA_ _DUSS_MSG_PHOTO_OSD_PARA_;
typedef struct _DUSS_MSG_PHOTO_OSD_PARA_ DUSS_MSG_PHOTO_OSD_PARA_t;
typedef struct _DUSS_MSG_PREVIEW_OSD_PARA_t _DUSS_MSG_PREVIEW_OSD_PARA_t;
typedef struct _DUSS_MSG_PREVIEW_OSD_PARA_t DUSS_MSG_PREVIEW_OSD_PARA_t;
typedef struct _DUSS_MSG_HISTOGRAM_t _DUSS_MSG_HISTOGRAM_t;
typedef struct _DUSS_MSG_HISTOGRAM_t DUSS_MSG_HISTOGRAM_t;
typedef struct _DUSS_MSG_CAMERA_AUDIO_STATUS_t _DUSS_MSG_CAMERA_AUDIO_STATUS_t;
typedef struct _DUSS_MSG_CAMERA_AUDIO_STATUS_t DUSS_MSG_CAMERA_AUDIO_STATUS_t;
typedef struct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t;
typedef struct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t;
typedef struct gs_wl_channel gs_wl_channel;
typedef struct gs_wl_channel gs_wl_channel_t;
typedef struct gs_local_playback_channel gs_local_playback_channel;
typedef struct gs_local_playback_channel gs_local_playback_channel_t;
typedef struct gs_av_in_channel gs_av_in_channel;
typedef struct gs_av_in_channel gs_av_in_channel_t;
typedef struct gs_rc_setting_channel gs_rc_setting_channel;
typedef struct gs_rc_setting_channel gs_rc_setting_channel_t;
typedef struct gs_csi_channel gs_csi_channel;
typedef struct gs_csi_channel gs_csi_channel_t;
typedef struct gs_non_video_channel gs_non_video_channel;
typedef struct gs_non_video_channel gs_non_video_channel_t;
typedef struct gs_video_channel gs_video_channel;
typedef struct gs_video_channel_message gs_video_channel_message;
typedef struct gs_video_channel_message gs_video_channel_message_t;
typedef struct gs_video_channel gs_video_channel_t;
typedef struct gs_video_channel_id gs_video_channel_id;
typedef struct gs_video_channel_id gs_video_channel_id_t;
typedef void (*gs_video_channel_switch_callback_t)(void *, gs_video_channel_id_t *);
typedef void (*gs_video_channel_push_callback_t)(void *, gs_video_channel_id_t *);
typedef struct gs_avin_us gs_avin_us;
typedef struct gs_avin_us gs_avin_us_t;
typedef struct AVFormatContext AVFormatContext;
typedef struct audio_dec audio_dec;
typedef enum decoder_event
{
DEC_EVENT_INVALID = 0,
DEC_EVENT_A_EOS = 1,
DEC_EVENT_V_EOS = 2
} decoder_event;
typedef enum decoder_event dec_event_t;
typedef struct audio_dec audio_dec_t;
typedef struct cp_vdec cp_vdec;
typedef struct cp_vdec cp_vdec_t;
typedef struct vdec_video_file_info vdec_video_file_info;
typedef struct vdec_video_file_info vdec_video_file_info_t;
typedef longlong __int64_t;
typedef __int64_t int64_t;
typedef struct sqlite3 sqlite3;
typedef struct gs_lv_csm gs_lv_csm;
typedef struct gs_lv_pkt gs_lv_pkt;
typedef struct gs_lv_pkt gs_lv_pkt_t;
typedef struct gs_lv_csm gs_lv_csm_t;
typedef struct duss_list_head duss_list_head;
typedef struct gs_usb_listener gs_usb_listener;
typedef enum usb_event
{
USB_EVENT_NONE = 0,
USB_EVENT_GADGET_CONNECTED = 1
} usb_event;
typedef enum usb_event usb_event_t;
typedef struct gs_usb_listener gs_usb_listener_t;
typedef struct ion_info ion_info;
typedef struct ion_info ion_info_t;
typedef enum record_state
{
RECORD_STATE_IDLE = 0,
RECORD_STATE_RECORDING = 1,
RECORD_STATE_STOP_ERROR = 2,
RECORD_STATE_STOP_FULL = 3,
RECORD_STATE_STOP_WRITE_SLOW = 4,
RECORD_STATE_STARTING = 5,
RECORD_STATE_STOPPING = 6
} record_state;
typedef enum record_state record_state_t;
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
typedef enum AVPixelFormat
{
AV_PIX_FMT_NONE = -1,
PIX_FMT_NONE = -1,
AV_PIX_FMT_YUV420P = 0,
PIX_FMT_YUV420P = 0,
AV_PIX_FMT_YUYV422 = 1,
PIX_FMT_YUYV422 = 1,
AV_PIX_FMT_RGB24 = 2,
PIX_FMT_RGB24 = 2,
AV_PIX_FMT_BGR24 = 3,
PIX_FMT_BGR24 = 3,
AV_PIX_FMT_YUV422P = 4,
PIX_FMT_YUV422P = 4,
AV_PIX_FMT_YUV444P = 5,
PIX_FMT_YUV444P = 5,
AV_PIX_FMT_YUV410P = 6,
PIX_FMT_YUV410P = 6,
AV_PIX_FMT_YUV411P = 7,
PIX_FMT_YUV411P = 7,
AV_PIX_FMT_GRAY8 = 8,
PIX_FMT_GRAY8 = 8,
AV_PIX_FMT_MONOWHITE = 9,
PIX_FMT_MONOWHITE = 9,
AV_PIX_FMT_MONOBLACK = 10,
PIX_FMT_MONOBLACK = 10,
AV_PIX_FMT_PAL8 = 11,
PIX_FMT_PAL8 = 11,
AV_PIX_FMT_YUVJ420P = 12,
PIX_FMT_YUVJ420P = 12,
AV_PIX_FMT_YUVJ422P = 13,
PIX_FMT_YUVJ422P = 13,
AV_PIX_FMT_YUVJ444P = 14,
PIX_FMT_YUVJ444P = 14,
AV_PIX_FMT_XVMC_MPEG2_MC = 15,
PIX_FMT_XVMC_MPEG2_MC = 15,
AV_PIX_FMT_XVMC_MPEG2_IDCT = 16,
PIX_FMT_XVMC_MPEG2_IDCT = 16,
AV_PIX_FMT_UYVY422 = 17,
PIX_FMT_UYVY422 = 17,
AV_PIX_FMT_UYYVYY411 = 18,
PIX_FMT_UYYVYY411 = 18,
AV_PIX_FMT_BGR8 = 19,
PIX_FMT_BGR8 = 19,
AV_PIX_FMT_BGR4 = 20,
PIX_FMT_BGR4 = 20,
AV_PIX_FMT_BGR4_BYTE = 21,
PIX_FMT_BGR4_BYTE = 21,
AV_PIX_FMT_RGB8 = 22,
PIX_FMT_RGB8 = 22,
AV_PIX_FMT_RGB4 = 23,
PIX_FMT_RGB4 = 23,
AV_PIX_FMT_RGB4_BYTE = 24,
PIX_FMT_RGB4_BYTE = 24,
AV_PIX_FMT_NV12 = 25,
PIX_FMT_NV12 = 25,
AV_PIX_FMT_NV21 = 26,
PIX_FMT_NV21 = 26,
AV_PIX_FMT_ARGB = 27,
PIX_FMT_ARGB = 27,
AV_PIX_FMT_RGBA = 28,
PIX_FMT_RGBA = 28,
AV_PIX_FMT_ABGR = 29,
PIX_FMT_ABGR = 29,
AV_PIX_FMT_BGRA = 30,
PIX_FMT_BGRA = 30,
AV_PIX_FMT_GRAY16BE = 31,
PIX_FMT_GRAY16BE = 31,
AV_PIX_FMT_GRAY16LE = 32,
PIX_FMT_GRAY16LE = 32,
AV_PIX_FMT_YUV440P = 33,
PIX_FMT_YUV440P = 33,
AV_PIX_FMT_YUVJ440P = 34,
PIX_FMT_YUVJ440P = 34,
AV_PIX_FMT_YUVA420P = 35,
PIX_FMT_YUVA420P = 35,
AV_PIX_FMT_VDPAU_H264 = 36,
PIX_FMT_VDPAU_H264 = 36,
AV_PIX_FMT_VDPAU_MPEG1 = 37,
PIX_FMT_VDPAU_MPEG1 = 37,
AV_PIX_FMT_VDPAU_MPEG2 = 38,
PIX_FMT_VDPAU_MPEG2 = 38,
AV_PIX_FMT_VDPAU_WMV3 = 39,
PIX_FMT_VDPAU_WMV3 = 39,
AV_PIX_FMT_VDPAU_VC1 = 40,
PIX_FMT_VDPAU_VC1 = 40,
AV_PIX_FMT_RGB48BE = 41,
PIX_FMT_RGB48BE = 41,
AV_PIX_FMT_RGB48LE = 42,
PIX_FMT_RGB48LE = 42,
AV_PIX_FMT_RGB565BE = 43,
PIX_FMT_RGB565BE = 43,
AV_PIX_FMT_RGB565LE = 44,
PIX_FMT_RGB565LE = 44,
AV_PIX_FMT_RGB555BE = 45,
PIX_FMT_RGB555BE = 45,
AV_PIX_FMT_RGB555LE = 46,
PIX_FMT_RGB555LE = 46,
AV_PIX_FMT_BGR565BE = 47,
PIX_FMT_BGR565BE = 47,
AV_PIX_FMT_BGR565LE = 48,
PIX_FMT_BGR565LE = 48,
AV_PIX_FMT_BGR555BE = 49,
PIX_FMT_BGR555BE = 49,
AV_PIX_FMT_BGR555LE = 50,
PIX_FMT_BGR555LE = 50,
AV_PIX_FMT_VAAPI_MOCO = 51,
PIX_FMT_VAAPI_MOCO = 51,
AV_PIX_FMT_VAAPI_IDCT = 52,
PIX_FMT_VAAPI_IDCT = 52,
AV_PIX_FMT_VAAPI_VLD = 53,
PIX_FMT_VAAPI_VLD = 53,
AV_PIX_FMT_YUV420P16LE = 54,
PIX_FMT_YUV420P16LE = 54,
AV_PIX_FMT_YUV420P16BE = 55,
PIX_FMT_YUV420P16BE = 55,
AV_PIX_FMT_YUV422P16LE = 56,
PIX_FMT_YUV422P16LE = 56,
AV_PIX_FMT_YUV422P16BE = 57,
PIX_FMT_YUV422P16BE = 57,
AV_PIX_FMT_YUV444P16LE = 58,
PIX_FMT_YUV444P16LE = 58,
AV_PIX_FMT_YUV444P16BE = 59,
PIX_FMT_YUV444P16BE = 59,
AV_PIX_FMT_VDPAU_MPEG4 = 60,
PIX_FMT_VDPAU_MPEG4 = 60,
AV_PIX_FMT_DXVA2_VLD = 61,
PIX_FMT_DXVA2_VLD = 61,
AV_PIX_FMT_RGB444LE = 62,
PIX_FMT_RGB444LE = 62,
AV_PIX_FMT_RGB444BE = 63,
PIX_FMT_RGB444BE = 63,
AV_PIX_FMT_BGR444LE = 64,
PIX_FMT_BGR444LE = 64,
AV_PIX_FMT_BGR444BE = 65,
PIX_FMT_BGR444BE = 65,
AV_PIX_FMT_GRAY8A = 66,
AV_PIX_FMT_Y400A = 66,
AV_PIX_FMT_YA8 = 66,
PIX_FMT_GRAY8A = 66,
AV_PIX_FMT_BGR48BE = 67,
PIX_FMT_BGR48BE = 67,
AV_PIX_FMT_BGR48LE = 68,
PIX_FMT_BGR48LE = 68,
AV_PIX_FMT_YUV420P9BE = 69,
PIX_FMT_YUV420P9BE = 69,
AV_PIX_FMT_YUV420P9LE = 70,
PIX_FMT_YUV420P9LE = 70,
AV_PIX_FMT_YUV420P10BE = 71,
PIX_FMT_YUV420P10BE = 71,
AV_PIX_FMT_YUV420P10LE = 72,
PIX_FMT_YUV420P10LE = 72,
AV_PIX_FMT_YUV422P10BE = 73,
PIX_FMT_YUV422P10BE = 73,
AV_PIX_FMT_YUV422P10LE = 74,
PIX_FMT_YUV422P10LE = 74,
AV_PIX_FMT_YUV444P9BE = 75,
PIX_FMT_YUV444P9BE = 75,
AV_PIX_FMT_YUV444P9LE = 76,
PIX_FMT_YUV444P9LE = 76,
AV_PIX_FMT_YUV444P10BE = 77,
PIX_FMT_YUV444P10BE = 77,
AV_PIX_FMT_YUV444P10LE = 78,
PIX_FMT_YUV444P10LE = 78,
AV_PIX_FMT_YUV422P9BE = 79,
PIX_FMT_YUV422P9BE = 79,
AV_PIX_FMT_YUV422P9LE = 80,
PIX_FMT_YUV422P9LE = 80,
AV_PIX_FMT_VDA_VLD = 81,
PIX_FMT_VDA_VLD = 81,
AV_PIX_FMT_GBRP = 82,
PIX_FMT_GBRP = 82,
AV_PIX_FMT_GBRP9BE = 83,
PIX_FMT_GBRP9BE = 83,
AV_PIX_FMT_GBRP9LE = 84,
PIX_FMT_GBRP9LE = 84,
AV_PIX_FMT_GBRP10BE = 85,
PIX_FMT_GBRP10BE = 85,
AV_PIX_FMT_GBRP10LE = 86,
PIX_FMT_GBRP10LE = 86,
AV_PIX_FMT_GBRP16BE = 87,
PIX_FMT_GBRP16BE = 87,
AV_PIX_FMT_GBRP16LE = 88,
PIX_FMT_GBRP16LE = 88,
AV_PIX_FMT_YUVA422P_LIBAV = 89,
AV_PIX_FMT_YUVA444P_LIBAV = 90,
AV_PIX_FMT_YUVA420P9BE = 91,
AV_PIX_FMT_YUVA420P9LE = 92,
AV_PIX_FMT_YUVA422P9BE = 93,
AV_PIX_FMT_YUVA422P9LE = 94,
AV_PIX_FMT_YUVA444P9BE = 95,
AV_PIX_FMT_YUVA444P9LE = 96,
AV_PIX_FMT_YUVA420P10BE = 97,
AV_PIX_FMT_YUVA420P10LE = 98,
AV_PIX_FMT_YUVA422P10BE = 99,
AV_PIX_FMT_YUVA422P10LE = 100,
AV_PIX_FMT_YUVA444P10BE = 101,
AV_PIX_FMT_YUVA444P10LE = 102,
AV_PIX_FMT_YUVA420P16BE = 103,
AV_PIX_FMT_YUVA420P16LE = 104,
AV_PIX_FMT_YUVA422P16BE = 105,
AV_PIX_FMT_YUVA422P16LE = 106,
AV_PIX_FMT_YUVA444P16BE = 107,
AV_PIX_FMT_YUVA444P16LE = 108,
AV_PIX_FMT_VDPAU = 109,
AV_PIX_FMT_XYZ12LE = 110,
AV_PIX_FMT_XYZ12BE = 111,
AV_PIX_FMT_NV16 = 112,
AV_PIX_FMT_NV20LE = 113,
AV_PIX_FMT_NV20BE = 114,
AV_PIX_FMT_RGBA64BE_LIBAV = 115,
AV_PIX_FMT_RGBA64LE_LIBAV = 116,
AV_PIX_FMT_BGRA64BE_LIBAV = 117,
AV_PIX_FMT_BGRA64LE_LIBAV = 118,
AV_PIX_FMT_YVYU422 = 119,
AV_PIX_FMT_VDA = 120,
AV_PIX_FMT_YA16BE = 121,
AV_PIX_FMT_YA16LE = 122,
AV_PIX_FMT_RGBA64BE = 291,
PIX_FMT_RGBA64BE = 291,
AV_PIX_FMT_RGBA64LE = 292,
PIX_FMT_RGBA64LE = 292,
AV_PIX_FMT_BGRA64BE = 293,
PIX_FMT_BGRA64BE = 293,
AV_PIX_FMT_BGRA64LE = 294,
PIX_FMT_BGRA64LE = 294,
AV_PIX_FMT_0RGB = 295,
PIX_FMT_0RGB = 295,
AV_PIX_FMT_RGB0 = 296,
PIX_FMT_RGB0 = 296,
AV_PIX_FMT_0BGR = 297,
PIX_FMT_0BGR = 297,
AV_PIX_FMT_BGR0 = 298,
PIX_FMT_BGR0 = 298,
AV_PIX_FMT_YUVA444P = 299,
PIX_FMT_YUVA444P = 299,
AV_PIX_FMT_YUVA422P = 300,
PIX_FMT_YUVA422P = 300,
AV_PIX_FMT_YUV420P12BE = 301,
PIX_FMT_YUV420P12BE = 301,
AV_PIX_FMT_YUV420P12LE = 302,
PIX_FMT_YUV420P12LE = 302,
AV_PIX_FMT_YUV420P14BE = 303,
PIX_FMT_YUV420P14BE = 303,
AV_PIX_FMT_YUV420P14LE = 304,
PIX_FMT_YUV420P14LE = 304,
AV_PIX_FMT_YUV422P12BE = 305,
PIX_FMT_YUV422P12BE = 305,
AV_PIX_FMT_YUV422P12LE = 306,
PIX_FMT_YUV422P12LE = 306,
AV_PIX_FMT_YUV422P14BE = 307,
PIX_FMT_YUV422P14BE = 307,
AV_PIX_FMT_YUV422P14LE = 308,
PIX_FMT_YUV422P14LE = 308,
AV_PIX_FMT_YUV444P12BE = 309,
PIX_FMT_YUV444P12BE = 309,
AV_PIX_FMT_YUV444P12LE = 310,
PIX_FMT_YUV444P12LE = 310,
AV_PIX_FMT_YUV444P14BE = 311,
PIX_FMT_YUV444P14BE = 311,
AV_PIX_FMT_YUV444P14LE = 312,
PIX_FMT_YUV444P14LE = 312,
AV_PIX_FMT_GBRP12BE = 313,
PIX_FMT_GBRP12BE = 313,
AV_PIX_FMT_GBRP12LE = 314,
PIX_FMT_GBRP12LE = 314,
AV_PIX_FMT_GBRP14BE = 315,
PIX_FMT_GBRP14BE = 315,
AV_PIX_FMT_GBRP14LE = 316,
PIX_FMT_GBRP14LE = 316,
AV_PIX_FMT_GBRAP = 317,
PIX_FMT_NB = 317,
AV_PIX_FMT_GBRAP16BE = 318,
AV_PIX_FMT_GBRAP16LE = 319,
AV_PIX_FMT_YUVJ411P = 320,
AV_PIX_FMT_BAYER_BGGR8 = 321,
AV_PIX_FMT_BAYER_RGGB8 = 322,
AV_PIX_FMT_BAYER_GBRG8 = 323,
AV_PIX_FMT_BAYER_GRBG8 = 324,
AV_PIX_FMT_BAYER_BGGR16LE = 325,
AV_PIX_FMT_BAYER_BGGR16BE = 326,
AV_PIX_FMT_BAYER_RGGB16LE = 327,
AV_PIX_FMT_BAYER_RGGB16BE = 328,
AV_PIX_FMT_BAYER_GBRG16LE = 329,
AV_PIX_FMT_BAYER_GBRG16BE = 330,
AV_PIX_FMT_BAYER_GRBG16LE = 331,
AV_PIX_FMT_BAYER_GRBG16BE = 332,
AV_PIX_FMT_NB = 333
} AVPixelFormat;
typedef struct pl_muxer_t pl_muxer_t;
typedef struct pl_muxer_t *p1_muxer_handle_t;
typedef struct __sFILE __sFILE;
typedef long __kernel_long_t;
typedef __kernel_long_t __kernel_off_t;
typedef __kernel_off_t off_t;
typedef off_t fpos_t;
typedef struct __sFILE FILE;
typedef struct pcm_config pcm_config;
typedef struct pcm pcm;
typedef __kernel_long_t __kernel_time_t;
typedef __kernel_long_t __kernel_suseconds_t;
typedef struct ext_fc_ops ext_fc_ops;
typedef enum batteryState_e
{
BATTERY_OK = 0,
BATTERY_WARNING = 1,
BATTERY_CRITICAL = 2,
BATTERY_NOT_PRESENT = 3,
BATTERY_INIT = 4
} batteryState_e;
typedef struct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH DUSS_MSG_FC_RACING_DRONE_OSD_PUSH;
typedef struct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t;
typedef struct DUSS_MSG_EXT_FC_RTC DUSS_MSG_EXT_FC_RTC;
typedef struct DUSS_MSG_EXT_FC_RTC DUSS_MSG_EXT_FC_RTC_t;
typedef struct ext_fc_ops ext_fc_ops_t;
typedef struct vr_device_ops vr_device_ops;
typedef short __int16_t;
typedef __int16_t int16_t;
typedef struct vr_device_ops vr_device_ops_t;
typedef struct local_playback_ops local_playback_ops;
typedef struct local_playback_ops local_playback_ops_t;
typedef struct uav_camera_ops uav_camera_ops;
typedef struct __gs_camera_cmd __gs_camera_cmd;
typedef struct __gs_camera_cmd gs_camera_cmd_t;
typedef struct uav_camera_ops uav_camera_ops_t;
typedef struct uav_gimbal_ops uav_gimbal_ops;
typedef struct uav_gimbal_ops uav_gimbal_ops_t;
typedef struct modem_ops modem_ops;
typedef enum gs_link_stat_t
{
GS_LINK_STAT_NORMAL = 0,
GS_LINK_STAT_WEAK = 1,
GS_LINK_STAT_LOST = 2,
GS_LINK_STAT_UKNOWN = 3
} gs_link_stat_t;
typedef struct modem_ops modem_ops_t;
typedef struct rc_ops rc_ops;
typedef struct rc_set_function_pack_t rc_set_function_pack_t;
typedef struct rc_set_stick_mode_pack_t rc_set_stick_mode_pack_t;
typedef struct rc_set_warning_mode_pack_t rc_set_warning_mode_pack_t;
typedef struct rc_monitor_pack_t rc_monitor_pack_t;
typedef struct rc_ops rc_ops_t;
typedef struct vcm_ops vcm_ops;
typedef enum gs_main_channel_id
{
GS_FIRST_VIDEO_CHANNEL = 0,
GS_WL_CHANNEL = 0,
GS_LOCAL_PLAYBACK_CHANNEL = 1,
GS_AV_IN_CHANNEL = 2,
GS_RC_SETTING_CHANNEL = 3,
GS_CSI_CHANNEL = 4,
GS_NONE_VIDEO_CHANNEL = 5,
GS_VIDEO_CHANNEL_COUNT = 6,
GS_UNKNOWN_VIDEO_CHANNEL = 7
} gs_main_channel_id;
typedef enum gs_main_channel_id gs_main_channel_id_t;
typedef struct vcm_ops vcm_ops_t;
typedef struct common_cmd_ops common_cmd_ops;
typedef struct __gs_common_cmd __gs_common_cmd;
typedef struct __gs_common_cmd gs_common_cmd_t;
typedef struct common_cmd_ops common_cmd_ops_t;
typedef struct debug_osd_item debug_osd_item;
typedef struct debug_osd_item debug_osd_item_t;
typedef uint gs_gui_event_type_t;
typedef uint gs_gui_event_symbol_t;
typedef struct DUSS_MSG_EXT_FC_PID DUSS_MSG_EXT_FC_PID;
typedef struct DUSS_MSG_EXT_FC_PID DUSS_MSG_EXT_FC_PID_t;
typedef struct DUSS_MSG_EXT_FC_AUX DUSS_MSG_EXT_FC_AUX;
typedef struct DUSS_MSG_EXT_FC_AUX DUSS_MSG_EXT_FC_AUX_t;
typedef struct DUSS_MSG_EXT_FC_RATE DUSS_MSG_EXT_FC_RATE;
typedef struct DUSS_MSG_EXT_FC_RATE DUSS_MSG_EXT_FC_RATE_t;
typedef struct DUSS_MSG_EXT_FC_SERVO DUSS_MSG_EXT_FC_SERVO;
typedef struct DUSS_MSG_EXT_FC_SERVO DUSS_MSG_EXT_FC_SERVO_t;
typedef struct DUSS_MSG_EXT_FC_FILTER DUSS_MSG_EXT_FC_FILTER;
typedef struct DUSS_MSG_EXT_FC_FILTER DUSS_MSG_EXT_FC_FILTER_t;
typedef struct DUSS_MSG_EXT_FC_ADVANCED_PID DUSS_MSG_EXT_FC_ADVANCED_PID;
typedef struct DUSS_MSG_EXT_FC_ADVANCED_PID DUSS_MSG_EXT_FC_ADVANCED_PID_t;
typedef struct DUSS_MSG_EXT_FC_MSP_STATUS DUSS_MSG_EXT_FC_MSP_STATUS;
typedef struct DUSS_MSG_EXT_FC_MSP_STATUS DUSS_MSG_EXT_FC_MSP_STATUS_t;
typedef struct DUSS_MSG_EXT_FC_RC DUSS_MSG_EXT_FC_RC;
typedef struct DUSS_MSG_EXT_FC_RC DUSS_MSG_EXT_FC_RC_t;
typedef struct DUSS_MSG_EXT_FC_BATTERY_STATE DUSS_MSG_EXT_FC_BATTERY_STATE;
typedef struct DUSS_MSG_EXT_FC_BATTERY_STATE DUSS_MSG_EXT_FC_BATTERY_STATE_t;
typedef struct DUSS_MSG_EXT_FC_OSD_CONFIG DUSS_MSG_EXT_FC_OSD_CONFIG;
typedef struct DUSS_MSG_EXT_FC_OSD_CONFIG DUSS_MSG_EXT_FC_OSD_CONFIG_t;
typedef struct DUSS_MSG_EXT_FC_ESC_DATA DUSS_MSG_EXT_FC_ESC_DATA;
typedef struct DUSS_MSG_EXT_FC_ESC_DATA DUSS_MSG_EXT_FC_ESC_DATA_t;
typedef struct DUSS_MSG_EXT_FC_VERSION DUSS_MSG_EXT_FC_VERSION;
typedef struct DUSS_MSG_EXT_FC_VERSION DUSS_MSG_EXT_FC_VERSION_t;
typedef enum GS_EXT_FC_OSD_STATUS
{
OSD_CONFIG_REQUEST_UPDATE = 0,
OSD_CONFIG_UPDATE_COMPLETE = 1
} GS_EXT_FC_OSD_STATUS;
typedef struct cJSON cJSON;
typedef struct gs_watermarker_us gs_watermarker_us;
typedef struct gs_watermarker_us gs_watermarker_us_t;
typedef uint8_t duss_mb_channel_t;
typedef uint8_t duss_mb_pack_ver_t;
typedef union anon_union_conflict1232_for_channel anon_union_conflict1232_for_channel;
typedef struct duss_mb_filter_t duss_mb_filter_t;
typedef struct duss_mb_route_table_t duss_mb_route_table_t;
typedef struct fd_set fd_set;
typedef struct duss_osal_msgq_attrib_t duss_osal_msgq_attrib_t;
typedef struct duss_osal_event_attrib_t duss_osal_event_attrib_t;
typedef uint8_t duss_hal_state_t;
typedef uint8_t duss_hal_class_t;
typedef void (*duss_osal_task_entry_t)(void *);
typedef struct duss_hal_storage_params duss_hal_storage_params;
typedef struct duss_hal_storage_info duss_hal_storage_info;
typedef struct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t;
typedef struct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t;
typedef struct anon_struct_conflict47e0 anon_struct_conflict47e0;
typedef struct rc_set_subtrim_t rc_set_subtrim_t;
typedef struct rc_set_endpoint_t rc_set_endpoint_t;
typedef struct rc_set_subtrim_reverse_t rc_set_subtrim_reverse_t;
typedef struct _DUSS_MSG_CAM_STATE_t _DUSS_MSG_CAM_STATE_t;
typedef struct _DUSS_MSG_CAM_STATE_t DUSS_MSG_CAM_STATE_t;
typedef union anon_union_conflict23e3_for_u anon_union_conflict23e3_for_u;
typedef union anon_union_conflict2544_for_u anon_union_conflict2544_for_u;
typedef enum gs_video_channel_message_id_t
{
GS_CHANNEL_MSG_HDMI_PLUG = 0,
GS_CHANNEL_MSG_CAMERA_WORKMODE = 1,
GS_CHANNEL_MSG_CAMERA_PBMODE = 2,
GS_CHANNEL_MSG_CAMERA_MODEL = 3,
GS_CHANNEL_MSG_WIRELESS_LINK_STATUS = 4,
GS_CHANNEL_MSG_LOCAL_PLAYBACK_ON = 5,
GS_CHANNEL_MSG_LOCAL_PLAYBACK_OFF = 6,
GS_CHANNEL_MSG_CHANNEL_ENABLE = 7,
GS_CHANNEL_MSG_CHANNEL_DISABLE = 8,
GS_CHANNEL_MSG_SUB_CHANNEL_ENABLE = 9,
GS_CHANNEL_MSG_SUB_CHANNEL_DISABLE = 10,
GS_CHANNEL_MSG_ANALOG_VIDEO_ON = 11,
GS_CHANNEL_MSG_ANALOG_VIDEO_OFF = 12,
GS_CHANNEL_MSG_RC_SETTING_ON = 13,
GS_CHANNEL_MSG_RC_SETTING_OFF = 14,
GS_CHANNEL_MSG_ID_COUNT = 15
} gs_video_channel_message_id_t;
typedef union anon_union_conflict3b31_for_field_1 anon_union_conflict3b31_for_field_1;
typedef enum gs_sub_channel_id
{
GS_FIRST_SUB_CHANNEL = 0,
GS_LIVEVIEW_SUB_CHANNEL = 0,
GS_PLAYBACK_SUB_CHANNEL = 1,
GS_RACING_SUB_CHANNEL = 2,
GS_HDMI_SUB_CHANNEL = 3,
GS_ANALOG_VIDEO_SUB_CHANNEL = 4,
GS_RC_SETTING_SUB_CHANNEL = 5,
GS_SUB_CHANNEL_COUNT = 6,
GS_NO_SUB_CHANNEL = 7
} gs_sub_channel_id;
typedef enum gs_sub_channel_id gs_sub_channel_id_t;
typedef struct AVClass AVClass;
typedef enum AVClassCategory
{
AV_CLASS_CATEGORY_NA = 0,
AV_CLASS_CATEGORY_INPUT = 1,
AV_CLASS_CATEGORY_OUTPUT = 2,
AV_CLASS_CATEGORY_MUXER = 3,
AV_CLASS_CATEGORY_DEMUXER = 4,
AV_CLASS_CATEGORY_ENCODER = 5,
AV_CLASS_CATEGORY_DECODER = 6,
AV_CLASS_CATEGORY_FILTER = 7,
AV_CLASS_CATEGORY_BITSTREAM_FILTER = 8,
AV_CLASS_CATEGORY_SWSCALER = 9,
AV_CLASS_CATEGORY_SWRESAMPLER = 10,
AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT = 41,
AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT = 42,
AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT = 43,
AV_CLASS_CATEGORY_DEVICE_OUTPUT = 44,
AV_CLASS_CATEGORY_DEVICE_INPUT = 45,
AV_CLASS_CATEGORY_NB = 46
} AVClassCategory;
typedef struct AVOptionRanges AVOptionRanges;
typedef struct AVInputFormat AVInputFormat;
typedef struct AVProbeData AVProbeData;
typedef struct AVPacket AVPacket;
typedef struct AVDeviceInfoList AVDeviceInfoList;
typedef struct AVDeviceCapabilitiesQuery AVDeviceCapabilitiesQuery;
typedef struct AVOutputFormat AVOutputFormat;
typedef enum AVCodecID
{
AV_CODEC_ID_NONE = 0,
CODEC_ID_NONE = 0,
AV_CODEC_ID_MPEG1VIDEO = 1,
CODEC_ID_MPEG1VIDEO = 1,
AV_CODEC_ID_MPEG2VIDEO = 2,
CODEC_ID_MPEG2VIDEO = 2,
AV_CODEC_ID_MPEG2VIDEO_XVMC = 3,
CODEC_ID_MPEG2VIDEO_XVMC = 3,
AV_CODEC_ID_H261 = 4,
CODEC_ID_H261 = 4,
AV_CODEC_ID_H263 = 5,
CODEC_ID_H263 = 5,
AV_CODEC_ID_RV10 = 6,
CODEC_ID_RV10 = 6,
AV_CODEC_ID_RV20 = 7,
CODEC_ID_RV20 = 7,
AV_CODEC_ID_MJPEG = 8,
CODEC_ID_MJPEG = 8,
AV_CODEC_ID_MJPEGB = 9,
CODEC_ID_MJPEGB = 9,
AV_CODEC_ID_LJPEG = 10,
CODEC_ID_LJPEG = 10,
AV_CODEC_ID_SP5X = 11,
CODEC_ID_SP5X = 11,
AV_CODEC_ID_JPEGLS = 12,
CODEC_ID_JPEGLS = 12,
AV_CODEC_ID_MPEG4 = 13,
CODEC_ID_MPEG4 = 13,
AV_CODEC_ID_RAWVIDEO = 14,
CODEC_ID_RAWVIDEO = 14,
AV_CODEC_ID_MSMPEG4V1 = 15,
CODEC_ID_MSMPEG4V1 = 15,
AV_CODEC_ID_MSMPEG4V2 = 16,
CODEC_ID_MSMPEG4V2 = 16,
AV_CODEC_ID_MSMPEG4V3 = 17,
CODEC_ID_MSMPEG4V3 = 17,
AV_CODEC_ID_WMV1 = 18,
CODEC_ID_WMV1 = 18,
AV_CODEC_ID_WMV2 = 19,
CODEC_ID_WMV2 = 19,
AV_CODEC_ID_H263P = 20,
CODEC_ID_H263P = 20,
AV_CODEC_ID_H263I = 21,
CODEC_ID_H263I = 21,
AV_CODEC_ID_FLV1 = 22,
CODEC_ID_FLV1 = 22,
AV_CODEC_ID_SVQ1 = 23,
CODEC_ID_SVQ1 = 23,
AV_CODEC_ID_SVQ3 = 24,
CODEC_ID_SVQ3 = 24,
AV_CODEC_ID_DVVIDEO = 25,
CODEC_ID_DVVIDEO = 25,
AV_CODEC_ID_HUFFYUV = 26,
CODEC_ID_HUFFYUV = 26,
AV_CODEC_ID_CYUV = 27,
CODEC_ID_CYUV = 27,
AV_CODEC_ID_H264 = 28,
CODEC_ID_H264 = 28,
AV_CODEC_ID_INDEO3 = 29,
CODEC_ID_INDEO3 = 29,
AV_CODEC_ID_VP3 = 30,
CODEC_ID_VP3 = 30,
AV_CODEC_ID_THEORA = 31,
CODEC_ID_THEORA = 31,
AV_CODEC_ID_ASV1 = 32,
CODEC_ID_ASV1 = 32,
AV_CODEC_ID_ASV2 = 33,
CODEC_ID_ASV2 = 33,
AV_CODEC_ID_FFV1 = 34,
CODEC_ID_FFV1 = 34,
AV_CODEC_ID_4XM = 35,
CODEC_ID_4XM = 35,
AV_CODEC_ID_VCR1 = 36,
CODEC_ID_VCR1 = 36,
AV_CODEC_ID_CLJR = 37,
CODEC_ID_CLJR = 37,
AV_CODEC_ID_MDEC = 38,
CODEC_ID_MDEC = 38,
AV_CODEC_ID_ROQ = 39,
CODEC_ID_ROQ = 39,
AV_CODEC_ID_INTERPLAY_VIDEO = 40,
CODEC_ID_INTERPLAY_VIDEO = 40,
AV_CODEC_ID_XAN_WC3 = 41,
CODEC_ID_XAN_WC3 = 41,
AV_CODEC_ID_XAN_WC4 = 42,
CODEC_ID_XAN_WC4 = 42,
AV_CODEC_ID_RPZA = 43,
CODEC_ID_RPZA = 43,
AV_CODEC_ID_CINEPAK = 44,
CODEC_ID_CINEPAK = 44,
AV_CODEC_ID_WS_VQA = 45,
CODEC_ID_WS_VQA = 45,
AV_CODEC_ID_MSRLE = 46,
CODEC_ID_MSRLE = 46,
AV_CODEC_ID_MSVIDEO1 = 47,
CODEC_ID_MSVIDEO1 = 47,
AV_CODEC_ID_IDCIN = 48,
CODEC_ID_IDCIN = 48,
AV_CODEC_ID_8BPS = 49,
CODEC_ID_8BPS = 49,
AV_CODEC_ID_SMC = 50,
CODEC_ID_SMC = 50,
AV_CODEC_ID_FLIC = 51,
CODEC_ID_FLIC = 51,
AV_CODEC_ID_TRUEMOTION1 = 52,
CODEC_ID_TRUEMOTION1 = 52,
AV_CODEC_ID_VMDVIDEO = 53,
CODEC_ID_VMDVIDEO = 53,
AV_CODEC_ID_MSZH = 54,
CODEC_ID_MSZH = 54,
AV_CODEC_ID_ZLIB = 55,
CODEC_ID_ZLIB = 55,
AV_CODEC_ID_QTRLE = 56,
CODEC_ID_QTRLE = 56,
AV_CODEC_ID_TSCC = 57,
CODEC_ID_TSCC = 57,
AV_CODEC_ID_ULTI = 58,
CODEC_ID_ULTI = 58,
AV_CODEC_ID_QDRAW = 59,
CODEC_ID_QDRAW = 59,
AV_CODEC_ID_VIXL = 60,
CODEC_ID_VIXL = 60,
AV_CODEC_ID_QPEG = 61,
CODEC_ID_QPEG = 61,
AV_CODEC_ID_PNG = 62,
CODEC_ID_PNG = 62,
AV_CODEC_ID_PPM = 63,
CODEC_ID_PPM = 63,
AV_CODEC_ID_PBM = 64,
CODEC_ID_PBM = 64,
AV_CODEC_ID_PGM = 65,
CODEC_ID_PGM = 65,
AV_CODEC_ID_PGMYUV = 66,
CODEC_ID_PGMYUV = 66,
AV_CODEC_ID_PAM = 67,
CODEC_ID_PAM = 67,
AV_CODEC_ID_FFVHUFF = 68,
CODEC_ID_FFVHUFF = 68,
AV_CODEC_ID_RV30 = 69,
CODEC_ID_RV30 = 69,
AV_CODEC_ID_RV40 = 70,
CODEC_ID_RV40 = 70,
AV_CODEC_ID_VC1 = 71,
CODEC_ID_VC1 = 71,
AV_CODEC_ID_WMV3 = 72,
CODEC_ID_WMV3 = 72,
AV_CODEC_ID_LOCO = 73,
CODEC_ID_LOCO = 73,
AV_CODEC_ID_WNV1 = 74,
CODEC_ID_WNV1 = 74,
AV_CODEC_ID_AASC = 75,
CODEC_ID_AASC = 75,
AV_CODEC_ID_INDEO2 = 76,
CODEC_ID_INDEO2 = 76,
AV_CODEC_ID_FRAPS = 77,
CODEC_ID_FRAPS = 77,
AV_CODEC_ID_TRUEMOTION2 = 78,
CODEC_ID_TRUEMOTION2 = 78,
AV_CODEC_ID_BMP = 79,
CODEC_ID_BMP = 79,
AV_CODEC_ID_CSCD = 80,
CODEC_ID_CSCD = 80,
AV_CODEC_ID_MMVIDEO = 81,
CODEC_ID_MMVIDEO = 81,
AV_CODEC_ID_ZMBV = 82,
CODEC_ID_ZMBV = 82,
AV_CODEC_ID_AVS = 83,
CODEC_ID_AVS = 83,
AV_CODEC_ID_SMACKVIDEO = 84,
CODEC_ID_SMACKVIDEO = 84,
AV_CODEC_ID_NUV = 85,
CODEC_ID_NUV = 85,
AV_CODEC_ID_KMVC = 86,
CODEC_ID_KMVC = 86,
AV_CODEC_ID_FLASHSV = 87,
CODEC_ID_FLASHSV = 87,
AV_CODEC_ID_CAVS = 88,
CODEC_ID_CAVS = 88,
AV_CODEC_ID_JPEG2000 = 89,
CODEC_ID_JPEG2000 = 89,
AV_CODEC_ID_VMNC = 90,
CODEC_ID_VMNC = 90,
AV_CODEC_ID_VP5 = 91,
CODEC_ID_VP5 = 91,
AV_CODEC_ID_VP6 = 92,
CODEC_ID_VP6 = 92,
AV_CODEC_ID_VP6F = 93,
CODEC_ID_VP6F = 93,
AV_CODEC_ID_TARGA = 94,
CODEC_ID_TARGA = 94,
AV_CODEC_ID_DSICINVIDEO = 95,
CODEC_ID_DSICINVIDEO = 95,
AV_CODEC_ID_TIERTEXSEQVIDEO = 96,
CODEC_ID_TIERTEXSEQVIDEO = 96,
AV_CODEC_ID_TIFF = 97,
CODEC_ID_TIFF = 97,
AV_CODEC_ID_GIF = 98,
CODEC_ID_GIF = 98,
AV_CODEC_ID_DXA = 99,
CODEC_ID_DXA = 99,
AV_CODEC_ID_DNXHD = 100,
CODEC_ID_DNXHD = 100,
AV_CODEC_ID_THP = 101,
CODEC_ID_THP = 101,
AV_CODEC_ID_SGI = 102,
CODEC_ID_SGI = 102,
AV_CODEC_ID_C93 = 103,
CODEC_ID_C93 = 103,
AV_CODEC_ID_BETHSOFTVID = 104,
CODEC_ID_BETHSOFTVID = 104,
AV_CODEC_ID_PTX = 105,
CODEC_ID_PTX = 105,
AV_CODEC_ID_TXD = 106,
CODEC_ID_TXD = 106,
AV_CODEC_ID_VP6A = 107,
CODEC_ID_VP6A = 107,
AV_CODEC_ID_AMV = 108,
CODEC_ID_AMV = 108,
AV_CODEC_ID_VB = 109,
CODEC_ID_VB = 109,
AV_CODEC_ID_PCX = 110,
CODEC_ID_PCX = 110,
AV_CODEC_ID_SUNRAST = 111,
CODEC_ID_SUNRAST = 111,
AV_CODEC_ID_INDEO4 = 112,
CODEC_ID_INDEO4 = 112,
AV_CODEC_ID_INDEO5 = 113,
CODEC_ID_INDEO5 = 113,
AV_CODEC_ID_MIMIC = 114,
CODEC_ID_MIMIC = 114,
AV_CODEC_ID_RL2 = 115,
CODEC_ID_RL2 = 115,
AV_CODEC_ID_ESCAPE124 = 116,
CODEC_ID_ESCAPE124 = 116,
AV_CODEC_ID_DIRAC = 117,
CODEC_ID_DIRAC = 117,
AV_CODEC_ID_BFI = 118,
CODEC_ID_BFI = 118,
AV_CODEC_ID_CMV = 119,
CODEC_ID_CMV = 119,
AV_CODEC_ID_MOTIONPIXELS = 120,
CODEC_ID_MOTIONPIXELS = 120,
AV_CODEC_ID_TGV = 121,
CODEC_ID_TGV = 121,
AV_CODEC_ID_TGQ = 122,
CODEC_ID_TGQ = 122,
AV_CODEC_ID_TQI = 123,
CODEC_ID_TQI = 123,
AV_CODEC_ID_AURA = 124,
CODEC_ID_AURA = 124,
AV_CODEC_ID_AURA2 = 125,
CODEC_ID_AURA2 = 125,
AV_CODEC_ID_V210X = 126,
CODEC_ID_V210X = 126,
AV_CODEC_ID_TMV = 127,
CODEC_ID_TMV = 127,
AV_CODEC_ID_V210 = 128,
CODEC_ID_V210 = 128,
AV_CODEC_ID_DPX = 129,
CODEC_ID_DPX = 129,
AV_CODEC_ID_MAD = 130,
CODEC_ID_MAD = 130,
AV_CODEC_ID_FRWU = 131,
CODEC_ID_FRWU = 131,
AV_CODEC_ID_FLASHSV2 = 132,
CODEC_ID_FLASHSV2 = 132,
AV_CODEC_ID_CDGRAPHICS = 133,
CODEC_ID_CDGRAPHICS = 133,
AV_CODEC_ID_R210 = 134,
CODEC_ID_R210 = 134,
AV_CODEC_ID_ANM = 135,
CODEC_ID_ANM = 135,
AV_CODEC_ID_BINKVIDEO = 136,
CODEC_ID_BINKVIDEO = 136,
AV_CODEC_ID_IFF_ILBM = 137,
CODEC_ID_IFF_ILBM = 137,
AV_CODEC_ID_IFF_BYTERUN1 = 138,
CODEC_ID_IFF_BYTERUN1 = 138,
AV_CODEC_ID_KGV1 = 139,
CODEC_ID_KGV1 = 139,
AV_CODEC_ID_YOP = 140,
CODEC_ID_YOP = 140,
AV_CODEC_ID_VP8 = 141,
CODEC_ID_VP8 = 141,
AV_CODEC_ID_PICTOR = 142,
CODEC_ID_PICTOR = 142,
AV_CODEC_ID_ANSI = 143,
CODEC_ID_ANSI = 143,
AV_CODEC_ID_A64_MULTI = 144,
CODEC_ID_A64_MULTI = 144,
AV_CODEC_ID_A64_MULTI5 = 145,
CODEC_ID_A64_MULTI5 = 145,
AV_CODEC_ID_R10K = 146,
CODEC_ID_R10K = 146,
AV_CODEC_ID_MXPEG = 147,
CODEC_ID_MXPEG = 147,
AV_CODEC_ID_LAGARITH = 148,
CODEC_ID_LAGARITH = 148,
AV_CODEC_ID_PRORES = 149,
CODEC_ID_PRORES = 149,
AV_CODEC_ID_JV = 150,
CODEC_ID_JV = 150,
AV_CODEC_ID_DFA = 151,
CODEC_ID_DFA = 151,
AV_CODEC_ID_WMV3IMAGE = 152,
CODEC_ID_WMV3IMAGE = 152,
AV_CODEC_ID_VC1IMAGE = 153,
CODEC_ID_VC1IMAGE = 153,
AV_CODEC_ID_UTVIDEO = 154,
CODEC_ID_UTVIDEO = 154,
AV_CODEC_ID_BMV_VIDEO = 155,
CODEC_ID_BMV_VIDEO = 155,
AV_CODEC_ID_VBLE = 156,
CODEC_ID_VBLE = 156,
AV_CODEC_ID_DXTORY = 157,
CODEC_ID_DXTORY = 157,
AV_CODEC_ID_V410 = 158,
CODEC_ID_V410 = 158,
AV_CODEC_ID_XWD = 159,
CODEC_ID_XWD = 159,
AV_CODEC_ID_CDXL = 160,
CODEC_ID_CDXL = 160,
AV_CODEC_ID_XBM = 161,
CODEC_ID_XBM = 161,
AV_CODEC_ID_ZEROCODEC = 162,
CODEC_ID_ZEROCODEC = 162,
AV_CODEC_ID_MSS1 = 163,
CODEC_ID_MSS1 = 163,
AV_CODEC_ID_MSA1 = 164,
CODEC_ID_MSA1 = 164,
AV_CODEC_ID_TSCC2 = 165,
CODEC_ID_TSCC2 = 165,
AV_CODEC_ID_MTS2 = 166,
CODEC_ID_MTS2 = 166,
AV_CODEC_ID_CLLC = 167,
CODEC_ID_CLLC = 167,
AV_CODEC_ID_MSS2 = 168,
AV_CODEC_ID_VP9 = 169,
AV_CODEC_ID_AIC = 170,
AV_CODEC_ID_ESCAPE130_DEPRECATED = 171,
AV_CODEC_ID_G2M_DEPRECATED = 172,
AV_CODEC_ID_WEBP_DEPRECATED = 173,
AV_CODEC_ID_HNM4_VIDEO = 174,
AV_CODEC_ID_HEVC_DEPRECATED = 175,
AV_CODEC_ID_FIC = 176,
AV_CODEC_ID_ALIAS_PIX = 177,
AV_CODEC_ID_BRENDER_PIX_DEPRECATED = 178,
AV_CODEC_ID_PAF_VIDEO_DEPRECATED = 179,
AV_CODEC_ID_EXR_DEPRECATED = 180,
AV_CODEC_ID_VP7_DEPRECATED = 181,
AV_CODEC_ID_SANM_DEPRECATED = 182,
AV_CODEC_ID_SGIRLE_DEPRECATED = 183,
AV_CODEC_ID_MVC1_DEPRECATED = 184,
AV_CODEC_ID_MVC2_DEPRECATED = 185,
AV_CODEC_ID_FIRST_AUDIO = 65536,
AV_CODEC_ID_PCM_S16LE = 65536,
CODEC_ID_FIRST_AUDIO = 65536,
CODEC_ID_PCM_S16LE = 65536,
AV_CODEC_ID_PCM_S16BE = 65537,
CODEC_ID_PCM_S16BE = 65537,
AV_CODEC_ID_PCM_U16LE = 65538,
CODEC_ID_PCM_U16LE = 65538,
AV_CODEC_ID_PCM_U16BE = 65539,
CODEC_ID_PCM_U16BE = 65539,
AV_CODEC_ID_PCM_S8 = 65540,
CODEC_ID_PCM_S8 = 65540,
AV_CODEC_ID_PCM_U8 = 65541,
CODEC_ID_PCM_U8 = 65541,
AV_CODEC_ID_PCM_MULAW = 65542,
CODEC_ID_PCM_MULAW = 65542,
AV_CODEC_ID_PCM_ALAW = 65543,
CODEC_ID_PCM_ALAW = 65543,
AV_CODEC_ID_PCM_S32LE = 65544,
CODEC_ID_PCM_S32LE = 65544,
AV_CODEC_ID_PCM_S32BE = 65545,
CODEC_ID_PCM_S32BE = 65545,
AV_CODEC_ID_PCM_U32LE = 65546,
CODEC_ID_PCM_U32LE = 65546,
AV_CODEC_ID_PCM_U32BE = 65547,
CODEC_ID_PCM_U32BE = 65547,
AV_CODEC_ID_PCM_S24LE = 65548,
CODEC_ID_PCM_S24LE = 65548,
AV_CODEC_ID_PCM_S24BE = 65549,
CODEC_ID_PCM_S24BE = 65549,
AV_CODEC_ID_PCM_U24LE = 65550,
CODEC_ID_PCM_U24LE = 65550,
AV_CODEC_ID_PCM_U24BE = 65551,
CODEC_ID_PCM_U24BE = 65551,
AV_CODEC_ID_PCM_S24DAUD = 65552,
CODEC_ID_PCM_S24DAUD = 65552,
AV_CODEC_ID_PCM_ZORK = 65553,
CODEC_ID_PCM_ZORK = 65553,
AV_CODEC_ID_PCM_S16LE_PLANAR = 65554,
CODEC_ID_PCM_S16LE_PLANAR = 65554,
AV_CODEC_ID_PCM_DVD = 65555,
CODEC_ID_PCM_DVD = 65555,
AV_CODEC_ID_PCM_F32BE = 65556,
CODEC_ID_PCM_F32BE = 65556,
AV_CODEC_ID_PCM_F32LE = 65557,
CODEC_ID_PCM_F32LE = 65557,
AV_CODEC_ID_PCM_F64BE = 65558,
CODEC_ID_PCM_F64BE = 65558,
AV_CODEC_ID_PCM_F64LE = 65559,
CODEC_ID_PCM_F64LE = 65559,
AV_CODEC_ID_PCM_BLURAY = 65560,
CODEC_ID_PCM_BLURAY = 65560,
AV_CODEC_ID_PCM_LXF = 65561,
CODEC_ID_PCM_LXF = 65561,
AV_CODEC_ID_S302M = 65562,
CODEC_ID_S302M = 65562,
AV_CODEC_ID_PCM_S8_PLANAR = 65563,
CODEC_ID_PCM_S8_PLANAR = 65563,
AV_CODEC_ID_PCM_S24LE_PLANAR_DEPRECATED = 65564,
AV_CODEC_ID_PCM_S32LE_PLANAR_DEPRECATED = 65565,
AV_CODEC_ID_ADPCM_IMA_QT = 69632,
CODEC_ID_ADPCM_IMA_QT = 69632,
AV_CODEC_ID_ADPCM_IMA_WAV = 69633,
CODEC_ID_ADPCM_IMA_WAV = 69633,
AV_CODEC_ID_ADPCM_IMA_DK3 = 69634,
CODEC_ID_ADPCM_IMA_DK3 = 69634,
AV_CODEC_ID_ADPCM_IMA_DK4 = 69635,
CODEC_ID_ADPCM_IMA_DK4 = 69635,
AV_CODEC_ID_ADPCM_IMA_WS = 69636,
CODEC_ID_ADPCM_IMA_WS = 69636,
AV_CODEC_ID_ADPCM_IMA_SMJPEG = 69637,
CODEC_ID_ADPCM_IMA_SMJPEG = 69637,
AV_CODEC_ID_ADPCM_MS = 69638,
CODEC_ID_ADPCM_MS = 69638,
AV_CODEC_ID_ADPCM_4XM = 69639,
CODEC_ID_ADPCM_4XM = 69639,
AV_CODEC_ID_ADPCM_XA = 69640,
CODEC_ID_ADPCM_XA = 69640,
AV_CODEC_ID_ADPCM_ADX = 69641,
CODEC_ID_ADPCM_ADX = 69641,
AV_CODEC_ID_ADPCM_EA = 69642,
CODEC_ID_ADPCM_EA = 69642,
AV_CODEC_ID_ADPCM_G726 = 69643,
CODEC_ID_ADPCM_G726 = 69643,
AV_CODEC_ID_ADPCM_CT = 69644,
CODEC_ID_ADPCM_CT = 69644,
AV_CODEC_ID_ADPCM_SWF = 69645,
CODEC_ID_ADPCM_SWF = 69645,
AV_CODEC_ID_ADPCM_YAMAHA = 69646,
CODEC_ID_ADPCM_YAMAHA = 69646,
AV_CODEC_ID_ADPCM_SBPRO_4 = 69647,
CODEC_ID_ADPCM_SBPRO_4 = 69647,
AV_CODEC_ID_ADPCM_SBPRO_3 = 69648,
CODEC_ID_ADPCM_SBPRO_3 = 69648,
AV_CODEC_ID_ADPCM_SBPRO_2 = 69649,
CODEC_ID_ADPCM_SBPRO_2 = 69649,
AV_CODEC_ID_ADPCM_THP = 69650,
CODEC_ID_ADPCM_THP = 69650,
AV_CODEC_ID_ADPCM_IMA_AMV = 69651,
CODEC_ID_ADPCM_IMA_AMV = 69651,
AV_CODEC_ID_ADPCM_EA_R1 = 69652,
CODEC_ID_ADPCM_EA_R1 = 69652,
AV_CODEC_ID_ADPCM_EA_R3 = 69653,
CODEC_ID_ADPCM_EA_R3 = 69653,
AV_CODEC_ID_ADPCM_EA_R2 = 69654,
CODEC_ID_ADPCM_EA_R2 = 69654,
AV_CODEC_ID_ADPCM_IMA_EA_SEAD = 69655,
CODEC_ID_ADPCM_IMA_EA_SEAD = 69655,
AV_CODEC_ID_ADPCM_IMA_EA_EACS = 69656,
CODEC_ID_ADPCM_IMA_EA_EACS = 69656,
AV_CODEC_ID_ADPCM_EA_XAS = 69657,
CODEC_ID_ADPCM_EA_XAS = 69657,
AV_CODEC_ID_ADPCM_EA_MAXIS_XA = 69658,
CODEC_ID_ADPCM_EA_MAXIS_XA = 69658,
AV_CODEC_ID_ADPCM_IMA_ISS = 69659,
CODEC_ID_ADPCM_IMA_ISS = 69659,
AV_CODEC_ID_ADPCM_G722 = 69660,
CODEC_ID_ADPCM_G722 = 69660,
AV_CODEC_ID_ADPCM_IMA_APC = 69661,
CODEC_ID_ADPCM_IMA_APC = 69661,
AV_CODEC_ID_ADPCM_VIMA_DEPRECATED = 69662,
AV_CODEC_ID_AMR_NB = 73728,
CODEC_ID_AMR_NB = 73728,
AV_CODEC_ID_AMR_WB = 73729,
CODEC_ID_AMR_WB = 73729,
AV_CODEC_ID_RA_144 = 77824,
CODEC_ID_RA_144 = 77824,
AV_CODEC_ID_RA_288 = 77825,
CODEC_ID_RA_288 = 77825,
AV_CODEC_ID_ROQ_DPCM = 81920,
CODEC_ID_ROQ_DPCM = 81920,
AV_CODEC_ID_INTERPLAY_DPCM = 81921,
CODEC_ID_INTERPLAY_DPCM = 81921,
AV_CODEC_ID_XAN_DPCM = 81922,
CODEC_ID_XAN_DPCM = 81922,
AV_CODEC_ID_SOL_DPCM = 81923,
CODEC_ID_SOL_DPCM = 81923,
AV_CODEC_ID_MP2 = 86016,
CODEC_ID_MP2 = 86016,
AV_CODEC_ID_MP3 = 86017,
CODEC_ID_MP3 = 86017,
AV_CODEC_ID_AAC = 86018,
CODEC_ID_AAC = 86018,
AV_CODEC_ID_AC3 = 86019,
CODEC_ID_AC3 = 86019,
AV_CODEC_ID_DTS = 86020,
CODEC_ID_DTS = 86020,
AV_CODEC_ID_VORBIS = 86021,
CODEC_ID_VORBIS = 86021,
AV_CODEC_ID_DVAUDIO = 86022,
CODEC_ID_DVAUDIO = 86022,
AV_CODEC_ID_WMAV1 = 86023,
CODEC_ID_WMAV1 = 86023,
AV_CODEC_ID_WMAV2 = 86024,
CODEC_ID_WMAV2 = 86024,
AV_CODEC_ID_MACE3 = 86025,
CODEC_ID_MACE3 = 86025,
AV_CODEC_ID_MACE6 = 86026,
CODEC_ID_MACE6 = 86026,
AV_CODEC_ID_VMDAUDIO = 86027,
CODEC_ID_VMDAUDIO = 86027,
AV_CODEC_ID_FLAC = 86028,
CODEC_ID_FLAC = 86028,
AV_CODEC_ID_MP3ADU = 86029,
CODEC_ID_MP3ADU = 86029,
AV_CODEC_ID_MP3ON4 = 86030,
CODEC_ID_MP3ON4 = 86030,
AV_CODEC_ID_SHORTEN = 86031,
CODEC_ID_SHORTEN = 86031,
AV_CODEC_ID_ALAC = 86032,
CODEC_ID_ALAC = 86032,
AV_CODEC_ID_WESTWOOD_SND1 = 86033,
CODEC_ID_WESTWOOD_SND1 = 86033,
AV_CODEC_ID_GSM = 86034,
CODEC_ID_GSM = 86034,
AV_CODEC_ID_QDM2 = 86035,
CODEC_ID_QDM2 = 86035,
AV_CODEC_ID_COOK = 86036,
CODEC_ID_COOK = 86036,
AV_CODEC_ID_TRUESPEECH = 86037,
CODEC_ID_TRUESPEECH = 86037,
AV_CODEC_ID_TTA = 86038,
CODEC_ID_TTA = 86038,
AV_CODEC_ID_SMACKAUDIO = 86039,
CODEC_ID_SMACKAUDIO = 86039,
AV_CODEC_ID_QCELP = 86040,
CODEC_ID_QCELP = 86040,
AV_CODEC_ID_WAVPACK = 86041,
CODEC_ID_WAVPACK = 86041,
AV_CODEC_ID_DSICINAUDIO = 86042,
CODEC_ID_DSICINAUDIO = 86042,
AV_CODEC_ID_IMC = 86043,
CODEC_ID_IMC = 86043,
AV_CODEC_ID_MUSEPACK7 = 86044,
CODEC_ID_MUSEPACK7 = 86044,
AV_CODEC_ID_MLP = 86045,
CODEC_ID_MLP = 86045,
AV_CODEC_ID_GSM_MS = 86046,
CODEC_ID_GSM_MS = 86046,
AV_CODEC_ID_ATRAC3 = 86047,
CODEC_ID_ATRAC3 = 86047,
AV_CODEC_ID_VOXWARE = 86048,
CODEC_ID_VOXWARE = 86048,
AV_CODEC_ID_APE = 86049,
CODEC_ID_APE = 86049,
AV_CODEC_ID_NELLYMOSER = 86050,
CODEC_ID_NELLYMOSER = 86050,
AV_CODEC_ID_MUSEPACK8 = 86051,
CODEC_ID_MUSEPACK8 = 86051,
AV_CODEC_ID_SPEEX = 86052,
CODEC_ID_SPEEX = 86052,
AV_CODEC_ID_WMAVOICE = 86053,
CODEC_ID_WMAVOICE = 86053,
AV_CODEC_ID_WMAPRO = 86054,
CODEC_ID_WMAPRO = 86054,
AV_CODEC_ID_WMALOSSLESS = 86055,
CODEC_ID_WMALOSSLESS = 86055,
AV_CODEC_ID_ATRAC3P = 86056,
CODEC_ID_ATRAC3P = 86056,
AV_CODEC_ID_EAC3 = 86057,
CODEC_ID_EAC3 = 86057,
AV_CODEC_ID_SIPR = 86058,
CODEC_ID_SIPR = 86058,
AV_CODEC_ID_MP1 = 86059,
CODEC_ID_MP1 = 86059,
AV_CODEC_ID_TWINVQ = 86060,
CODEC_ID_TWINVQ = 86060,
AV_CODEC_ID_TRUEHD = 86061,
CODEC_ID_TRUEHD = 86061,
AV_CODEC_ID_MP4ALS = 86062,
CODEC_ID_MP4ALS = 86062,
AV_CODEC_ID_ATRAC1 = 86063,
CODEC_ID_ATRAC1 = 86063,
AV_CODEC_ID_BINKAUDIO_RDFT = 86064,
CODEC_ID_BINKAUDIO_RDFT = 86064,
AV_CODEC_ID_BINKAUDIO_DCT = 86065,
CODEC_ID_BINKAUDIO_DCT = 86065,
AV_CODEC_ID_AAC_LATM = 86066,
CODEC_ID_AAC_LATM = 86066,
AV_CODEC_ID_QDMC = 86067,
CODEC_ID_QDMC = 86067,
AV_CODEC_ID_CELT = 86068,
CODEC_ID_CELT = 86068,
AV_CODEC_ID_G723_1 = 86069,
CODEC_ID_G723_1 = 86069,
AV_CODEC_ID_G729 = 86070,
CODEC_ID_G729 = 86070,
AV_CODEC_ID_8SVX_EXP = 86071,
CODEC_ID_8SVX_EXP = 86071,
AV_CODEC_ID_8SVX_FIB = 86072,
CODEC_ID_8SVX_FIB = 86072,
AV_CODEC_ID_BMV_AUDIO = 86073,
CODEC_ID_BMV_AUDIO = 86073,
AV_CODEC_ID_RALF = 86074,
CODEC_ID_RALF = 86074,
AV_CODEC_ID_IAC = 86075,
CODEC_ID_IAC = 86075,
AV_CODEC_ID_ILBC = 86076,
CODEC_ID_ILBC = 86076,
AV_CODEC_ID_OPUS_DEPRECATED = 86077,
AV_CODEC_ID_COMFORT_NOISE = 86078,
AV_CODEC_ID_TAK_DEPRECATED = 86079,
AV_CODEC_ID_METASOUND = 86080,
AV_CODEC_ID_PAF_AUDIO_DEPRECATED = 86081,
AV_CODEC_ID_ON2AVC = 86082,
AV_CODEC_ID_DVD_SUBTITLE = 94208,
AV_CODEC_ID_FIRST_SUBTITLE = 94208,
CODEC_ID_DVD_SUBTITLE = 94208,
CODEC_ID_FIRST_SUBTITLE = 94208,
AV_CODEC_ID_DVB_SUBTITLE = 94209,
CODEC_ID_DVB_SUBTITLE = 94209,
AV_CODEC_ID_TEXT = 94210,
CODEC_ID_TEXT = 94210,
AV_CODEC_ID_XSUB = 94211,
CODEC_ID_XSUB = 94211,
AV_CODEC_ID_SSA = 94212,
CODEC_ID_SSA = 94212,
AV_CODEC_ID_MOV_TEXT = 94213,
CODEC_ID_MOV_TEXT = 94213,
AV_CODEC_ID_HDMV_PGS_SUBTITLE = 94214,
CODEC_ID_HDMV_PGS_SUBTITLE = 94214,
AV_CODEC_ID_DVB_TELETEXT = 94215,
CODEC_ID_DVB_TELETEXT = 94215,
AV_CODEC_ID_SRT = 94216,
CODEC_ID_SRT = 94216,
AV_CODEC_ID_FIRST_UNKNOWN = 98304,
AV_CODEC_ID_TTF = 98304,
CODEC_ID_FIRST_UNKNOWN = 98304,
CODEC_ID_TTF = 98304,
AV_CODEC_ID_PROBE = 102400,
CODEC_ID_PROBE = 102400,
AV_CODEC_ID_MPEG2TS = 131072,
CODEC_ID_MPEG2TS = 131072,
AV_CODEC_ID_MPEG4SYSTEMS = 131073,
CODEC_ID_MPEG4SYSTEMS = 131073,
AV_CODEC_ID_FFMETADATA = 135168,
CODEC_ID_FFMETADATA = 135168,
AV_CODEC_ID_G2M = 4665933,
CODEC_ID_G2M = 4665933,
AV_CODEC_ID_IDF = 4801606,
CODEC_ID_IDF = 4801606,
AV_CODEC_ID_OTF = 5198918,
CODEC_ID_OTF = 5198918,
AV_CODEC_ID_PCM_S24LE_PLANAR = 407917392,
AV_CODEC_ID_PCM_S32LE_PLANAR = 542135120,
AV_CODEC_ID_012V = 808530518,
AV_CODEC_ID_EXR = 809850962,
CODEC_ID_EXR = 809850962,
AV_CODEC_ID_ADPCM_G726LE = 909260615,
AV_CODEC_ID_ADPCM_AFC = 1095123744,
AV_CODEC_ID_APNG = 1095781959,
AV_CODEC_ID_ASS = 1095979808,
AV_CODEC_ID_AVRP = 1096176208,
CODEC_ID_AVRP = 1096176208,
AV_CODEC_ID_AVRN = 1096176238,
AV_CODEC_ID_AVUI = 1096176969,
CODEC_ID_AVUI = 1096176969,
AV_CODEC_ID_AYUV = 1096373590,
CODEC_ID_AYUV = 1096373590,
AV_CODEC_ID_BRENDER_PIX = 1112557912,
AV_CODEC_ID_BINTEXT = 1112823892,
CODEC_ID_BINTEXT = 1112823892,
AV_CODEC_ID_CPIA = 1129335105,
AV_CODEC_ID_BIN_DATA = 1145132097,
AV_CODEC_ID_DVD_NAV = 1145979222,
AV_CODEC_ID_DSD_LSBF_PLANAR = 1146307633,
AV_CODEC_ID_DSD_MSBF_PLANAR = 1146307640,
AV_CODEC_ID_DSD_LSBF = 1146307660,
AV_CODEC_ID_DSD_MSBF = 1146307661,
AV_CODEC_ID_ADPCM_DTK = 1146374944,
AV_CODEC_ID_ESCAPE130 = 1160852272,
CODEC_ID_ESCAPE130 = 1160852272,
AV_CODEC_ID_FFWAVESYNTH = 1179014995,
CODEC_ID_FFWAVESYNTH = 1179014995,
AV_CODEC_ID_HEVC = 1211250229,
AV_CODEC_ID_JACOSUB = 1246975298,
CODEC_ID_JACOSUB = 1246975298,
AV_CODEC_ID_SMPTE_KLV = 1263294017,
AV_CODEC_ID_MPL2 = 1297108018,
AV_CODEC_ID_MVC1 = 1297498929,
AV_CODEC_ID_MVC2 = 1297498930,
AV_CODEC_ID_ADPCM_IMA_OKI = 1330333984,
AV_CODEC_ID_OPUS = 1330664787,
CODEC_ID_OPUS = 1330664787,
AV_CODEC_ID_PAF_AUDIO = 1346455105,
CODEC_ID_PAF_AUDIO = 1346455105,
AV_CODEC_ID_PAF_VIDEO = 1346455126,
CODEC_ID_PAF_VIDEO = 1346455126,
AV_CODEC_ID_PCM_S16BE_PLANAR = 1347637264,
AV_CODEC_ID_PJS = 1349012051,
AV_CODEC_ID_ADPCM_IMA_RAD = 1380008992,
AV_CODEC_ID_REALTEXT = 1381259348,
CODEC_ID_REALTEXT = 1381259348,
AV_CODEC_ID_SAMI = 1396788553,
CODEC_ID_SAMI = 1396788553,
AV_CODEC_ID_SANM = 1396788813,
CODEC_ID_SANM = 1396788813,
AV_CODEC_ID_SGIRLE = 1397180754,
AV_CODEC_ID_SMVJPEG = 1397577290,
AV_CODEC_ID_SNOW = 1397641047,
CODEC_ID_SNOW = 1397641047,
AV_CODEC_ID_SONIC = 1397706307,
CODEC_ID_SONIC = 1397706307,
AV_CODEC_ID_SONIC_LS = 1397706316,
CODEC_ID_SONIC_LS = 1397706316,
AV_CODEC_ID_SUBRIP = 1397909872,
AV_CODEC_ID_SUBVIEWER1 = 1398953521,
AV_CODEC_ID_STL = 1399870540,
AV_CODEC_ID_SUBVIEWER = 1400201814,
CODEC_ID_SUBVIEWER = 1400201814,
AV_CODEC_ID_TARGA_Y216 = 1412575542,
AV_CODEC_ID_TIMED_ID3 = 1414087731,
AV_CODEC_ID_V308 = 1446195256,
CODEC_ID_V308 = 1446195256,
AV_CODEC_ID_V408 = 1446260792,
CODEC_ID_V408 = 1446260792,
AV_CODEC_ID_ADPCM_VIMA = 1447644481,
AV_CODEC_ID_VIMA = 1447644481,
CODEC_ID_VIMA = 1447644481,
AV_CODEC_ID_VP7 = 1448097584,
AV_CODEC_ID_VPLAYER = 1448111218,
AV_CODEC_ID_WEBP = 1464156752,
AV_CODEC_ID_WEBVTT = 1465275476,
AV_CODEC_ID_XBIN = 1480739150,
CODEC_ID_XBIN = 1480739150,
AV_CODEC_ID_XFACE = 1480999235,
AV_CODEC_ID_Y41P = 1496592720,
CODEC_ID_Y41P = 1496592720,
AV_CODEC_ID_YUV4 = 1498764852,
CODEC_ID_YUV4 = 1498764852,
AV_CODEC_ID_EIA_608 = 1664495672,
CODEC_ID_EIA_608 = 1664495672,
AV_CODEC_ID_MICRODVD = 1833195076,
CODEC_ID_MICRODVD = 1833195076,
AV_CODEC_ID_EVRC = 1936029283,
AV_CODEC_ID_SMV = 1936944502,
AV_CODEC_ID_TAK = 1950507339
} AVCodecID;
typedef struct AVIOContext AVIOContext;
typedef struct AVStream AVStream;
typedef struct AVProgram AVProgram;
typedef struct AVChapter AVChapter;
typedef struct AVDictionary AVDictionary;
typedef struct AVIOInterruptCB AVIOInterruptCB;
typedef enum AVDurationEstimationMethod
{
AVFMT_DURATION_FROM_PTS = 0,
AVFMT_DURATION_FROM_STREAM = 1,
AVFMT_DURATION_FROM_BITRATE = 2
} AVDurationEstimationMethod;
typedef struct AVPacketList AVPacketList;
typedef struct AVRational AVRational;
typedef struct AVFormatInternal AVFormatInternal;
typedef struct AVCodec AVCodec;
typedef struct AVSubtitle AVSubtitle;
typedef enum AUDIO_PB_STATE
{
AUDIO_PB_IDLE = 0,
AUDIO_PB_PREPARING = 1,
AUDIO_PB_PLAYING = 2,
AUDIO_PB_PAUSED = 3,
AUDIO_PB_EOF = 4,
AUDIO_PB_ERROR = 5,
AUDIO_PB_CLOSED = 6
} AUDIO_PB_STATE;
typedef enum AUDIO_PB_STATE audio_pb_state_t;
typedef struct pl_decoder_t pl_decoder_t;
typedef struct pl_decoder_t *pl_decoder_handle_t;
typedef struct pl_decoder_itf_t pl_decoder_itf_t;
typedef int (*input_data_cb)(void *, void *, int, int);
typedef int (*output_data_cb)(void *, void *, int, int);
typedef struct audio_resampler_t audio_resampler_t;
typedef struct audio_resampler_t *audio_resampler_ptr;
typedef enum VDEC_STATE
{
VDEC_IDLE = 0,
VDEC_PREPARING = 1,
VDEC_PLAYING = 2,
VDEC_PAUSED = 3,
VDEC_EOF = 4,
VDEC_ERROR = 5,
VDEC_CLOSED = 6
} VDEC_STATE;
typedef enum VDEC_STATE vdec_state_t;
typedef enum DUSS_FILE_FORMAT
{
DUSS_FILE_FORMAT_NULL = 0,
DUSS_FILE_FORMAT_H264RAW = 1,
DUSS_FILE_FORMAT_H265RAW = 2,
DUSS_FILE_FORMAT_AACRAW = 3,
DUSS_FILE_FORMAT_AACADTSRAW = 4,
DUSS_FILE_FORMAT_MJPGRAW = 5,
DUSS_FILE_FORMAT_PRORESRAW = 6,
DUSS_FILE_FORMAT_PRORESRAWRAW = 7,
DUSS_FILE_FORMAT_USERDATARAW = 8,
DUSS_FILE_FORMAT_MPEG4RAW = 9,
DUSS_FILE_FORMAT_RAWMAX = 10,
DUSS_FILE_FORMAT_MP4 = 11,
DUSS_FILE_FORMAT_MOV = 12,
DUSS_FILE_FORMAT_MP2TS = 13,
DUSS_FILE_FORMAT_MAX = 14,
DUSS_FILE_FORMAT_NOT_SUPPORTED_YET = 14
} DUSS_FILE_FORMAT;
typedef int (*gs_consumer_prepare_t)(void *);
typedef struct bridge_io_pkt bridge_io_pkt;
typedef struct bridge_io_pkt bridge_io_pkt_t;
typedef int (*gs_consumer_finish_t)(void *);
typedef struct duss_hal_mem_config_t duss_hal_mem_config_t;
typedef struct duss_hal_mem_param_t duss_hal_mem_param_t;
typedef struct duss_hal_mem_buf duss_hal_mem_buf;
typedef struct duss_hal_mem_buf *duss_hal_mem_handle_t;
typedef enum AVMediaType
{
AVMEDIA_TYPE_UNKNOWN = -1,
AVMEDIA_TYPE_VIDEO = 0,
AVMEDIA_TYPE_AUDIO = 1,
AVMEDIA_TYPE_DATA = 2,
AVMEDIA_TYPE_SUBTITLE = 3,
AVMEDIA_TYPE_ATTACHMENT = 4,
AVMEDIA_TYPE_NB = 5
} AVMediaType;
typedef struct AVCodecInternal AVCodecInternal;
typedef enum AVPictureType
{
AV_PICTURE_TYPE_NONE = 0,
AV_PICTURE_TYPE_I = 1,
AV_PICTURE_TYPE_P = 2,
AV_PICTURE_TYPE_B = 3,
AV_PICTURE_TYPE_S = 4,
AV_PICTURE_TYPE_SI = 5,
AV_PICTURE_TYPE_SP = 6,
AV_PICTURE_TYPE_BI = 7
} AVPictureType;
typedef struct AVPanScan AVPanScan;
typedef struct AVBufferRef AVBufferRef;
typedef struct AVFrameSideData AVFrameSideData;
typedef enum AVColorRange
{
AVCOL_RANGE_UNSPECIFIED = 0,
AVCOL_RANGE_MPEG = 1,
AVCOL_RANGE_JPEG = 2,
AVCOL_RANGE_NB = 3
} AVColorRange;
typedef enum AVColorPrimaries
{
AVCOL_PRI_RESERVED0 = 0,
AVCOL_PRI_BT709 = 1,
AVCOL_PRI_UNSPECIFIED = 2,
AVCOL_PRI_RESERVED = 3,
AVCOL_PRI_BT470M = 4,
AVCOL_PRI_BT470BG = 5,
AVCOL_PRI_SMPTE170M = 6,
AVCOL_PRI_SMPTE240M = 7,
AVCOL_PRI_FILM = 8,
AVCOL_PRI_BT2020 = 9,
AVCOL_PRI_NB = 10
} AVColorPrimaries;
typedef enum AVColorTransferCharacteristic
{
AVCOL_TRC_RESERVED0 = 0,
AVCOL_TRC_BT709 = 1,
AVCOL_TRC_UNSPECIFIED = 2,
AVCOL_TRC_RESERVED = 3,
AVCOL_TRC_GAMMA22 = 4,
AVCOL_TRC_GAMMA28 = 5,
AVCOL_TRC_SMPTE170M = 6,
AVCOL_TRC_SMPTE240M = 7,
AVCOL_TRC_LINEAR = 8,
AVCOL_TRC_LOG = 9,
AVCOL_TRC_LOG_SQRT = 10,
AVCOL_TRC_IEC61966_2_4 = 11,
AVCOL_TRC_BT1361_ECG = 12,
AVCOL_TRC_IEC61966_2_1 = 13,
AVCOL_TRC_BT2020_10 = 14,
AVCOL_TRC_BT2020_12 = 15,
AVCOL_TRC_NB = 16
} AVColorTransferCharacteristic;
typedef enum AVColorSpace
{
AVCOL_SPC_RGB = 0,
AVCOL_SPC_BT709 = 1,
AVCOL_SPC_UNSPECIFIED = 2,
AVCOL_SPC_RESERVED = 3,
AVCOL_SPC_FCC = 4,
AVCOL_SPC_BT470BG = 5,
AVCOL_SPC_SMPTE170M = 6,
AVCOL_SPC_SMPTE240M = 7,
AVCOL_SPC_YCOCG = 8,
AVCOL_SPC_BT2020_NCL = 9,
AVCOL_SPC_BT2020_CL = 10,
AVCOL_SPC_NB = 11
} AVColorSpace;
typedef enum AVChromaLocation
{
AVCHROMA_LOC_UNSPECIFIED = 0,
AVCHROMA_LOC_LEFT = 1,
AVCHROMA_LOC_CENTER = 2,
AVCHROMA_LOC_TOPLEFT = 3,
AVCHROMA_LOC_TOP = 4,
AVCHROMA_LOC_BOTTOMLEFT = 5,
AVCHROMA_LOC_BOTTOM = 6,
AVCHROMA_LOC_NB = 7
} AVChromaLocation;
typedef enum AVFieldOrder
{
AV_FIELD_UNKNOWN = 0,
AV_FIELD_PROGRESSIVE = 1,
AV_FIELD_TT = 2,
AV_FIELD_BB = 3,
AV_FIELD_TB = 4,
AV_FIELD_BT = 5
} AVFieldOrder;
typedef enum AVSampleFormat
{
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8 = 0,
AV_SAMPLE_FMT_S16 = 1,
AV_SAMPLE_FMT_S32 = 2,
AV_SAMPLE_FMT_FLT = 3,
AV_SAMPLE_FMT_DBL = 4,
AV_SAMPLE_FMT_U8P = 5,
AV_SAMPLE_FMT_S16P = 6,
AV_SAMPLE_FMT_S32P = 7,
AV_SAMPLE_FMT_FLTP = 8,
AV_SAMPLE_FMT_DBLP = 9,
AV_SAMPLE_FMT_NB = 10
} AVSampleFormat;
typedef enum AVAudioServiceType
{
AV_AUDIO_SERVICE_TYPE_MAIN = 0,
AV_AUDIO_SERVICE_TYPE_EFFECTS = 1,
AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2,
AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED = 3,
AV_AUDIO_SERVICE_TYPE_DIALOGUE = 4,
AV_AUDIO_SERVICE_TYPE_COMMENTARY = 5,
AV_AUDIO_SERVICE_TYPE_EMERGENCY = 6,
AV_AUDIO_SERVICE_TYPE_VOICE_OVER = 7,
AV_AUDIO_SERVICE_TYPE_KARAOKE = 8,
AV_AUDIO_SERVICE_TYPE_NB = 9
} AVAudioServiceType;
typedef struct RcOverride RcOverride;
typedef struct AVHWAccel AVHWAccel;
typedef struct MpegEncContext MpegEncContext;
typedef enum AVDiscard
{
AVDISCARD_NONE = -16,
AVDISCARD_DEFAULT = 0,
AVDISCARD_NONREF = 8,
AVDISCARD_BIDIR = 16,
AVDISCARD_NONINTRA = 24,
AVDISCARD_NONKEY = 32,
AVDISCARD_ALL = 48
} AVDiscard;
typedef struct AVCodecDescriptor AVCodecDescriptor;
typedef struct pl_muxer_itf_t pl_muxer_itf_t;
typedef enum pl_muxer_config_index_t
{
PL_MUXER_CONFIG_INDEX_NONE = 0,
PL_MUXER_CONFIG_INDEX_EXTRADATA = 1,
PL_MUXER_CONFIG_INDEX_FRAGMENTED_MP4 = 2
} pl_muxer_config_index_t;
typedef struct __sbuf __sbuf;
typedef enum pcm_format
{
PCM_FORMAT_INVALID = -1,
PCM_FORMAT_S16_LE = 0,
PCM_FORMAT_S32_LE = 1,
PCM_FORMAT_S8 = 2,
PCM_FORMAT_S24_LE = 3,
PCM_FORMAT_S24_3LE = 4,
PCM_FORMAT_MAX = 5
} pcm_format;
typedef enum pcm_tstamp
{
NONE_TSTAMP = 0,
ALSA_TSTAMP = 1,
ALSA_TSTAMP_SYNC = 2,
TSTAMP_TYPE_MAX = 3
} pcm_tstamp;
typedef struct snd_pcm_mmap_status snd_pcm_mmap_status;
typedef struct snd_pcm_mmap_control snd_pcm_mmap_control;
typedef struct snd_pcm_sync_ptr snd_pcm_sync_ptr;
typedef union anon_union_conflict2c9b_for_field_3 anon_union_conflict2c9b_for_field_3;
typedef struct stick_mode_self_define_t stick_mode_self_define_t;
typedef struct loc_channel_addr loc_channel_addr;
typedef struct ip_channel_addr ip_channel_addr;
typedef struct wl_channel_addr wl_channel_addr;
typedef struct uart_channel_addr uart_channel_addr;
typedef struct can_channel_addr can_channel_addr;
typedef struct i2c_channel_addr i2c_channel_addr;
typedef struct spi_channel_addr spi_channel_addr;
typedef struct hpi_channel_addr hpi_channel_addr;
typedef struct usb_channel_addr usb_channel_addr;
typedef struct usbacc_channel_addr usbacc_channel_addr;
typedef struct icc_channel_addr icc_channel_addr;
typedef struct netlink_channel_addr netlink_channel_addr;
typedef struct bulk_channel_addr bulk_channel_addr;
typedef enum duss_storage_client_type_t
{
DUSS_STORAGE_EMMC0 = 0,
DUSS_STORAGE_SDCARD0 = 1,
DUSS_STORAGE_SDCARD1 = 2,
DUSS_STORAGE_UDISK = 3
} duss_storage_client_type_t;
typedef struct anon_struct_conflict2349 anon_struct_conflict2349;
typedef struct anon_struct_conflict242a anon_struct_conflict242a;
typedef struct gs_local_video_info gs_local_video_info;
typedef struct gs_local_video_info gs_local_video_info_t;
typedef struct gs_local_panorama_info gs_local_panorama_info;
typedef struct gs_local_panorama_info gs_local_panorama_info_t;
typedef struct AVOption AVOption;
typedef struct AVOptionRange AVOptionRange;
typedef struct AVCodecTag AVCodecTag;
typedef struct AVPacketSideData AVPacketSideData;
typedef struct AVFrac AVFrac;
typedef struct anon_struct_conflict69b3c_for_info anon_struct_conflict69b3c_for_info;
typedef enum AVStreamParseType
{
AVSTREAM_PARSE_NONE = 0,
AVSTREAM_PARSE_FULL = 1,
AVSTREAM_PARSE_HEADERS = 2,
AVSTREAM_PARSE_TIMESTAMPS = 3,
AVSTREAM_PARSE_FULL_ONCE = 4,
AVSTREAM_PARSE_FULL_RAW = 1463898624
} AVStreamParseType;
typedef struct AVCodecParserContext AVCodecParserContext;
typedef struct AVIndexEntry AVIndexEntry;
typedef struct AVProfile AVProfile;
typedef struct AVCodecDefault AVCodecDefault;
typedef struct AVSubtitleRect AVSubtitleRect;
typedef struct SwrContext SwrContext;
typedef struct AVBuffer AVBuffer;
typedef enum AVFrameSideDataType
{
AV_FRAME_DATA_PANSCAN = 0,
AV_FRAME_DATA_A53_CC = 1,
AV_FRAME_DATA_STEREO3D = 2,
AV_FRAME_DATA_MATRIXENCODING = 3,
AV_FRAME_DATA_DOWNMIX_INFO = 4,
AV_FRAME_DATA_REPLAYGAIN = 5,
AV_FRAME_DATA_DISPLAYMATRIX = 6,
AV_FRAME_DATA_AFD = 7,
AV_FRAME_DATA_MOTION_VECTORS = 8,
AV_FRAME_DATA_SKIP_SAMPLES = 9
} AVFrameSideDataType;
typedef struct _DUSS_MSG_APERTURE_t _DUSS_MSG_APERTURE_t;
typedef struct _DUSS_MSG_APERTURE_t DUSS_MSG_APERTURE_t;
typedef struct _DUSS_MSG_SHUTTER_SPEED_t _DUSS_MSG_SHUTTER_SPEED_t;
typedef struct _DUSS_MSG_SHUTTER_SPEED_t DUSS_MSG_SHUTTER_SPEED_t;
typedef struct _DUSS_MSG_ISO_t _DUSS_MSG_ISO_t;
typedef struct _DUSS_MSG_ISO_t DUSS_MSG_ISO_t;
typedef struct _DUSS_MSG_EXPO_MODE_t _DUSS_MSG_EXPO_MODE_t;
typedef struct _DUSS_MSG_EXPO_MODE_t DUSS_MSG_EXPO_MODE_t;
typedef struct _DUSS_MSG_EV_BIAS_t _DUSS_MSG_EV_BIAS_t;
typedef struct _DUSS_MSG_EV_BIAS_t DUSS_MSG_EV_BIAS_t;
typedef struct _DUSS_MSG_P_STORAGE_FMT_t _DUSS_MSG_P_STORAGE_FMT_t;
typedef struct _DUSS_MSG_P_STORAGE_FMT_t DUSS_MSG_P_STORAGE_FMT_t;
typedef struct _DUSS_MSG_WB_t _DUSS_MSG_WB_t;
typedef struct _DUSS_MSG_WB_t DUSS_MSG_WB_t;
typedef struct _DUSS_MSG_SCENE_MODE_t _DUSS_MSG_SCENE_MODE_t;
typedef struct _DUSS_MSG_SCENE_MODE_t DUSS_MSG_SCENE_MODE_t;
typedef struct _DUSS_MSG_DIGITAL_EFFECT_t _DUSS_MSG_DIGITAL_EFFECT_t;
typedef struct _DUSS_MSG_DIGITAL_EFFECT_t DUSS_MSG_DIGITAL_EFFECT_t;
typedef struct _DUSS_MSG_P_SIZE_t _DUSS_MSG_P_SIZE_t;
typedef struct _DUSS_MSG_P_SIZE_t DUSS_MSG_P_SIZE_t;
typedef struct _DUSS_MSG_SET_FOCUS_REGION_t _DUSS_MSG_SET_FOCUS_REGION_t;
typedef struct _DUSS_MSG_SET_FOCUS_REGION_t DUSS_MSG_SET_FOCUS_REGION_t;
typedef struct _DUSS_MSG_CAPTURE_MODE_t _DUSS_MSG_CAPTURE_MODE_t;
typedef struct _DUSS_MSG_CAPTURE_MODE_t DUSS_MSG_CAPTURE_MODE_t;
typedef struct _DUSS_MSG_CONTICAP_PARAM_t _DUSS_MSG_CONTICAP_PARAM_t;
typedef struct _DUSS_MSG_CONTICAP_PARAM_t DUSS_MSG_CONTICAP_PARAM_t;
typedef struct _DUSS_MSG_V_STORAGE_FMT_t _DUSS_MSG_V_STORAGE_FMT_t;
typedef struct _DUSS_MSG_V_STORAGE_FMT_t DUSS_MSG_V_STORAGE_FMT_t;
typedef struct _DUSS_MSG_V_FORMAT_t _DUSS_MSG_V_FORMAT_t;
typedef struct _DUSS_MSG_V_FORMAT_t DUSS_MSG_V_FORMAT_t;
typedef struct _DUSS_MSG_VIDEO_STANDARD_t _DUSS_MSG_VIDEO_STANDARD_t;
typedef struct _DUSS_MSG_VIDEO_STANDARD_t DUSS_MSG_VIDEO_STANDARD_t;
typedef struct _DUSS_MSG_WORKMODE_t _DUSS_MSG_WORKMODE_t;
typedef struct _DUSS_MSG_WORKMODE_t DUSS_MSG_WORKMODE_t;
typedef struct _DUSS_MSG_SINGLE_PLAY_CRTL_t _DUSS_MSG_SINGLE_PLAY_CRTL_t;
typedef struct _DUSS_MSG_SINGLE_PLAY_CRTL_t DUSS_MSG_SINGLE_PLAY_CRTL_t;
typedef struct _DUSS_MSG_V_CRTL_t _DUSS_MSG_V_CRTL_t;
typedef struct _DUSS_MSG_V_CRTL_t DUSS_MSG_V_CRTL_t;
typedef struct _DUSS_MSG_SD_INFO_t _DUSS_MSG_SD_INFO_t;
typedef struct _DUSS_MSG_SD_INFO_t DUSS_MSG_SD_INFO_t;
typedef struct _DUSS_MSG_ZOOM_PARAM_t _DUSS_MSG_ZOOM_PARAM_t;
typedef struct _DUSS_MSG_ZOOM_PARAM_t DUSS_MSG_ZOOM_PARAM_t;
typedef struct _DUSS_MSG_FLIP _DUSS_MSG_FLIP;
typedef struct _DUSS_MSG_FLIP DUSS_MSG_FLIP_t;
typedef struct _DUSS_MSG_SHARPNESS_t _DUSS_MSG_SHARPNESS_t;
typedef struct _DUSS_MSG_SHARPNESS_t DUSS_MSG_SHARPNESS_t;
typedef struct _DUSS_MSG_CONTRAST_t _DUSS_MSG_CONTRAST_t;
typedef struct _DUSS_MSG_CONTRAST_t DUSS_MSG_CONTRAST_t;
typedef struct _DUSS_MSG_SATURATION_t _DUSS_MSG_SATURATION_t;
typedef struct _DUSS_MSG_SATURATION_t DUSS_MSG_SATURATION_t;
typedef struct _DUSS_MSG_CAPTURE_t _DUSS_MSG_CAPTURE_t;
typedef struct _DUSS_MSG_CAPTURE_t DUSS_MSG_CAPTURE_t;
typedef struct _DUSS_MSG_RECORD_t _DUSS_MSG_RECORD_t;
typedef struct _DUSS_MSG_RECORD_t DUSS_MSG_RECORD_t;
typedef struct _UAV_RECORD_MODE_t _UAV_RECORD_MODE_t;
typedef struct _UAV_RECORD_MODE_t UAV_RECORD_MODE_t;
typedef uint8_t duss_wl_prot_type_t;
typedef uint8_t duss_wl_prio_t;
typedef struct duss_hal_uart_config_t duss_hal_uart_config_t;
typedef struct duss_hal_can_config_t duss_hal_can_config_t;
typedef struct duss_hal_i2c_config_t duss_hal_i2c_config_t;
typedef struct duss_hal_spi_config_t duss_hal_spi_config_t;
typedef struct hpi_channel hpi_channel;
typedef struct hpi_channel duss_hal_hpi_config_t;
typedef struct duss_hal_usbacc_config_t duss_hal_usbacc_config_t;
typedef struct duss_hal_icc_config_t duss_hal_icc_config_t;
typedef struct duss_hal_bulk_config_t duss_hal_bulk_config_t;
typedef enum AVOptionType
{
AV_OPT_TYPE_FLAGS = 0,
FF_OPT_TYPE_FLAGS = 0,
AV_OPT_TYPE_INT = 1,
FF_OPT_TYPE_INT = 1,
AV_OPT_TYPE_INT64 = 2,
FF_OPT_TYPE_INT64 = 2,
AV_OPT_TYPE_DOUBLE = 3,
FF_OPT_TYPE_DOUBLE = 3,
AV_OPT_TYPE_FLOAT = 4,
FF_OPT_TYPE_FLOAT = 4,
AV_OPT_TYPE_STRING = 5,
FF_OPT_TYPE_STRING = 5,
AV_OPT_TYPE_RATIONAL = 6,
FF_OPT_TYPE_RATIONAL = 6,
AV_OPT_TYPE_BINARY = 7,
FF_OPT_TYPE_BINARY = 7,
AV_OPT_TYPE_DICT = 8,
AV_OPT_TYPE_CONST = 128,
FF_OPT_TYPE_CONST = 128,
AV_OPT_TYPE_CHANNEL_LAYOUT = 1128811585,
AV_OPT_TYPE_COLOR = 1129270354,
AV_OPT_TYPE_DURATION = 1146442272,
AV_OPT_TYPE_PIXEL_FMT = 1346784596,
AV_OPT_TYPE_SAMPLE_FMT = 1397116244,
AV_OPT_TYPE_IMAGE_SIZE = 1397316165,
AV_OPT_TYPE_VIDEO_RATE = 1448231252
} AVOptionType;
typedef union anon_union_conflictac8f4_for_default_val anon_union_conflictac8f4_for_default_val;
typedef enum AVPacketSideDataType
{
AV_PKT_DATA_PALETTE = 0,
AV_PKT_DATA_NEW_EXTRADATA = 1,
AV_PKT_DATA_PARAM_CHANGE = 2,
AV_PKT_DATA_H263_MB_INFO = 3,
AV_PKT_DATA_REPLAYGAIN = 4,
AV_PKT_DATA_DISPLAYMATRIX = 5,
AV_PKT_DATA_STEREO3D = 6,
AV_PKT_DATA_SKIP_SAMPLES = 70,
AV_PKT_DATA_JP_DUALMONO = 71,
AV_PKT_DATA_STRINGS_METADATA = 72,
AV_PKT_DATA_SUBTITLE_POSITION = 73,
AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL = 74,
AV_PKT_DATA_WEBVTT_IDENTIFIER = 75,
AV_PKT_DATA_WEBVTT_SETTINGS = 76,
AV_PKT_DATA_METADATA_UPDATE = 77
} AVPacketSideDataType;
typedef struct AVCodecParser AVCodecParser;
typedef enum AVPictureStructure
{
AV_PICTURE_STRUCTURE_UNKNOWN = 0,
AV_PICTURE_STRUCTURE_TOP_FIELD = 1,
AV_PICTURE_STRUCTURE_BOTTOM_FIELD = 2,
AV_PICTURE_STRUCTURE_FRAME = 3
} AVPictureStructure;
typedef struct AVPicture AVPicture;
typedef enum AVSubtitleType
{
SUBTITLE_NONE = 0,
SUBTITLE_BITMAP = 1,
SUBTITLE_TEXT = 2,
SUBTITLE_ASS = 3
} AVSubtitleType;
typedef struct _DUSS_MSG_SHUTTER_A_t _DUSS_MSG_SHUTTER_A_t;
typedef struct _DUSS_MSG_SHUTTER_A_t DUSS_MSG_SHUTTER_A_t;
typedef struct _DUSS_MSG_V_SS_CONFIG_t _DUSS_MSG_V_SS_CONFIG_t;
typedef struct _DUSS_MSG_V_SS_CONFIG_t DUSS_MSG_V_SS_CONFIG_t;
typedef struct _DUSS_MSG_SD_STATUS_t _DUSS_MSG_SD_STATUS_t;
typedef struct _DUSS_MSG_SD_STATUS_t DUSS_MSG_SD_STATUS_t;
typedef struct _DUSS_MSG_ZOOM_CTRL_MODE_t_ _DUSS_MSG_ZOOM_CTRL_MODE_t_;
typedef struct _DUSS_MSG_ZOOM_CTRL_MODE_t_ DUSS_MSG_ZOOM_CTRL_MODE_t;
typedef union anon_union_conflict21a2_for_OZoomRaUnion anon_union_conflict21a2_for_OZoomRaUnion;
typedef uint8_t duss_uart_parity_t;
typedef uint8_t duss_uart_stopbit_t;
typedef uint8_t duss_uart_wordlen_t;
typedef uint8_t duss_mb_can_chip_t;
typedef uint16_t duss_mb_host_id_t;
typedef uint8_t duss_i2c_addr_len_t;
typedef uint __u32;
typedef uchar __u8;
typedef ushort __u16;
typedef uint16_t duss_usbacc_port_t;
typedef enum duss_hal_icc_role_t
{
DUSS_HAL_ICC_ROLE_SOURCE = 0,
DUSS_HAL_ICC_ROLE_TARGET = 1,
DUSS_HAL_ICC_ROLE_MAX = 2
} duss_hal_icc_role_t;
typedef enum duss_hal_icc_shm_type_t
{
DUSS_HAL_ICC_SHM_SRAM = 0,
DUSS_HAL_ICC_SHM_DDR = 1,
DUSS_HAL_ICC_SHM_MAX = 2
} duss_hal_icc_shm_type_t;
typedef struct _DUSS_MSG_ZOOM_S_t _DUSS_MSG_ZOOM_S_t;
typedef struct _DUSS_MSG_ZOOM_S_t DUSS_MSG_ZOOM_S_t;
typedef struct _DUSS_MSG_ZOOM_P_t _DUSS_MSG_ZOOM_P_t;
typedef struct _DUSS_MSG_ZOOM_P_t DUSS_MSG_ZOOM_P_t;
typedef struct _DUSS_MSG_ZOOM_C_t _DUSS_MSG_ZOOM_C_t;
typedef struct _DUSS_MSG_ZOOM_C_t DUSS_MSG_ZOOM_C_t;
struct _DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t
{
uint16_t freq_idx;
uint8_t occupied_ind;
uint8_t work_ind;
uint32_t chn_ipsd;
};
struct __gs_avin_test_ctrl
{
struct duss_osal_task_handle_t *avin_test_task;
_Bool avin_test_task_quit;
_Bool avin_test_flag;
_Bool init;
undefined field4_0x7;
};
struct gs_lv_csm
{
char *name;
_Bool high_prior;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
void *ctx;
gs_consumer_prepare_t prepare;
int (*consume)(void *, gs_lv_pkt_t *);
gs_consumer_finish_t finish;
};
struct gs_usb_gadget_vt
{
int fdin;
int fdout;
gs_usb_listener_t *usb_listener;
struct duss_osal_task_handle_t *usb_out_task;
_Bool usb_task_quit;
_Bool is_wl_chnl_orig;
_Bool is_wl_chnl_cur;
undefined field7_0x13;
gs_lv_src_t *lv_src;
gs_lv_csm_t lv_csm;
gs_media_cmd_chnl_t *mcc;
gs_modem_ctrl_t *modem_ctrl;
int lv_igr_cnt;
uint32_t listener_id;
};
struct gs_battery_voltage
{
int32_t voltage;
float batt;
uint8_t series;
undefined field3_0x9;
undefined field4_0xa;
undefined field5_0xb;
enum batteryState_e state;
};
struct gs_battery_info
{
uint32_t interval;
char *addr;
gs_battery_voltage_t vol;
};
struct gs_meta_listener
{
void *ctx;
int (*event_cb)(void *, meta_event_t);
};
struct metadata_retriever
{
gs_meta_listener_t meta_listener;
struct duss_osal_mutex_handle_t *db_mutex;
struct sqlite3 *p_db;
_Bool b_parse_pending;
_Bool b_running;
undefined field5_0x12;
undefined field6_0x13;
struct duss_osal_task_handle_t *task_retriever;
};
struct __gs_common_cmd_ctrl
{
struct duss_osal_task_handle_t *cmn_cmd_task;
struct duss_osal_msgq_handle_t *cmn_cmd_queue;
_Bool cmn_cmd_task_quit;
undefined field3_0x9;
undefined field4_0xa;
undefined field5_0xb;
duss_event_client_handle_t event_client;
_Bool init;
undefined field8_0x11;
undefined field9_0x12;
undefined field10_0x13;
void *ctx;
};
struct gs_media_cmd_chnl
{
int chnl_fd;
_Bool init;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
};
struct keys_pack_to_racing_glass_t
{
uint8_t right_wheel_press : 2;
uint8_t right_wheel_scroll : 2;
uint8_t f1_press : 2;
uint8_t home : 2;
uint8_t left_wheel_scroll : 2;
uint8_t record_press : 2;
uint8_t reverved_2 : 2;
uint8_t reverved_3 : 2;
};
struct gs_audio_wl
{
void *private_data;
};
struct anon_struct_conflictc3fb_for_sw
{
uint8_t fun_key1 : 1;
uint8_t fun_key2 : 1;
uint8_t others : 6;
};
struct anon_struct_conflictc431_for_key
{
uint8_t up_5d : 1;
uint8_t down_5d : 1;
uint8_t left_5d : 1;
uint8_t right_5d : 1;
uint8_t ensure_5d : 1;
uint8_t cancel : 1;
uint8_t record : 1;
uint8_t pair : 1;
};
struct factory_check
{
uint32_t reserved : 31;
uint32_t result : 1;
};
struct pack_for_factory_test_t
{
struct factory_check check;
uint16_t not_use1[6];
struct anon_struct_conflictc3fb_for_sw sw;
struct anon_struct_conflictc431_for_key key;
uint8_t not_use2[26];
};
struct gs_shram
{
int fd;
uint8_t *addr;
int phy_size;
int map_offset;
int size;
};
struct gs_watermarker_us
{
uint8_t watermarker_flag;
undefined field1_0x1;
undefined field2_0x2;
undefined field3_0x3;
int dev_mem;
void *uncal_nvram_va;
void *uncal_nvram_align_va;
size_t uncal_nvram_map_sz;
};
struct gs_watermarker_ctrl
{
gs_media_cmd_chnl_t *mcc;
gs_watermarker_us_t watermarker_us;
};
struct __gs_queue
{
uchar *addr;
uint size;
uint num;
uint writer;
uint reader;
};
struct __gs_gui
{
_Bool gui_en;
undefined field1_0x1;
undefined field2_0x2;
undefined field3_0x3;
void *context;
void *dl_hdl;
gs_queue_t racing_drone_fc_osd_queue;
int (*initialize)(void **, gs_gui_config_t *);
void (*destroy)(void *);
int (*run)(void *);
int (*send_event)(void *, struct gs_gui_event_t *);
};
struct __gs_local_sd_info
{
duss_storage_client_t *sdcard_cli;
_Bool sd_insert;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
sd_file_type_t sd_type;
uint32_t sd_total_kbytes;
uint32_t sd_free_kbytes;
uint32_t sd_minfree_kbytes;
uint8_t sd_status;
uint8_t sd_formatting;
_Bool sd_init;
undefined field12_0x1b;
struct duss_osal_mutex_handle_t *sd_status_mutex;
gs_storage_listener_t *storage_listener;
gs_sd_listener_t *sd_listener;
gs_meta_listener_t *meta_listener;
gs_playback_listener_t *pb_listener;
};
struct rc_set_endpoint_t
{
uint8_t ep_min;
uint8_t ep_max;
};
struct rc_set_all_ep_t
{
struct rc_set_endpoint_t a_ch;
struct rc_set_endpoint_t e_ch;
struct rc_set_endpoint_t t_ch;
struct rc_set_endpoint_t r_ch;
};
struct dummy_ui_ctx_t
{
_Bool client;
undefined field1_0x1;
undefined field2_0x2;
undefined field3_0x3;
int sock_fd;
_Bool quit;
undefined field6_0x9;
undefined field7_0xa;
undefined field8_0xb;
struct duss_osal_task_handle_t *console_task;
struct duss_osal_timer_handle_t *console_timer;
struct duss_osal_event_handle_t *event;
struct duss_osal_task_handle_t *reader_task;
int (*msg_cb)(void *, void *);
};
struct rc_set_subtrim_reverse_t
{
uint16_t reserved : 6;
uint16_t reverse : 1;
uint16_t subtrim : 9;
};
struct rc_set_all_st_and_rev_t
{
struct rc_set_subtrim_reverse_t a_ch;
struct rc_set_subtrim_reverse_t e_ch;
struct rc_set_subtrim_reverse_t t_ch;
struct rc_set_subtrim_reverse_t r_ch;
};
struct rc_set_subtrim_t
{
int16_t st_value;
};
struct rc_set_all_st_t
{
struct rc_set_subtrim_t a_ch;
struct rc_set_subtrim_t e_ch;
struct rc_set_subtrim_t t_ch;
struct rc_set_subtrim_t r_ch;
};
struct anon_struct_conflict47e0
{
uint8_t reserved : 4;
uint8_t is_reverse_r : 1;
uint8_t is_reverse_t : 1;
uint8_t is_reverse_e : 1;
uint8_t is_reverse_a : 1;
};
union rc_set_reverse_t
{
struct anon_struct_conflict47e0 rev_bit;
uint8_t rev_byte;
};
struct gs_rc_ctrl
{
struct duss_osal_msgq_handle_t *rc_cmd_queue;
struct duss_osal_task_handle_t *rc_cmd_task;
_Bool rc_cmd_task_quit;
_Bool init;
union rc_set_reverse_t reverse_setting;
int8_t reverse_valid;
struct rc_set_all_st_t subtrim_setting;
int8_t subtrim_valid;
struct rc_set_all_ep_t endpoint_setting;
int8_t endpoint_valid;
uint8_t stick_mode;
int8_t stick_valid;
char rc_firm_ver[128];
char rc_sn[20];
int rc_mp_state;
int rc_active_state;
int8_t version_valid;
struct rc_set_all_st_and_rev_t subtrim_endpiont_setting;
uint8_t rc_lock_stat;
_Bool rc_monitor_quit;
undefined field20_0xc7;
struct duss_osal_task_handle_t *rc_monitor;
duss_event_client_handle_t event_client;
struct dummy_ui_ctx_t dummy_ui_ctx;
void *ctx;
};
struct gs_wl_ctrl
{
gs_media_cmd_chnl_t *mcc;
_Bool wl_start;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
};
struct racing_debug_info_t
{
uint8_t sdr_log_dump;
uint8_t vstream_dump;
uint8_t record;
uint32_t enc_sto_frm_drop_cnt;
uint32_t enc_lv_frm_drop_cnt;
uint32_t csi_frm_drop_cnt;
uint16_t channel_status;
uint16_t cam_frame_interval;
uint16_t outlier_frame_interval;
uint16_t outlier_frame_interval_cnt;
uint16_t cam_error;
uint16_t cam_ae_bv;
uint16_t cam_ae_dv;
uint32_t cam_ae_exp_time;
uint16_t cam_ae_iso;
uint16_t cam_awb_temp;
uint16_t cam_atm_value;
uint16_t cam_scene_mode;
uint16_t cam_snr;
uint16_t cam_ae_speed;
uint16_t cam_ltm_str;
uint16_t cam_saturation;
uint16_t cam_saturation_bias;
uint16_t cam_gamma;
uint16_t cam_snr_temp;
uint8_t cam_resolution;
uint8_t cam_angle;
uint8_t cam_mode;
uint8_t cam_ev;
uint8_t cam_satur;
uint8_t cam_white_balance;
int32_t shtl_ddr_temp;
int32_t shtl_acpu_temp;
int32_t shtl_modem_temp;
int32_t shtl_media_temp;
int32_t shtl_omc_temp;
uint8_t shtl_cp_report;
uint8_t shtl_cp_seq;
char shtl_ap_ver[128];
char shtl_board_sn[16];
uint8_t uav_rec_mode;
uint8_t uav_low_power_mode;
uint8_t uav_power_policy;
uint8_t HDVT_pref;
uint8_t enc_roi;
uint8_t sbus_mode;
char shtl_hardware_version[16];
uint8_t camera_type;
};
struct gs_debug_ctrl
{
struct __gs_info *gs_info;
product_shm_info_t *pdt_shm_va;
void *pdt_shm_align_va;
size_t pdt_shm_map_sz;
struct modem_shmem_info_t *plf_shm_va;
void *plf_shm_align_va;
size_t plf_shm_map_sz;
struct racing_debug_info_t rac_dbg_info;
undefined field8_0x116;
undefined field9_0x117;
};
struct timeval
{
__kernel_time_t tv_sec;
__kernel_suseconds_t tv_usec;
};
struct pcm_config
{
uint channels;
uint rate;
uint period_size;
uint period_count;
enum pcm_format format;
enum pcm_tstamp tstamp_type;
uint start_threshold;
uint stop_threshold;
uint silence_threshold;
uint silence_size;
int avail_min;
};
struct gs_aout
{
struct pcm_config config;
struct pcm *pcm_device;
uint32_t buffer_size;
};
struct gs_lv_src
{
int lv_dmi_fd;
_Bool force_stop;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
gs_media_cmd_chnl_t *mcc;
struct duss_osal_task_handle_t *lpc_ctrl_task;
_Bool lpc_ctrl_task_quit;
undefined field8_0x11;
undefined field9_0x12;
undefined field10_0x13;
struct duss_osal_msgq_handle_t *lpc_ctrl_queue;
struct duss_osal_task_handle_t *lpc_data_task;
_Bool lpc_data_task_quit;
_Bool lpc_data_eos;
undefined field15_0x1e;
undefined field16_0x1f;
gs_lv_csm_t *csm;
};
struct gs_user_json_root
{
struct cJSON *user_file_json_root;
struct cJSON *device_sn_json_root;
};
struct gs_video_channel
{
int (*start)(struct gs_video_channel *);
int (*stop)(struct gs_video_channel *);
_Bool (*handle_message)(struct gs_video_channel *, gs_video_channel_message_t *);
_Bool (*update_channel)(struct gs_video_channel *);
struct gs_video_channel_manager *chnl_mgr;
_Bool ready;
_Bool enable;
_Bool lazy_stop;
_Bool force_switch;
gs_main_channel_id_t id;
gs_sub_channel_id_t sub_id;
char *name;
_Bool sub_chnl_enable[6];
undefined field13_0x2a;
undefined field14_0x2b;
};
struct gs_wl_channel
{
gs_video_channel_t base;
_Bool wl_link;
uint8_t cam_workmode;
uint8_t cam_pbmode;
uint8_t cam_model;
};
struct gs_local_playback_channel
{
gs_video_channel_t base;
char video_path[256];
char audio_path[256];
};
struct gs_csi_channel
{
gs_video_channel_t base;
_Bool hdmi_plugin;
undefined field2_0x2d;
undefined field3_0x2e;
undefined field4_0x2f;
};
struct gs_rc_setting_channel
{
gs_video_channel_t base;
};
struct gs_non_video_channel
{
gs_video_channel_t base;
};
struct gs_video_channel_id
{
gs_main_channel_id_t main_chnl_id;
gs_sub_channel_id_t sub_chnl_id;
};
struct gs_av_in_channel
{
gs_video_channel_t base;
_Bool av_plugin;
undefined field2_0x2d;
undefined field3_0x2e;
undefined field4_0x2f;
};
struct gs_video_channel_manager
{
gs_wl_channel_t wl_chnl;
gs_local_playback_channel_t local_pb_chnl;
gs_av_in_channel_t av_in_chnl;
gs_rc_setting_channel_t rc_setting_chnl;
gs_csi_channel_t csi_chnl;
gs_non_video_channel_t non_video_chnl;
gs_video_channel_t *cur_chnl;
gs_video_channel_t *indexed_chnls[6];
gs_video_channel_t *prior_ordered_chnls[6];
gs_video_channel_t *pend_release_chnl;
gs_video_channel_id_t last_chnl_id;
struct __gs_info *gs_info;
gs_video_channel_switch_callback_t chnl_switch_cb;
void *chnl_switch_data;
gs_video_channel_push_callback_t chnl_push_cb;
void *chnl_push_data;
int8_t ignore_cam_push_cnt;
uint8_t cam_model;
uint8_t cam_workmode;
uint8_t cam_pbmode;
uint8_t racing_mode;
undefined field21_0x36d;
undefined field22_0x36e;
undefined field23_0x36f;
uint32_t listener_id;
struct duss_osal_task_handle_t *chnl_ctrl_task;
_Bool chnl_ctrl_task_quit;
undefined field27_0x379;
undefined field28_0x37a;
undefined field29_0x37b;
struct duss_osal_msgq_handle_t *chnl_ctrl_queue;
struct duss_osal_mutex_handle_t *chnl_ctrl_lock;
};
struct duss_osal_mutex_attrib_t
{
char *name;
};
struct gs_buzzer_info
{
struct duss_osal_mutex_handle_t *buzzer_mutex;
struct duss_osal_mutex_attrib_t buzzer_mutex_attr;
struct duss_osal_task_handle_t *buzzer_play_sound;
_Bool buzzer_play_sound_quit;
undefined field4_0xd;
undefined field5_0xe;
undefined field6_0xf;
int buzzer_enable_fd;
int buzzer_period_fd;
int buzzer_duty_fd;
_Bool buzzer_bat_enable;
undefined field11_0x1d;
undefined field12_0x1e;
undefined field13_0x1f;
};
struct _DUSS_MSG_RC_BAT_INFO_t
{
uint32_t bat_remain_value;
uint8_t bat_remain_ratio;
uint8_t bat_is_charging;
};
struct glass_signal_quality_t
{
uint8_t direction;
uint8_t signal_percent;
uint8_t energy1_uav_or_gnd;
uint8_t energy2_uav_or_gnd;
};
struct _DUSS_MSG_RACING_PHY_CHECK_REQ_t
{
uint8_t ant_check;
uint8_t reserved[2];
};
struct _DUSS_MSG_RACING_PHY_CHECK_INFO_t
{
uint8_t tx_ant_status;
uint8_t rx_ant_status;
uint8_t tx_ant_numbers;
uint8_t rx_ant_numbers;
uint8_t reserved;
};
struct _DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t
{
uint8_t chnl_num;
uint8_t bw;
DUSS_MSG_RACING_CHANNEL_OCCUPIED_IPSD_t chn_info[16];
uint8_t flag;
};
struct _DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t
{
uint8_t chnl_num;
uint8_t bw;
uint16_t freq_idx[16];
};
struct gs_modem_link_listener
{
void (*link_chg_cb)(void *, _Bool);
void *cb_ctx;
};
struct _DUSS_MSG_RACING_CHANNEL_ROB_INFO_t
{
uint8_t left_time;
uint8_t robber_name[7];
};
struct gs_modem_ctrl
{
int8_t gls_role;
uint8_t gls_count;
uint16_t conf_items[17];
struct duss_osal_mutex_handle_t *status_mutex_handle;
_Bool modem_monitor_quit;
undefined field5_0x29;
undefined field6_0x2a;
undefined field7_0x2b;
struct duss_osal_task_handle_t *modem_monitor;
uint8_t modem_state;
uint8_t sdr_role;
_Bool in_factory_mode;
_Bool in_aging_test;
_Bool flag_pairing_finished;
uint8_t last_modem_state;
uint8_t rob_flag;
undefined field16_0x37;
duss_event_client_handle_t event_client;
uint32_t seq_id;
gs_modem_link_listener_t listeners[8];
struct glass_signal_quality_t signal_quality;
DUSS_MSG_RACING_CHANNEL_SCAN_REQ_t scan_req;
DUSS_MSG_RACING_CHANNEL_SCAN_INFO_t scan_info;
DUSS_MSG_RACING_CHANNEL_ROB_INFO_t rob_info;
DUSS_MSG_RACING_PHY_CHECK_REQ_t check_req;
DUSS_MSG_RACING_PHY_CHECK_INFO_t check_info;
undefined field26_0x139;
undefined field27_0x13a;
undefined field28_0x13b;
enum gs_modem_scan_type_t modem_scan_type;
uint8_t area_id;
uint8_t nation_code[2];
uint8_t area_active_flag;
int dev_mem;
void *uncal_nvram_va;
void *uncal_nvram_align_va;
size_t uncal_nvram_map_sz;
struct glass_signal_quality_t rc_signal_quality;
uint16_t update_bandwidth;
_Bool power_level_expanded;
_Bool bw_pending;
};
struct DUSS_MSG_EXT_FC_RTC
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hours;
uint8_t minutes;
uint8_t seconds;
uint16_t millis;
};
struct DUSS_MSG_EXT_FC_RATE
{
uint8_t roll_rates;
uint8_t roll_expos;
uint8_t roll_s_rates;
uint8_t pitch_s_rates;
uint8_t yaw_s_rates;
uint8_t dyn_thr;
uint8_t thr_mid;
uint8_t thr_expos;
uint16_t tpa_bp;
uint8_t yaw_expos;
uint8_t yaw_rates;
uint8_t pitch_rates;
uint8_t pitch_expos;
uint8_t throttle_limit_type;
uint8_t throttle_limit_percent;
uint16_t rate_limit_roll;
uint16_t rate_limit_pitch;
uint16_t rate_limit_yaw;
};
struct DUSS_MSG_EXT_FC_FILTER
{
uint8_t gyro_lpf1;
uint16_t dterm_lpf1;
uint16_t yaw_lpf;
uint16_t gyro_no1;
uint16_t gyro_no1_cut;
uint16_t dterm_no1;
uint16_t dterm_no1_cut;
uint16_t gyro_no2;
uint16_t gyro_no2_cut;
uint8_t dterm_lpf1_type;
uint8_t gyro_hw_cut;
uint8_t gyro_hw32_cut;
uint16_t gyro_lpf1_alt;
uint16_t gyro_lpf2;
uint8_t gyro_lpf1_type;
uint8_t gyro_lpf2_type;
uint16_t dterm_lpf2;
uint8_t dterm_ft2_type;
uint16_t dyn_lpf_gyro_min_hz;
uint16_t dyn_lpf_gyro_max_hz;
uint16_t dyn_lpf_dterm_min_hz;
uint16_t dyn_lpf_dterm_max_hz;
uint8_t dyn_notch_range;
uint8_t dyn_notch_width_percent;
uint16_t dyn_notch_q;
uint16_t dyn_notch_min_hz;
uint8_t gyro_rpm_notch_harmonics;
uint8_t gyro_rpm_notch_min;
};
struct DUSS_MSG_EXT_FC_AUX
{
uint8_t aux_id;
uint8_t aux_index;
uint8_t start_step;
uint8_t end_step;
};
struct DUSS_MSG_EXT_FC_BATTERY_STATE
{
uint8_t cell;
uint16_t capacity;
uint8_t voltage;
uint16_t MAhDrawn;
uint16_t amperage;
uint8_t state;
uint16_t battVoltage;
};
struct DUSS_MSG_EXT_FC_RC
{
uint16_t chnl_1_v;
uint16_t chnl_2_v;
uint16_t chnl_3_v;
uint16_t chnl_4_v;
uint16_t chnl_5_v;
uint16_t chnl_6_v;
uint16_t chnl_7_v;
uint16_t chnl_8_v;
uint16_t chnl_9_v;
uint16_t chnl_10_v;
uint16_t chnl_11_v;
uint16_t chnl_12_v;
uint16_t chnl_13_v;
uint16_t chnl_14_v;
uint16_t chnl_15_v;
uint16_t chnl_16_v;
uint16_t chnl_17_v;
uint16_t chnl_18_v;
};
struct DUSS_MSG_EXT_FC_MSP_STATUS
{
uint16_t task_delta_time;
uint16_t i2c_error_counter;
uint16_t sensor_status;
uint32_t flight_flags_lsb;
uint8_t pid_index;
uint16_t sysAvgLoad;
uint8_t pid_count;
uint8_t rate_index;
uint8_t flight_count;
uint8_t arming_count;
uint32_t arming_flags;
uint8_t flight_flags_msb;
};
struct DUSS_MSG_EXT_FC_ESC_DATA
{
uint8_t temperature;
uint16_t rpm;
};
struct DUSS_MSG_EXT_FC_OSD_CONFIG
{
uint8_t osdFlags;
uint8_t videoSystems;
uint8_t units;
uint8_t rssi_alarm;
uint16_t cap_alarm;
uint8_t reserved;
uint8_t item_count;
uint16_t alt_alarm;
uint16_t item_position[100];
uint8_t stat_count;
uint8_t stat_state[100];
uint8_t timer_count;
uint16_t timer[10];
uint16_t enablewarnings;
uint8_t warning_count;
uint32_t warnings;
uint8_t profile_count;
uint8_t profile_index;
uint8_t stick_overlay;
};
struct DUSS_MSG_EXT_FC_SERVO
{
uint16_t min;
uint16_t max;
uint16_t middle;
uint8_t rate;
uint8_t ffchnl;
uint32_t reverse;
};
struct DUSS_MSG_EXT_FC_VERSION
{
uint8_t major;
uint8_t minor;
uint8_t patch_level;
};
struct DUSS_MSG_EXT_FC_ADVANCED_PID
{
uint16_t reserved1;
uint16_t reserved2;
uint16_t reserved3;
uint8_t reserved4;
uint8_t vbat_comp;
uint8_t feedForwardTrans;
uint8_t reserved5;
uint8_t reserved6;
uint8_t reserved7;
uint8_t reserved8;
uint16_t rateAcelLimit;
uint16_t yawrateAcelLimit;
uint8_t levelAngleLimit;
uint8_t reserved9;
uint16_t ThrottleThreshold;
uint16_t AcceleratorGain;
uint16_t reserved10;
uint8_t rotation;
uint8_t smartFF;
uint8_t relax;
uint8_t relax_type;
uint8_t abs_control_gain;
uint8_t throttle_boost;
uint8_t acro_trainer_angle_limit;
uint16_t RollFF;
uint16_t PitchFF;
uint16_t YawFF;
uint8_t ag_mode;
uint8_t d_min_roll;
uint8_t d_min_pitch;
uint8_t d_min_yaw;
uint8_t d_min_gain;
uint8_t d_min_advance;
uint8_t use_integrated_yaw;
uint8_t integrated_yaw_relax;
uint8_t iterm_relax_cutoff;
};
struct DUSS_MSG_FC_RACING_DRONE_OSD_PUSH
{
uint8_t GPS_3D_fix;
uint8_t GPS_numSat;
int32_t Lattitude;
int32_t Longtitude;
uint16_t GPS_altitude;
uint16_t GPS_speed;
uint16_t GPS_distance_home;
int16_t GPS_direction_home;
int16_t angx;
int16_t angy;
int16_t heading;
int32_t estAlt;
int16_t vario;
uint8_t vbat;
uint16_t intPowerMeterSum;
uint16_t rssi;
uint16_t amperage;
};
struct DUSS_MSG_EXT_FC_PID
{
uint8_t roll_p;
uint8_t roll_i;
uint8_t roll_d;
uint8_t pitch_p;
uint8_t pitch_i;
uint8_t pitch_d;
uint8_t yaw_p;
uint8_t yaw_i;
uint8_t yaw_d;
uint8_t angle_str;
uint8_t horizon_str;
uint8_t horizon_trans;
uint8_t mag_p;
uint8_t mag_i;
uint8_t mag_d;
};
struct gs_ext_fc
{
uint8_t requestType;
DUSS_MSG_EXT_FC_PID_t pid;
DUSS_MSG_EXT_FC_AUX_t aux[20];
DUSS_MSG_EXT_FC_RATE_t rate;
DUSS_MSG_EXT_FC_SERVO_t servo[8];
DUSS_MSG_EXT_FC_FILTER_t filter;
DUSS_MSG_EXT_FC_ADVANCED_PID_t pid_adv;
DUSS_MSG_EXT_FC_MSP_STATUS_t msp_status;
DUSS_MSG_EXT_FC_RC_t rc;
DUSS_MSG_EXT_FC_BATTERY_STATE_t batt;
DUSS_MSG_EXT_FC_OSD_CONFIG_t osd_config;
DUSS_MSG_EXT_FC_ESC_DATA_t esc_data[4];
DUSS_MSG_EXT_FC_RTC_t fc_rtc;
char name[17];
DUSS_MSG_EXT_FC_VERSION_t fc_version;
DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t osd_info;
undefined field16_0x31b;
enum GS_EXT_FC_OSD_STATUS osd_status;
struct duss_osal_task_handle_t *get_osd_config;
_Bool get_osd_quit;
undefined field20_0x325;
undefined field21_0x326;
undefined field22_0x327;
};
struct _DUSS_MSG_HISTOGRAM_t
{
uint8_t Op : 1;
uint8_t Reserved : 7;
};
struct anon_struct_conflict2349
{
uint16_t DataDisp : 1;
uint16_t PertureDisp : 1;
uint16_t ShutterSpeedDisp : 1;
uint16_t ISODisp : 1;
uint16_t EvBiasDisp : 1;
uint16_t SharpnessDisp : 1;
uint16_t ContrastDisp : 1;
uint16_t SaturationDisp : 1;
uint16_t Res : 8;
};
union anon_union_conflict23e3_for_u
{
struct anon_struct_conflict2349 bits;
uint16_t Value;
};
struct _DUSS_MSG_PHOTO_OSD_PARA_
{
union anon_union_conflict23e3_for_u u;
};
struct anon_struct_conflict242a
{
uint32_t DataDisp : 1;
uint32_t PertureDisp : 1;
uint32_t ShutterSpeedDisp : 1;
uint32_t ISODisp : 1;
uint32_t AeModeDisp : 1;
uint32_t FlashLightDisp : 1;
uint32_t WbDisp : 1;
uint32_t EvBiasDisp : 1;
uint32_t SharpnessDisp : 1;
uint32_t ContrastDisp : 1;
uint32_t SaturationDisp : 1;
uint32_t CapModeDisp : 1;
uint32_t PhoSizeDisp : 1;
uint32_t PhoVideoQualityDisp : 1;
uint32_t HistogramsDisp : 1;
uint32_t BatteryPowerDisp : 1;
uint32_t Res : 16;
};
union anon_union_conflict2544_for_u
{
struct anon_struct_conflict242a bits;
uint16_t Value;
};
struct _DUSS_MSG_PREVIEW_OSD_PARA_t
{
union anon_union_conflict2544_for_u u;
};
struct _DUSS_MSG_QUICKVIEW_t
{
uint8_t Time : 7;
uint8_t Op : 1;
};
struct _DUSS_MSG_CAMERA_AUDIO_STATUS_t
{
uint8_t reversed : 6;
uint8_t mic_opened : 1;
uint8_t speaker_opened : 1;
uint8_t speaker_audio_volumn;
};
struct _DUSS_MSG_F_INDEX_MODE_t
{
uint8_t Mode;
};
struct _DUSS_MSG_CAM_STATE_t
{
uint32_t WifiCon : 1;
uint32_t UsbCon : 1;
uint32_t TimeSync : 1;
uint32_t Capture : 3;
uint32_t Record : 2;
uint32_t SensorErr : 1;
uint32_t SdInserted : 1;
uint32_t SdStatus : 4;
uint32_t FwUpgrading : 1;
uint32_t FwUpgradErr : 2;
uint32_t OverHeat : 1;
uint32_t CapForbit : 1;
uint32_t Storage : 1;
uint32_t ContiCap : 1;
uint32_t Hdmi : 1;
uint32_t Encrypt : 2;
uint32_t FileSync : 1;
uint32_t RCBtmForbid : 1;
uint32_t InFocus : 1;
uint32_t gimbal_state : 1;
uint32_t is_tracking : 1;
uint32_t is_hyperlapse : 1;
uint32_t Reserved : 2;
};
struct _DUSS_MSG_SYSTEM_STATE_t
{
DUSS_MSG_CAM_STATE_t CamState;
uint8_t WorkMode;
};
struct _DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t
{
float x;
float y;
};
struct _DUSS_MSG_CAMERA_STATUS_PUSH_t
{
DUSS_MSG_SYSTEM_STATE_t CameraStatus;
uint32_t Capacity;
uint32_t SpareCap;
uint32_t RemainShots;
uint32_t RemainTime;
DUSS_MSG_F_INDEX_MODE_t FileIndexMode;
DUSS_MSG_QUICKVIEW_t PhotoQuickViewPara;
DUSS_MSG_PHOTO_OSD_PARA_t PhotoOSDPara;
DUSS_MSG_PREVIEW_OSD_PARA_t PreviewOSDPara;
uint16_t RecordTime;
uint8_t CapCapacityNum;
DUSS_MSG_HISTOGRAM_t Histogram;
uint8_t CameraModel;
uint16_t CurrentRecordingTime;
uint8_t ProtocolType;
uint8_t RawSupport;
uint8_t LcdFormat;
uint8_t Reserved;
uint8_t CropMode;
uint8_t NdFilter;
uint32_t ErrCode;
uint8_t mic_audio_volumn;
uint8_t camera_advance_mode;
uint8_t StreamQuality;
DUSS_MSG_CAMERA_AUDIO_STATUS_t audio_status;
DUSS_MSG_HYPERLAPSE_LIVEVIEW_MARGIN_t HyperlapseMargin;
};
struct duss_list_head
{
struct duss_list_head *next;
struct duss_list_head *prev;
};
struct gs_storage_listener
{
void *ctx;
int (*event_cb)(void *, storage_event_t);
};
struct gs_lv_rec_ctrl
{
uint8_t *lv_rec_state;
gs_lv_src_t *lv_src;
gs_lv_csm_t lv_csm;
gs_storage_listener_t storage_listener;
struct duss_osal_mutex_handle_t *list_mutex;
struct duss_list_head frame_list;
int num_in_list;
int frames_received;
_Bool b_wrf_running;
undefined field9_0x3d;
undefined field10_0x3e;
undefined field11_0x3f;
int frames_write;
struct duss_osal_task_handle_t *task_wrf;
};
struct gs_avin_us
{
uint8_t brightness;
uint8_t saturation;
undefined field2_0x2;
undefined field3_0x3;
int dev_mem;
void *uncal_nvram_va;
void *uncal_nvram_align_va;
size_t uncal_nvram_map_sz;
};
struct gs_av_in_ctrl
{
gs_media_cmd_chnl_t *mcc;
_Bool avin_start;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
gs_avin_us_t avin_us;
};
struct gs_camera_cmd_ctrl_t
{
struct duss_osal_task_handle_t *cam_cmd_task;
struct duss_osal_msgq_handle_t *cam_cmd_queue;
_Bool cam_cmd_task_quit;
undefined field3_0x9;
undefined field4_0xa;
undefined field5_0xb;
duss_event_client_handle_t event_client;
_Bool init;
_Bool set_best_liveview;
uint8_t lv_format;
undefined field10_0x13;
struct duss_osal_task_handle_t *cam_monitor_task;
_Bool cam_monitor_task_quit;
undefined field13_0x19;
undefined field14_0x1a;
undefined field15_0x1b;
void *ctx;
};
struct gs_bl
{
duss_hal_obj_handle_t i2c_obj[2];
uint8_t brightness_value;
undefined field2_0x9;
undefined field3_0xa;
undefined field4_0xb;
float lr_ratio;
uint8_t mode;
_Bool b_quit_monitor;
undefined field8_0x12;
undefined field9_0x13;
struct duss_osal_task_handle_t *lcd_monitor_task;
};
struct _DUSS_MSG_RC_MS_LINK_STATUS
{
uint8_t pair_role;
uint8_t master_link_stat : 4;
uint8_t master_device_type : 4;
uint8_t slave_link_stat : 4;
uint8_t slave_device_type : 4;
uint8_t coach_role;
uint8_t double_rc_stat;
};
struct duss_osal_timer_attrib_t
{
char *name;
duss_osal_timer_entry_t entry;
void *param;
uint32_t timeout_us;
int32_t repeat_times;
enum duss_osal_priority_t priority;
};
struct gs_fan_info
{
struct duss_osal_task_handle_t *fan_adjust_task;
_Bool fan_adjust_task_quit;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
int fan_pwm_period;
int fan_pwm_duty;
enum gs_fan_level_t fan_pwm_level;
_Bool fan_enable;
undefined field9_0x15;
undefined field10_0x16;
undefined field11_0x17;
};
struct __gs_use_times_info
{
uint8_t magic[2];
_Bool flag;
uint32_t times;
};
struct gs_usb_listener
{
void *ctx;
int (*event_cb)(void *, usb_event_t);
};
struct gs_sd_listener
{
void *ctx;
int (*event_cb)(void *, sd_event_t);
};
struct gs_lv_transcode
{
ion_info_t *ion_info;
struct duss_osal_mutex_handle_t *rec_mutex;
record_state_t cur_state;
record_state_t tgt_state;
_Bool b_rec_running;
undefined field5_0x11;
undefined field6_0x12;
undefined field7_0x13;
struct duss_osal_task_handle_t *rec_thread;
uint8_t *lv_rec_state;
gs_local_sd_info_t *sd_info;
gs_sd_listener_t sd_listener;
gs_usb_listener_t usb_listener;
gs_media_cmd_chnl_t *mcc;
int dmi_data_fd;
int file_idx;
char file_name[128];
struct AVCodecContext *avcodec_ctx;
p1_muxer_handle_t muxer;
record_mode_t rec_mode;
int video_stream_index;
undefined field21_0xcc;
undefined field22_0xcd;
undefined field23_0xce;
undefined field24_0xcf;
int64_t cur_rec_time;
char *sps_buf;
uint32_t sps_len;
struct duss_osal_mutex_handle_t *video_list_mutex;
struct duss_list_head video_list;
int num_frames;
_Bool b_running;
_Bool b_eos;
undefined field33_0xf2;
undefined field34_0xf3;
struct duss_osal_task_handle_t *task_rx;
uint32_t frames_received;
uint32_t frames_write;
uint32_t last_key_frm_idx;
uint32_t last_frame_idx;
_Bool b_wrf_running;
undefined field41_0x109;
undefined field42_0x10a;
undefined field43_0x10b;
FILE *p_file;
struct duss_osal_task_handle_t *task_wrf;
int first_idx_in_file;
_Bool b_pending_new_file;
_Bool b_avin_recording;
undefined field49_0x11a;
undefined field50_0x11b;
undefined field51_0x11c;
undefined field52_0x11d;
undefined field53_0x11e;
undefined field54_0x11f;
int64_t cur_rec_time_ms;
char srt_file_name[128];
_Bool b_srt_record_running;
_Bool b_srt_switch_new_file;
undefined field59_0x1aa;
undefined field60_0x1ab;
FILE *p_srt_file;
struct duss_osal_task_handle_t *task_srt_record;
undefined field63_0x1b4;
undefined field64_0x1b5;
undefined field65_0x1b6;
undefined field66_0x1b7;
};
struct __gs_info
{
duss_event_client_handle_t event_client;
duss_hal_obj_handle_t vmem_obj;
duss_hal_obj_handle_t gpio_obj;
int32_t dev_mem;
gs_debug_ctrl_t dbg_ctrl;
struct duss_osal_task_handle_t *sdr_status_listen_task;
_Bool b_sdr_status_quit;
undefined field7_0x12d;
undefined field8_0x12e;
undefined field9_0x12f;
gs_local_sd_info_t sd_info;
struct duss_osal_timer_handle_t *sd_monitor_timer;
DUSS_MSG_RC_BAT_INFO_t bat_info;
undefined field13_0x16a;
undefined field14_0x16b;
gs_battery_info_t batt_info;
uint8_t current_uav_rc_sdr_mode;
undefined field17_0x185;
undefined field18_0x186;
undefined field19_0x187;
gs_modem_ctrl_t modem_ctrl;
DUSS_MSG_RC_MS_LINK_STATUS_t ms_link_stat;
_Bool b_active_quit;
_Bool b_rc_mp_state_got;
_Bool b_rc_active_get_pending;
_Bool b_uav_mp_state_got;
_Bool b_uav_active_get_pending;
uint8_t uav_mp_state;
uint8_t uav_active_state;
uint8_t gls_active_state;
undefined field30_0x2f1;
undefined field31_0x2f2;
undefined field32_0x2f3;
struct duss_osal_task_handle_t *get_activate_task;
gs_use_times_info_t use_times_before_active;
undefined field35_0x2ff;
gs_rc_ctrl_t rc_ctrl;
struct keys_pack_to_racing_glass_t key_state;
_Bool key_flag;
undefined field39_0x3f7;
struct duss_osal_timer_handle_t *keyscan_task_handle;
struct duss_osal_timer_attrib_t keyscan_task_attrib;
struct duss_osal_timer_handle_t *detect_taking_off_task_handle;
struct duss_osal_timer_attrib_t detect_taking_off_task_attrib;
struct pack_for_factory_test_t factory_test_key_info;
gs_buzzer_info_t buzzer_info;
gs_fan_info_t fan_info;
uint8_t cam_model;
uint8_t cam_work_mode;
_Bool check_cam_work_mode;
_Bool cam_is_pb;
gs_common_cmd_ctrl_t cmn_cmd_ctrl;
struct gs_camera_cmd_ctrl_t cam_cmd_ctrl;
DUSS_MSG_CAMERA_STATUS_PUSH_t uav_cam_stat;
undefined field54_0x50b;
gs_video_channel_manager_t chnl_mgr;
gs_wl_ctrl_t wl_ctrl;
gs_av_in_ctrl_t avin_ctrl;
vdec_local_player_t *player;
metadata_retriever_t metad_rt;
uint8_t lv_rec_state;
undefined field61_0x8d1;
undefined field62_0x8d2;
undefined field63_0x8d3;
gs_lv_src_t lv_src;
gs_lv_rec_ctrl_t lv_rec_ctrl;
gs_usb_gadget_vt_t usb_vt;
gs_lv_transcode_t lv_transcode;
gs_aout_t gs_aout;
gs_audio_wl_t audio_wl;
gs_media_cmd_chnl_t media_cmd_chnl;
gs_bl_t gs_bl;
struct timeval disarm_start;
uint8_t disarm_flag;
uint8_t low_power_mode;
undefined field75_0xb9a;
undefined field76_0xb9b;
uint32_t rtc_listener_id;
struct duss_osal_timer_handle_t *rtc_brdcst_timer;
struct duss_osal_timer_handle_t *lcdc_blending_check_timer;
gs_gui_t gui;
gs_ext_fc_t ext_fc;
gs_shram_t pdt_shram;
gs_shram_t plf_shram;
char user_data_file_name[64];
gs_queue_t user_data_queue;
struct duss_osal_timer_handle_t *user_data_update_timer;
struct duss_osal_task_handle_t *user_data_sender_task;
struct duss_osal_task_handle_t *device_sn_check_task;
_Bool is_pc_connect;
uint8_t user_privacy_setting;
uint16_t user_vt_sn_id;
uint16_t user_rc_sn_id;
uint8_t user_country_code[2];
_Bool b_sender_task_quit;
_Bool b_sn_check_task_quit;
gs_user_json_root_t json_root_info;
undefined field97_0xf9a;
undefined field98_0xf9b;
struct duss_osal_mutex_handle_t *user_queue_mutex;
gs_avin_test_ctrl_t avin_test_ctrl;
_Bool is_speaker_test_start;
undefined field102_0xfa9;
undefined field103_0xfaa;
undefined field104_0xfab;
struct duss_osal_mutex_handle_t *test_status_mutex;
uint8_t uav_power_status;
uint8_t arm_flag_from_uav;
uint8_t exit_flag;
undefined field109_0xfb3;
gs_watermarker_ctrl_t watermarker_ctrl;
undefined field111_0xfcc;
undefined field112_0xfcd;
undefined field113_0xfce;
undefined field114_0xfcf;
};
struct ip_channel_addr
{
char *ifx;
uint16_t loc_port;
undefined field2_0x6;
undefined field3_0x7;
char *address;
uint16_t rmt_port;
undefined field6_0xe;
undefined field7_0xf;
};
struct pthread_cond_t
{
int32_t __private[1];
};
struct AVFrac
{
int64_t val;
int64_t num;
int64_t den;
};
struct duss_hal_storage_info
{
char id[64];
char fs[8];
char path[256];
char state;
undefined field4_0x149;
undefined field5_0x14a;
undefined field6_0x14b;
int speed;
int uhs_grade;
int capacity;
int readonly;
int block_size;
};
struct AVIndexEntry
{
int64_t pos;
int64_t timestamp;
int flags : 2;
int size : 30;
int min_distance;
};
struct AVBuffer
{
};
struct AVRational
{
int num;
int den;
};
struct AVChapter
{
int id;
struct AVRational time_base;
undefined field2_0xc;
undefined field3_0xd;
undefined field4_0xe;
undefined field5_0xf;
int64_t start;
int64_t end;
struct AVDictionary *metadata;
undefined field9_0x24;
undefined field10_0x25;
undefined field11_0x26;
undefined field12_0x27;
};
struct gs_gui_event_t
{
gs_gui_event_type_t type;
gs_gui_event_symbol_t symbol;
float posx;
float posy;
float dx;
float dy;
float yaw;
float pitch;
float roll;
};
struct _DUSS_MSG_V_CRTL_t
{
uint8_t Op;
uint32_t time_offset;
};
struct pthread_mutex_t
{
int32_t __private[1];
};
struct duss_osal_event_attrib_t
{
char *name;
};
struct duss_osal_event_handle_t
{
struct duss_osal_event_attrib_t attrib;
struct pthread_mutex_t mutex;
struct pthread_cond_t cond;
uint32_t data;
int32_t flag;
};
struct modem_ops
{
int (*pairing_control)(void *, uint32_t);
int (*set_pairing_role)(void *, uint32_t);
int (*get_link_stat)(void *, enum gs_link_stat_t *);
int (*get_bandwidth)(void *, uint16_t *);
int (*set_bandwidth)(void *, uint16_t);
int (*get_chnl)(void *, uint16_t *);
int (*set_chnl)(void *, uint16_t);
int (*rob_chnl)(void *, uint16_t);
int (*rob_chnl_cancel)(void *);
int (*get_rob_left_time)(void *, uint8_t *);
int (*get_rob_flag)(void *, uint8_t *);
int (*clear_rob_flag)(void *);
int (*set_public_chnl)(void *);
int (*is_drone_broadcast)(void *, uint16_t *);
int (*enable_drone_broadcast)(void *, uint16_t);
int (*listen_broadcast)(void *, uint16_t);
int (*is_silent)(void *, uint16_t *);
int (*set_chnl_scan_info)(void *, uint8_t, uint8_t);
int (*scan_chnl_x)(void *, uint8_t, uint8_t);
int (*check_ant_status)(void *);
int (*if_new_chnl_scan_info)(void *, uint8_t *);
int (*if_new_phy_check_info)(void *, uint8_t *);
int (*if_update_bandwidth_pending)(void *, uint8_t *);
int (*clear_scan_flag)(void *);
int (*clear_check_flag)(void *);
int (*get_chnl_scan_info)(void *, uint8_t, uint8_t *, uint8_t *, uint32_t *);
int (*get_ant_status)(void *, uint8_t *, uint8_t *);
int (*get_dbg_mcs)(void *, uint16_t *);
int (*set_dbg_mcs)(void *, uint16_t);
int (*get_dbg_harq)(void *, uint16_t *);
int (*set_dbg_harq)(void *, uint16_t);
int (*get_dbg_codec_rate)(void *, uint16_t *);
int (*set_dbg_codec_rate)(void *, uint16_t);
int (*get_signal_quality)(void *, struct glass_signal_quality_t *);
int (*get_rc_signal_quality)(void *, struct glass_signal_quality_t *);
int (*set_bandwidth_mode)(void *, uint16_t);
int (*change_silent_mode)(void *, uint8_t);
int (*get_chnl_cnt)(void *, uint16_t *);
int (*get_freq_by_index)(void *, uint16_t *, uint16_t);
int (*get_scan_type)(void *, uint8_t *);
int (*get_wl_area_id)(void *, uint8_t *);
int (*get_pairing_finish_flag)(void *, uint8_t *);
int (*check_conf_items)(void *);
};
struct _UAV_RECORD_MODE_t
{
uint8_t type;
uint8_t value;
};
struct AVFrameSideData
{
enum AVFrameSideDataType type;
uint8_t *data;
int size;
struct AVDictionary *metadata;
};
struct _DUSS_MSG_APERTURE_t
{
uint16_t Value;
};
struct _DUSS_MSG_VIDEO_STANDARD_t
{
uint8_t Value;
};
struct AVCodecParser
{
int codec_ids[5];
int priv_data_size;
int (*parser_init)(struct AVCodecParserContext *);
int (*parser_parse)(struct AVCodecParserContext *, struct AVCodecContext *, uint8_t **, int *, uint8_t *, int);
void (*parser_close)(struct AVCodecParserContext *);
int (*split)(struct AVCodecContext *, uint8_t *, int);
struct AVCodecParser *next;
};
struct hpi_channel
{
__u32 priority;
__u8 major_id : 4;
__u8 minor_id : 4;
undefined field3_0x5;
__u16 order;
};
struct hpi_channel_addr
{
char *obj_name;
duss_hal_hpi_config_t cfg;
};
struct _DUSS_MSG_WORKMODE_t
{
uint8_t WorkMode;
};
struct timespec
{
__kernel_time_t tv_sec;
long tv_nsec;
};
struct ext_fc_ops
{
int (*get_craft_name)(void *, char *);
int (*get_pid_profile_index)(void *, uint8_t *);
int (*get_rate_profile_index)(void *, uint8_t *);
int (*get_osd_profile_index)(void *, uint8_t *);
int (*select_file)(void *, uint8_t, uint8_t);
int (*copy_file)(void *, uint8_t, uint8_t, uint8_t);
int (*write_eeprom)(void *);
int (*get_rc_channel)(void *, uint8_t, uint16_t *);
int (*get_aux_id)(void *, uint8_t *, uint8_t, uint8_t *, uint8_t *);
int (*get_aux_mode)(void *, uint8_t, uint8_t *, uint8_t *, uint8_t *);
int (*set_aux_mode)(void *, uint8_t, uint8_t, uint8_t, uint8_t);
int (*set_request_type)(void *, uint8_t);
int (*get_request_type)(void *, uint8_t *);
int (*get_ag_gain)(void *, uint16_t *);
int (*set_ag_gain)(void *, uint16_t);
int (*get_ag_thr)(void *, uint16_t *);
int (*set_ag_thr)(void *, uint16_t);
int (*get_thr_boost)(void *, uint8_t *);
int (*set_thr_boost)(void *, uint8_t);
int (*get_ff_trans)(void *, uint8_t *);
int (*set_ff_trans)(void *, uint8_t);
int (*get_filter)(void *, uint8_t, uint16_t *);
int (*set_filter)(void *, uint8_t, uint16_t);
int (*get_rate)(void *, uint8_t, uint8_t *);
int (*set_rate)(void *, uint8_t, uint8_t);
int (*get_super_rate)(void *, uint8_t, uint8_t *);
int (*set_super_rate)(void *, uint8_t, uint8_t);
int (*get_expo)(void *, uint8_t, uint8_t *);
int (*set_expo)(void *, uint8_t, uint8_t);
int (*get_dyn_thr)(void *, uint8_t *);
int (*set_dyn_thr)(void *, uint8_t);
int (*get_thr_mid)(void *, uint8_t *);
int (*set_thr_mid)(void *, uint8_t);
int (*get_thr_expo)(void *, uint8_t *);
int (*set_thr_expo)(void *, uint8_t);
int (*get_tpa)(void *, uint16_t *);
int (*set_tpa)(void *, uint16_t);
int (*get_pid)(void *, uint8_t, uint8_t *);
int (*set_pid)(void *, uint8_t, uint8_t);
int (*get_pid_f)(void *, uint8_t, uint16_t *);
int (*set_pid_f)(void *, uint8_t, uint16_t);
int (*get_battery_state)(void *, enum batteryState_e *);
int (*get_battery_voltage)(void *, uint16_t *);
int (*get_battery_avg_voltage)(void *, uint16_t *);
int (*get_battery_amperage)(void *, uint16_t *);
int (*get_battery_mah)(void *, uint16_t *);
int (*get_battery_usage)(void *, uint16_t *);
int (*get_battery_power)(void *, uint16_t *);
int (*set_arming_disabled)(void *, uint8_t);
int (*get_arming_disabled)(void *, uint32_t *);
int (*get_fc_version)(void *, uint8_t *, uint8_t *, uint8_t *);
int (*set_sbus_mode)(void *, uint8_t);
int (*get_racing_osd_info)(void *, DUSS_MSG_FC_RACING_DRONE_OSD_PUSH_t *);
int (*get_osd_position)(void *, int, _Bool *, int *, int *);
int (*get_units)(void *, uint8_t *);
int (*get_fly_mode)(void *, uint16_t *);
int (*get_rtc_date)(void *, DUSS_MSG_EXT_FC_RTC_t *);
int (*get_esc_temperature)(void *, uint8_t *);
};
struct duss_hal_usbacc_config_t
{
duss_usbacc_port_t port;
};
struct usbacc_channel_addr
{
char *obj_name;
struct duss_hal_usbacc_config_t cfg;
undefined field2_0x6;
undefined field3_0x7;
};
struct AVCodecContext
{
struct AVClass *av_class;
int log_level_offset;
enum AVMediaType codec_type;
struct AVCodec *codec;
char codec_name[32];
enum AVCodecID codec_id;
uint codec_tag;
uint stream_codec_tag;
void *priv_data;
struct AVCodecInternal *internal;
void *opaque;
int bit_rate;
int bit_rate_tolerance;
int global_quality;
int compression_level;
int flags;
int flags2;
uint8_t *extradata;
int extradata_size;
struct AVRational time_base;
int ticks_per_frame;
int delay;
int width;
int height;
int coded_width;
int coded_height;
int gop_size;
enum AVPixelFormat pix_fmt;
int me_method;
void (*draw_horiz_band)(struct AVCodecContext *, struct AVFrame *, int *, int, int, int);
AVPixelFormat (*get_format)(struct AVCodecContext *, enum AVPixelFormat *);
int max_b_frames;
float b_quant_factor;
int rc_strategy;
int b_frame_strategy;
float b_quant_offset;
int has_b_frames;
int mpeg_quant;
float i_quant_factor;
float i_quant_offset;
float lumi_masking;
float temporal_cplx_masking;
float spatial_cplx_masking;
float p_masking;
float dark_masking;
int slice_count;
int prediction_method;
int *slice_offset;
struct AVRational sample_aspect_ratio;
int me_cmp;
int me_sub_cmp;
int mb_cmp;
int ildct_cmp;
int dia_size;
int last_predictor_count;
int pre_me;
int me_pre_cmp;
int pre_dia_size;
int me_subpel_quality;
int dtg_active_format;
int me_range;
int intra_quant_bias;
int inter_quant_bias;
int slice_flags;
int xvmc_acceleration;
int mb_decision;
uint16_t *intra_matrix;
uint16_t *inter_matrix;
int scenechange_threshold;
int noise_reduction;
int me_threshold;
int mb_threshold;
int intra_dc_precision;
int skip_top;
int skip_bottom;
float border_masking;
int mb_lmin;
int mb_lmax;
int me_penalty_compensation;
int bidir_refine;
int brd_scale;
int keyint_min;
int refs;
int chromaoffset;
int scenechange_factor;
int mv0_threshold;
int b_sensitivity;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
enum AVColorSpace colorspace;
enum AVColorRange color_range;
enum AVChromaLocation chroma_sample_location;
int slices;
enum AVFieldOrder field_order;
int sample_rate;
int channels;
enum AVSampleFormat sample_fmt;
int frame_size;
int frame_number;
int block_align;
int cutoff;
int request_channels;
undefined field102_0x1bc;
undefined field103_0x1bd;
undefined field104_0x1be;
undefined field105_0x1bf;
uint64_t channel_layout;
uint64_t request_channel_layout;
enum AVAudioServiceType audio_service_type;
enum AVSampleFormat request_sample_fmt;
int (*get_buffer)(struct AVCodecContext *, struct AVFrame *);
void (*release_buffer)(struct AVCodecContext *, struct AVFrame *);
int (*reget_buffer)(struct AVCodecContext *, struct AVFrame *);
int (*get_buffer2)(struct AVCodecContext *, struct AVFrame *, int);
int refcounted_frames;
float qcompress;
float qblur;
int qmin;
int qmax;
int max_qdiff;
float rc_qsquish;
float rc_qmod_amp;
int rc_qmod_freq;
int rc_buffer_size;
int rc_override_count;
struct RcOverride *rc_override;
char *rc_eq;
int rc_max_rate;
int rc_min_rate;
float rc_buffer_aggressivity;
float rc_initial_cplx;
float rc_max_available_vbv_use;
float rc_min_vbv_overflow_use;
int rc_initial_buffer_occupancy;
int coder_type;
int context_model;
int lmin;
int lmax;
int frame_skip_threshold;
int frame_skip_factor;
int frame_skip_exp;
int frame_skip_cmp;
int trellis;
int min_prediction_order;
int max_prediction_order;
undefined field145_0x264;
undefined field146_0x265;
undefined field147_0x266;
undefined field148_0x267;
int64_t timecode_frame_start;
void (*rtp_callback)(struct AVCodecContext *, void *, int, int);
int rtp_payload_size;
int mv_bits;
int header_bits;
int i_tex_bits;
int p_tex_bits;
int i_count;
int p_count;
int skip_count;
int misc_bits;
int frame_bits;
char *stats_out;
char *stats_in;
int workaround_bugs;
int strict_std_compliance;
int error_concealment;
int debug;
int debug_mv;
int err_recognition;
undefined field169_0x2bc;
undefined field170_0x2bd;
undefined field171_0x2be;
undefined field172_0x2bf;
int64_t reordered_opaque;
struct AVHWAccel *hwaccel;
void *hwaccel_context;
uint64_t error[8];
int dct_algo;
int idct_algo;
int bits_per_coded_sample;
int bits_per_raw_sample;
int lowres;
struct AVFrame *coded_frame;
int thread_count;
int thread_type;
int active_thread_type;
int thread_safe_callbacks;
int (*execute)(struct AVCodecContext *, int (*)(struct AVCodecContext *, void *), void *, int *, int, int);
int (*execute2)(struct AVCodecContext *, int (*)(struct AVCodecContext *, void *, int, int), void *, int *, int);
void *thread_opaque;
int nsse_weight;
int profile;
int level;
enum AVDiscard skip_loop_filter;
enum AVDiscard skip_idct;
enum AVDiscard skip_frame;
uint8_t *subtitle_header;
int subtitle_header_size;
int error_rate;
struct AVPacket *pkt;
undefined field200_0x36c;
undefined field201_0x36d;
undefined field202_0x36e;
undefined field203_0x36f;
uint64_t vbv_delay;
int side_data_only_packets;
int initial_padding;
struct AVRational framerate;
struct AVRational pkt_timebase;
struct AVCodecDescriptor *codec_descriptor;
undefined field210_0x394;
undefined field211_0x395;
undefined field212_0x396;
undefined field213_0x397;
int64_t pts_correction_num_faulty_pts;
int64_t pts_correction_num_faulty_dts;
int64_t pts_correction_last_pts;
int64_t pts_correction_last_dts;
char *sub_charenc;
int sub_charenc_mode;
int skip_alpha;
int seek_preroll;
uint16_t *chroma_intra_matrix;
uint8_t *dump_separator;
char *codec_whitelist;
int bit_depth_luma;
int chroma_format_idc;
undefined field227_0x3dc;
undefined field228_0x3dd;
undefined field229_0x3de;
undefined field230_0x3df;
};
struct _DUSS_MSG_SATURATION_t
{
int8_t Value;
};
struct _DUSS_MSG_ZOOM_P_t
{
uint16_t Ratio;
};
struct _DUSS_MSG_ZOOM_S_t
{
uint16_t Step : 8;
uint16_t Direction : 1;
uint16_t Reserved : 7;
};
struct _DUSS_MSG_ZOOM_C_t
{
uint16_t Speed : 8;
uint16_t Direction : 1;
uint16_t Reserved : 7;
};
union anon_union_conflict21a2_for_OZoomRaUnion
{
DUSS_MSG_ZOOM_S_t ZoomS;
DUSS_MSG_ZOOM_P_t ZoomP;
DUSS_MSG_ZOOM_C_t ZoomC;
};
struct _DUSS_MSG_ZOOM_CTRL_MODE_t_
{
uint8_t DZoomMode : 2;
uint8_t Reserved1 : 1;
uint8_t DZoomCtrl : 1;
uint8_t OZoomMode : 2;
uint8_t Reserved2 : 1;
uint8_t OZoomCtrl : 1;
};
struct _DUSS_MSG_ZOOM_PARAM_t
{
DUSS_MSG_ZOOM_CTRL_MODE_t Mode;
union anon_union_conflict21a2_for_OZoomRaUnion OZoomRaUnion;
union anon_union_conflict21a2_for_OZoomRaUnion DZoomRaUnion;
};
struct _DUSS_MSG_CAPTURE_MODE_t
{
uint8_t Op;
uint8_t Mcap_Cnt;
uint8_t Ccap_Type;
uint8_t Ccap_Cnt;
uint16_t Ccap_Interval;
};
struct _DUSS_MSG_ISO_t
{
uint8_t Value;
};
struct _DUSS_MSG_SD_STATUS_t
{
uint8_t Inserted : 1;
uint8_t Status : 4;
uint8_t Reserved : 3;
};
struct _DUSS_MSG_SD_INFO_t
{
DUSS_MSG_SD_STATUS_t SdStatus;
uint32_t Capacity;
uint32_t SpareCap;
uint32_t RemainShots;
uint32_t RemainTime;
};
struct _DUSS_MSG_EXPO_MODE_t
{
uint8_t Mode;
uint8_t ScnMode;
};
struct _DUSS_MSG_FLIP
{
uint8_t type;
uint8_t angle;
};
struct _DUSS_MSG_SHARPNESS_t
{
int8_t Value;
};
struct _DUSS_MSG_CONTICAP_PARAM_t
{
uint8_t Type;
uint8_t Cnt;
uint16_t Interval;
};
struct _DUSS_MSG_P_STORAGE_FMT_t
{
uint8_t Fmt;
};
struct _DUSS_MSG_RECORD_t
{
uint8_t Op : 2;
uint8_t type : 4;
uint8_t Reserved : 2;
};
struct _DUSS_MSG_WB_t
{
uint8_t Mode;
uint8_t WbCt;
};
struct _DUSS_MSG_CONTRAST_t
{
int8_t Value;
};
struct _DUSS_MSG_SHUTTER_A_t
{
uint16_t Value : 15;
uint16_t Flag : 1;
uint8_t Decimal;
};
struct _DUSS_MSG_SHUTTER_SPEED_t
{
uint8_t Mode;
DUSS_MSG_SHUTTER_A_t ShutterA;
};
struct _DUSS_MSG_DIGITAL_EFFECT_t
{
uint8_t Effect;
};
struct _DUSS_MSG_EV_BIAS_t
{
uint8_t Bias;
};
struct _DUSS_MSG_V_STORAGE_FMT_t
{
uint8_t Fmt;
};
struct _DUSS_MSG_SINGLE_PLAY_CRTL_t
{
uint32_t FileIndex;
};
struct _DUSS_MSG_SCENE_MODE_t
{
uint8_t Mode;
};
struct _DUSS_MSG_SET_FOCUS_REGION_t
{
float x;
float y;
};
struct _DUSS_MSG_V_SS_CONFIG_t
{
uint8_t Op : 1;
uint8_t Reserved : 7;
};
struct _DUSS_MSG_V_FORMAT_t
{
uint8_t FSResolution;
uint8_t FrameRate;
uint8_t Fov;
DUSS_MSG_V_SS_CONFIG_t SSConfig;
uint8_t SSResolution;
};
struct _DUSS_MSG_P_SIZE_t
{
uint8_t Size;
uint8_t AspectRatio;
};
struct _DUSS_MSG_CAPTURE_t
{
uint8_t Op;
};
union anon_union_conflict2c9b_for_field_3
{
DUSS_MSG_APERTURE_t aperture;
DUSS_MSG_SHUTTER_SPEED_t shut_spd;
DUSS_MSG_ISO_t iso;
DUSS_MSG_EXPO_MODE_t expo_mode;
DUSS_MSG_EV_BIAS_t ev_bias;
DUSS_MSG_P_STORAGE_FMT_t p_storage;
DUSS_MSG_WB_t wb;
DUSS_MSG_SCENE_MODE_t scene_mode;
DUSS_MSG_DIGITAL_EFFECT_t digit_effect;
DUSS_MSG_P_SIZE_t p_size;
DUSS_MSG_SET_FOCUS_REGION_t focus;
DUSS_MSG_CAPTURE_MODE_t cap_mode;
DUSS_MSG_CONTICAP_PARAM_t conticap;
DUSS_MSG_V_STORAGE_FMT_t v_storage;
DUSS_MSG_V_FORMAT_t v_fmt;
DUSS_MSG_VIDEO_STANDARD_t v_std;
DUSS_MSG_WORKMODE_t work_mode;
DUSS_MSG_SINGLE_PLAY_CRTL_t play_ctrl_data;
DUSS_MSG_V_CRTL_t video_ctrl_data;
DUSS_MSG_SD_INFO_t sd_info;
DUSS_MSG_ZOOM_PARAM_t zoom_param;
DUSS_MSG_FLIP_t flip_mode;
DUSS_MSG_SHARPNESS_t sharpness;
DUSS_MSG_CONTRAST_t contrast;
DUSS_MSG_SATURATION_t saturation;
uint8_t lv_format;
DUSS_MSG_CAPTURE_t cap_photo;
DUSS_MSG_RECORD_t cap_video;
UAV_RECORD_MODE_t rec_mode;
uint8_t uav_low_power_mode;
uint8_t uav_power_policy;
uint8_t HDVT_pref;
uint8_t enc_roi;
uint8_t white_balance;
};
struct __gs_camera_cmd
{
uint16_t cmd_id;
uint16_t session_id;
uint8_t ack_result;
union anon_union_conflict2c9b_for_field_3 field_3;
undefined field4_0x16;
undefined field5_0x17;
void (*call_back_handler)(struct __gs_camera_cmd *);
};
struct bridge_io_pkt
{
uint32_t paddr;
uint32_t size;
uint8_t notify;
undefined field3_0x9;
undefined field4_0xa;
undefined field5_0xb;
};
struct gs_lv_pkt
{
void *align_vaddr;
int align_size;
void *vaddr;
void *payload_vaddr;
uint32_t payload_size;
bridge_io_pkt_t pkt;
};
struct uav_gimbal_ops
{
int (*ctrl_start)(void *);
int (*ctrl_racing_start)(void *);
int (*ctrl_stop)(void *);
int (*reset_init_value)(void *);
int (*turndown_start)(void *);
int (*turndown_stop)(void *);
int (*set_workmode)(void *, uint8_t);
int (*ctrl_set_racing_pitch_offset)(void *, int16_t);
int (*ctrl_set_racing_yaw_offset)(void *, int16_t);
int (*ctrl_set_racing_pitch_sense)(void *, float);
int (*ctrl_set_racing_yaw_sense)(void *, float);
int (*ctrl_get_state)(void *, uint8_t *);
};
struct duss_hal_icc_config_t
{
enum duss_hal_icc_role_t role;
enum duss_hal_icc_shm_type_t shm_type;
uint32_t shm_size;
};
struct RcOverride
{
int start_frame;
int end_frame;
int qscale;
float quality_factor;
};
struct uav_camera_ops
{
int (*send_camera_cmd)(void *, gs_camera_cmd_t *, _Bool);
int (*set_camera_param)(void *, uint8_t, uint8_t);
int (*get_rec_time)(void *, int *, int *);
};
struct duss_event_cmd_desc
{
duss_result_t (*req_cb)(struct duss_event_client *, duss_event_t *, void *);
duss_result_t (*ack_cb)(struct duss_event_client *, duss_event_t *, void *);
uint32_t flags;
};
struct cJSON
{
struct cJSON *next;
struct cJSON *prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
undefined field8_0x24;
undefined field9_0x25;
undefined field10_0x26;
undefined field11_0x27;
};
struct vr_device_ops
{
int (*get_video_scale)(void *, float *);
int (*set_video_scale)(void *, float);
int (*adjust_fov)(void *, uint32_t, int16_t, int16_t);
int (*set_brightness_value)(void *, uint8_t);
int (*get_brightness_value)(void *, uint8_t *);
int (*get_device_volume)(void *, uint8_t, uint32_t *);
int (*set_device_volume)(void *, uint8_t, uint32_t);
int (*calibrate_imu)(char *);
int (*initialize_imu)(void *, ...);
int (*destroy_imu)(void *, ...);
int (*start_imu)(void *, ...);
int (*stop_imu)(void *, ...);
int (*get_imu_init_status)(void *, ...);
int (*get_imu_attitude)(float *);
int (*format_sd)(void *);
int (*get_sd_info)(void *, gs_local_sd_info_t *);
int (*get_battery_info)(void *, DUSS_MSG_RC_BAT_INFO_t *);
int (*get_device_active_state)(void *, int);
int (*get_upgrade_info)(void *, _Bool *, uint8_t *, uint8_t *);
int (*av_in_us_get_brightness)(void *, uint8_t *);
int (*av_in_us_set_brightness)(void *, uint8_t);
int (*av_in_us_get_saturation)(void *, uint8_t *);
int (*av_in_us_set_saturation)(void *, uint8_t);
int (*reset_user_settings)(void *);
int (*event_tracking)(void *, int32_t, uint8_t, uint8_t);
void (*get_flight_stat)(uint8_t *);
void (*set_flight_stat)(uint8_t);
int (*watermarker_us_get_flag)(void *, uint8_t *);
int (*watermarker_us_set_flag)(void *, uint8_t);
int (*watermarker_us_reset)(void *);
};
struct pl_decoder_itf_t
{
int (*create)(pl_decoder_handle_t *, void *, void *);
int (*destroy)(pl_decoder_handle_t);
int (*init)(pl_decoder_handle_t);
int (*config)(pl_decoder_handle_t, void *);
int (*prepare)(pl_decoder_handle_t);
int (*release)(pl_decoder_handle_t);
int (*decode)(pl_decoder_handle_t, void *, uint32_t, void *, uint32_t *);
int (*push_frame)(pl_decoder_handle_t, void *, uint32_t);
int (*set_input_datacb)(pl_decoder_handle_t, void *, input_data_cb);
int (*set_output_datacb)(pl_decoder_handle_t, void *, output_data_cb);
};
struct duss_event_ack_identify
{
duss_msg_id_t msg_id;
uint16_t peer_host;
uint16_t seq_id;
uint16_t valid;
undefined field4_0xa;
undefined field5_0xb;
void *buffer_ptr;
uint32_t buffer_size;
};
struct vcm_ops
{
int (*register_channel_switch_callback)(void *, gs_video_channel_switch_callback_t, void *);
int (*register_channel_push_callback)(void *, gs_video_channel_push_callback_t, void *);
int (*query_current_channel)(void *, gs_video_channel_id_t *);
int (*enable_channel)(void *, gs_main_channel_id_t);
int (*disable_channel)(void *, gs_main_channel_id_t);
int (*enable_sub_channel)(void *, gs_video_channel_id_t *);
int (*disable_sub_channel)(void *, gs_video_channel_id_t *);
int (*local_playback_on)(void *, char *);
int (*local_playback_off)(void *);
int (*rc_setting_on)(void *);
int (*rc_setting_off)(void *);
};
struct AVOptionRange
{
char *str;
undefined field1_0x4;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
double value_min;
double value_max;
double component_min;
double component_max;
int is_range;
undefined field10_0x2c;
undefined field11_0x2d;
undefined field12_0x2e;
undefined field13_0x2f;
};
struct debug_osd_item
{
char *name;
int value;
};
struct debug_cam_osd_info
{
debug_osd_item_t items[26];
int n_items;
};
struct AVPanScan
{
int id;
int width;
int height;
int16_t position[3][2];
};
struct AVProfile
{
int profile;
char *name;
};
struct loc_channel_addr
{
uint16_t direct_host;
};
struct __gs_common_cmd
{
duss_msg_id_t msg_id;
uint16_t receiver_id;
uint16_t session_id;
uint8_t ack_result;
undefined field4_0x9;
undefined field5_0xa;
undefined field6_0xb;
uint8_t *msg_buf;
uint32_t msg_len;
uint8_t *resp_msg_buf;
uint32_t resp_msg_len;
void (*call_back_handler)(struct __gs_common_cmd *);
};
struct duss_hal_mem_config_t
{
uint32_t reserved;
};
struct wl_channel_addr
{
char *ifx;
uint16_t loc_port;
undefined field2_0x6;
undefined field3_0x7;
char *address;
uint16_t rmt_port;
uint16_t mtu;
_Bool is_server;
duss_wl_prot_type_t protocol;
duss_wl_prio_t priority;
undefined field10_0x13;
int flags;
};
struct AVDeviceCapabilitiesQuery
{
};
struct __sbuf
{
uchar *_base;
int _size;
};
struct vdec_video_file_info
{
uint32_t strm_type;
uint32_t total_time;
uint32_t frame_interval;
uint32_t fps_numerator;
uint32_t fps_denorm;
uint32_t width;
uint32_t height;
int32_t rotation;
enum DUSS_FILE_FORMAT video_format;
};
struct fd_set
{
ulong fds_bits[32];
};
struct duss_mb_client
{
uint16_t this_host;
undefined field1_0x2;
undefined field2_0x3;
duss_result_t (*msg_cb)(struct duss_mb_client *, uint16_t, uint16_t, duss_msg_attrib_t, duss_msg_id_t, uint8_t *, int32_t, void *);
void *msg_cb_userdata;
duss_result_t (*filter_cb)(struct duss_mb_client *, struct duss_mb_route_item_t *, uint16_t, uint16_t, uint16_t, duss_msg_attrib_t, duss_msg_id_t, uint8_t *, int32_t, void *);
void *filter_cb_userdata;
struct duss_mb_route_table_t *route_table;
struct duss_osal_mutex_handle_t *lock;
struct duss_osal_task_handle_t *task;
int fd_max;
struct fd_set fds;
_Bool finish;
undefined field13_0xa5;
undefined field14_0xa6;
undefined field15_0xa7;
int channel_num;
void *channel[32];
int target_num;
void *target[64];
};
struct AVPacket
{
struct AVBufferRef *buf;
undefined field1_0x4;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
struct AVPacketSideData *side_data;
int side_data_elems;
int duration;
void (*destruct)(struct AVPacket *);
void *priv;
undefined field16_0x3c;
undefined field17_0x3d;
undefined field18_0x3e;
undefined field19_0x3f;
int64_t pos;
int64_t convergence_duration;
_Bool is_last_section;
undefined field23_0x51;
undefined field24_0x52;
undefined field25_0x53;
int32_t nal_total_size;
};
struct icc_channel_addr
{
char *obj_name;
char *snd_chn;
char *rcv_chn;
struct duss_hal_icc_config_t cfg;
};
struct duss_mb_route_table_t
{
void *priv_data;
int target_size;
struct duss_mb_route_item_t *targets[128];
};
struct AVCodecInternal
{
};
struct pl_decoder_t
{
struct pl_decoder_itf_t *itf;
void *context;
struct AVRational time_base;
void *me;
_Bool running;
undefined field5_0x15;
undefined field6_0x16;
undefined field7_0x17;
struct duss_osal_msgq_handle_t *packet_queue;
struct duss_osal_msgq_handle_t *frame_queue;
struct duss_osal_task_handle_t *decode_task;
};
struct gs_local_panorama_info
{
char file_path[256];
uint8_t file_type;
undefined field2_0x101;
undefined field3_0x102;
undefined field4_0x103;
int gfxLayerFd;
};
struct duss_hal_i2c_config_t
{
int baudrate;
duss_i2c_addr_len_t addr_len;
undefined field2_0x5;
undefined field3_0x6;
undefined field4_0x7;
};
struct duss_hal_storage_params
{
enum duss_storage_client_type_t client_id;
void *user_data;
void (*callback)(struct duss_hal_storage_info *, void *);
};
struct AVBufferRef
{
struct AVBuffer *buffer;
uint8_t *data;
int size;
};
struct sem_t
{
uint count;
};
struct duss_osal_mutex_handle_t
{
struct duss_osal_mutex_attrib_t attrib;
struct sem_t sema;
};
struct stick_mode_self_define_t
{
uint8_t ch_reverse : 1;
uint8_t stick_channel : 7;
};
struct rc_set_stick_mode_pack_t
{
uint8_t stick_mode;
struct stick_mode_self_define_t ch_setup[0];
};
struct duss_osal_task_attrib_t
{
char *name;
duss_osal_task_entry_t entry;
void *param;
enum duss_osal_priority_t priority;
uint32_t stack_size;
uint32_t time_slice;
_Bool detached;
undefined field7_0x19;
undefined field8_0x1a;
undefined field9_0x1b;
uint32_t affinity_mask;
};
struct SwrContext
{
};
struct AVHWAccel
{
char *name;
enum AVMediaType type;
enum AVCodecID id;
enum AVPixelFormat pix_fmt;
int capabilities;
struct AVHWAccel *next;
int (*alloc_frame)(struct AVCodecContext *, struct AVFrame *);
int (*start_frame)(struct AVCodecContext *, uint8_t *, uint32_t);
int (*decode_slice)(struct AVCodecContext *, uint8_t *, uint32_t);
int (*end_frame)(struct AVCodecContext *);
int frame_priv_data_size;
void (*decode_mb)(struct MpegEncContext *);
int (*init)(struct AVCodecContext *);
int (*uninit)(struct AVCodecContext *);
int priv_data_size;
};
struct netlink_channel_addr
{
uint16_t host_pid;
uint16_t group;
uint16_t target_pid;
};
struct duss_hal_uart_config_t
{
int baudrate;
duss_uart_parity_t parity;
duss_uart_stopbit_t stopbit;
duss_uart_wordlen_t wordlen;
undefined field4_0x7;
};
struct uart_channel_addr
{
char *obj_name;
struct duss_hal_uart_config_t cfg;
};
struct usb_channel_addr
{
char *device;
uint32_t vid;
uint32_t pid;
uint16_t interface;
uint16_t altsetting;
uint8_t in_ep;
uint8_t out_ep;
undefined field7_0x12;
undefined field8_0x13;
};
struct duss_hal_spi_config_t
{
int mode;
int baudrate;
int lsb_first;
int bits_per_word;
};
struct spi_channel_addr
{
char *obj_name;
struct duss_hal_spi_config_t cfg;
};
struct duss_hal_can_config_t
{
duss_mb_can_chip_t chip;
undefined field1_0x1;
undefined field2_0x2;
undefined field3_0x3;
uint32_t baudrate;
duss_mb_host_id_t target;
undefined field6_0xa;
undefined field7_0xb;
uint32_t rx;
uint32_t tx;
};
struct can_channel_addr
{
char *obj_name;
struct duss_hal_can_config_t cfg;
};
struct duss_hal_bulk_config_t
{
};
struct bulk_channel_addr
{
char *obj_name;
struct duss_hal_bulk_config_t cfg;
};
struct i2c_channel_addr
{
char *obj_name;
struct duss_hal_i2c_config_t cfg;
};
union anon_union_conflict1232_for_channel
{
struct loc_channel_addr local;
struct ip_channel_addr ip;
struct wl_channel_addr wl;
struct uart_channel_addr uart;
struct can_channel_addr can;
struct i2c_channel_addr i2c;
struct spi_channel_addr spi;
struct hpi_channel_addr hpi;
struct usb_channel_addr usb;
struct usbacc_channel_addr usbacc;
struct icc_channel_addr icc;
struct netlink_channel_addr netlink;
struct bulk_channel_addr bulk;
};
struct AVPicture
{
uint8_t *data[8];
int linesize[8];
};
struct debug_cp_osd_info
{
debug_osd_item_t items[4];
int n_items;
};
struct AVDictionary
{
};
struct duss_hal_mem_param_t
{
uint32_t reserved;
};
struct ion_info
{
duss_hal_obj_handle_t mem_obj;
struct duss_hal_mem_config_t mem_cfg;
struct duss_hal_mem_param_t mem_param;
duss_hal_mem_handle_t ion_buf;
void *ion_virt_addr;
uint32_t ion_len;
struct duss_osal_mutex_handle_t *ion_mutex;
uint32_t start_addr;
uint32_t stop_addr;
uint32_t size_waste;
};
struct audio_resampler_t
{
struct SwrContext *swr_ctx;
int src_rate;
int dst_rate;
int src_nb_channels;
int dst_nb_channels;
enum AVSampleFormat src_sample_fmt;
enum AVSampleFormat dst_sample_fmt;
};
struct pcm
{
int fd;
uint flags;
uint running : 1;
uint prepared : 1;
undefined field4_0x9;
undefined field5_0xa;
undefined field6_0xb;
int underruns;
uint buffer_size;
uint boundary;
char error[128];
struct pcm_config config;
struct snd_pcm_mmap_status *mmap_status;
struct snd_pcm_mmap_control *mmap_control;
struct snd_pcm_sync_ptr *sync_ptr;
void *mmap_buffer;
uint noirq_frames_per_msec;
int wait_for_avail_min;
struct timespec tstamp;
int (*ev_callback)(int, void *);
void *ev_params;
char ev_running;
undefined field22_0xed;
undefined field23_0xee;
undefined field24_0xef;
pthread_t ev_thread;
char chg_3le_to_le;
undefined field27_0xf5;
undefined field28_0xf6;
undefined field29_0xf7;
char *buffer_4le;
uint size_4le;
};
struct duss_mb_filter_t
{
uint16_t source;
uint16_t target;
duss_msg_id_t msg_id;
duss_msg_attrib_t attrib;
undefined field4_0xa;
undefined field5_0xb;
};
struct duss_hal_obj_dev_t
{
char *name;
duss_hal_state_t obj_state;
duss_hal_class_t obj_class;
uint16_t obj_index;
int32_t obj_refcnt;
struct duss_osal_mutex_handle_t *obj_lock;
struct duss_osal_mutex_handle_t *app_lock;
duss_result_t (*open)(duss_hal_obj_handle_t, void *);
duss_result_t (*close)(duss_hal_obj_handle_t);
duss_result_t (*set_cfg)(duss_hal_obj_handle_t, void *);
duss_result_t (*get_cfg)(duss_hal_obj_handle_t, void *);
duss_result_t (*start)(duss_hal_obj_handle_t, void *);
duss_result_t (*stop)(duss_hal_obj_handle_t, void *);
};
struct duss_hal_obj
{
struct duss_hal_obj_dev_t dev;
void *dev_class;
void *dev_priv;
};
struct duss_event
{
duss_msg_id_t msg_id;
uint16_t peer_host;
duss_msg_attrib_t attrib;
uint16_t seq_id;
undefined field4_0xa;
undefined field5_0xb;
uint32_t msg_len;
uint8_t msg_buf[0];
};
struct common_cmd_ops
{
int (*send_common_cmd)(void *, gs_common_cmd_t *, _Bool);
};
struct local_playback_ops
{
int (*open)(void *, char *);
int (*close)(void *);
int (*stop)(void *);
int (*pause)(void *);
int (*resume)(void *);
int (*seek)(void *, uint32_t);
int (*seek_time)(void *, uint32_t);
int (*get_duration)(char *, uint32_t *);
int (*get_cur_time)(void *, uint32_t *, uint32_t *);
int (*get_state)(void *, uint32_t *);
int (*get_file_info)(char *, vdec_video_file_info_t *);
int (*is_playing)(void *, _Bool *);
int (*delete_file)(char *);
};
struct rc_ops
{
int (*get_version)(void *, char *, char *, int8_t *);
int (*get_version_async)(void *);
int (*set_subtrim)(void *, uint8_t, int16_t);
int (*set_subtrim_async)(void *, uint8_t, int16_t);
int (*get_subtrim)(void *, struct rc_set_all_st_t *, int8_t *);
int (*get_subtrim_async)(void *);
int (*set_reverse)(void *, uint8_t, uint8_t);
int (*get_reverse)(void *, union rc_set_reverse_t *, int8_t *);
int (*get_reverse_async)(void *);
int (*set_endpoint)(void *, uint8_t, uint8_t, uint8_t);
int (*set_endpoint_async)(void *, uint8_t, uint8_t, uint8_t);
int (*get_endpoint)(void *, struct rc_set_all_ep_t *, int8_t *);
int (*get_endpoint_async)(void *);
int (*set_func_mode)(void *, uint8_t, uint8_t, uint8_t);
int (*get_func_mode)(void *, struct rc_set_function_pack_t *);
int (*set_stick_mode)(void *, uint8_t);
int (*get_stick_mode)(void *, struct rc_set_stick_mode_pack_t *, int8_t *);
int (*get_stick_mode_ext)(void *, uint8_t *);
int (*get_stick_mode_async)(void *);
int (*set_stick_cali)(void *, uint8_t, uint8_t, uint8_t *);
int (*get_stick_cali)(void *, uint8_t, int8_t *, int8_t *, int8_t *, int8_t *);
int (*set_warning)(void *, struct rc_set_warning_mode_pack_t);
int (*get_warning)(void *, struct rc_set_warning_mode_pack_t *);
int (*monitor)(void *, struct rc_monitor_pack_t *);
int (*reset_all_get_status)(void *);
int (*reset_default_async)(void *, uint8_t);
uint8_t (*get_lock_state)(void *);
uint8_t (*get_link_state)(void *);
};
struct __gs_gui_config
{
gs_queue_t *osd_gen_data_queue;
gs_queue_t *osd_home_data_queue;
gs_queue_t *file_del_queue;
gs_queue_t *camera_stat_queue;
gs_queue_t *camera_cap_param_queue;
gs_queue_t *camera_pb_param_queue;
gs_queue_t *camera_file_info_queue;
gs_queue_t *camera_dcf_abstract_queue;
gs_queue_t *camera_fov_param_queue;
gs_queue_t *rc_bat_stat_queue;
gs_queue_t *signal_quality_queue;
gs_queue_t *uav_bat_common_info_queue;
gs_queue_t *sdr_wl_env_queue;
gs_queue_t *sdr_uav_data_queue;
gs_queue_t *sdr_gnd_data_queue;
gs_queue_t *rc_param_queue;
gs_queue_t *rc_pushed_gps_queue;
gs_queue_t *racing_drone_fc_osd_queue;
_Bool *touchpadLockState;
void *ctx;
ext_fc_ops_t ext_fc_ops;
vr_device_ops_t vdev_ops;
local_playback_ops_t local_pb_ops;
uav_camera_ops_t uav_cam_ops;
uav_gimbal_ops_t gimbal_ops;
modem_ops_t modem_ops;
rc_ops_t rc_ops;
vcm_ops_t vcm_ops;
common_cmd_ops_t cmn_cmd_ops;
int (*get_codec_debug_osd_info)(void *, debug_codec_osd_info_t *);
int (*get_cam_debug_osd_info)(void *, debug_cam_osd_info_t *);
int (*get_temp_debug_osd_info)(void *, debug_temp_osd_info_t *);
int (*get_uav_max_temp)(void *, int32_t *);
int (*get_cp_debug_osd_info)(void *, debug_cp_osd_info_t *);
int (*set_racing_enc_strategy)(void *, uint8_t);
int (*is_camera_in_record)(void *, int *);
int (*get_cparm_version)(void *, char *);
int (*get_dsp_version)(void *, char *);
int (*get_flight_time)(void *, uint64_t *);
int (*get_arm_status)(void *, uint8_t *);
int (*clear_adb_log_info)(void *);
int (*reset_camera_param)(void *);
int (*set_low_power_mode)(void *, _Bool);
int (*get_uav_version)(void *, char *, char *);
int (*get_uav_hardware_version)(void *, char *);
int (*get_uav_power_status)(void *, uint8_t *, uint8_t *);
int (*get_uav_camera_type)(void *, uint8_t *);
int (*get_liveview_fps)(void *, uint8_t *);
int (*fbdev_open)(void);
int (*fbdev_disp_frame)(int);
int (*fbdev_close)(void);
int (*record_liveview)(void *, _Bool);
int (*get_liveview_state)(void *, int *);
record_mode_t (*get_liveview_record_mode)(void *);
int (*set_liveview_record_mode)(void *, record_mode_t);
int (*get_liveview_record_enable)(void *);
int (*set_liveview_record_enable)(void *, _Bool, enum Record_sender);
int (*get_liveview_record_time)(void *, int *);
int (*enable_audio_liveview)(void *, _Bool);
int (*get_frame_delay_e2e)(void *, uint16_t *);
int (*get_chnl_status)(void *, uint16_t *);
int (*get_pigeon_battery_info)(void *, gs_battery_voltage_t *);
int (*get_sd_status)(void *, int, uint8_t *, uint32_t *, uint32_t *);
int (*play_pwm_buzzer)(void *, int, int, int);
int (*buzzer_enable_bat)(void *, _Bool);
};
struct AVCodecParserContext
{
void *priv_data;
struct AVCodecParser *parser;
int64_t frame_offset;
int64_t cur_offset;
int64_t next_frame_offset;
int pict_type;
int repeat_pict;
int64_t pts;
int64_t dts;
int64_t last_pts;
int64_t last_dts;
int fetch_timestamp;
int cur_frame_start_index;
int64_t cur_frame_offset[4];
int64_t cur_frame_pts[4];
int64_t cur_frame_dts[4];
int flags;
undefined field17_0xb4;
undefined field18_0xb5;
undefined field19_0xb6;
undefined field20_0xb7;
int64_t offset;
int64_t cur_frame_end[4];
int key_frame;
undefined field24_0xe4;
undefined field25_0xe5;
undefined field26_0xe6;
undefined field27_0xe7;
int64_t convergence_duration;
int dts_sync_point;
int dts_ref_dts_delta;
int pts_dts_delta;
undefined field32_0xfc;
undefined field33_0xfd;
undefined field34_0xfe;
undefined field35_0xff;
int64_t cur_frame_pos[4];
int64_t pos;
int64_t last_pos;
int duration;
enum AVFieldOrder field_order;
enum AVPictureStructure picture_structure;
int output_picture_number;
};
struct duss_osal_msgq_attrib_t
{
char *name;
uint32_t buf_size;
};
struct duss_osal_msgq_handle_t
{
struct duss_osal_msgq_attrib_t attrib;
struct pthread_mutex_t r_mutex;
struct pthread_mutex_t s_mutex;
struct pthread_cond_t r_cond;
struct pthread_cond_t s_cond;
uint32_t buf_size;
uint32_t head;
uint32_t tail;
uint8_t *buffer;
};
struct duss_mb_route_item_t
{
uint8_t status;
char name[16];
undefined field2_0x11;
uint16_t target;
duss_mb_channel_t access_channel;
uint8_t distance;
duss_mb_pack_ver_t pack_ver;
undefined field7_0x17;
union anon_union_conflict1232_for_channel channel;
int filter_size;
struct duss_mb_filter_t *filters;
};
struct rc_monitor_pack_t
{
uint8_t channel_num;
uint16_t channel_val[0];
};
struct AVFormatInternal
{
};
struct debug_osd_info
{
debug_osd_item_t items[15];
int n_items;
};
struct AVOptionRanges
{
struct AVOptionRange **range;
int nb_ranges;
int nb_components;
};
struct snd_pcm_sync_ptr
{
};
union anon_union_conflictac8f4_for_default_val
{
int64_t i64;
double dbl;
char *str;
struct AVRational q;
};
struct gs_playback_listener
{
void *ctx;
int (*event_cb)(void *, playback_event_t);
};
struct gs_local_video_info
{
char video_path[256];
char audio_path[256];
};
union anon_union_conflict3b31_for_field_1
{
_Bool plugin;
uint8_t cam_workmode;
uint8_t cam_pbmode;
uint8_t cam_model;
_Bool wl_link;
gs_main_channel_id_t chnl_id;
gs_video_channel_id_t sub_chnl_id;
gs_local_video_info_t video_info;
uint32_t video_index;
_Bool is_pal;
gs_local_panorama_info_t pano_info;
};
struct gs_video_channel_message
{
enum gs_video_channel_message_id_t msg_id;
union anon_union_conflict3b31_for_field_1 field_1;
};
struct snd_pcm_mmap_status
{
};
struct audio_dec
{
void *gs_info;
void *cb_ctx;
int (*cb_func)(void *, dec_event_t);
audio_pb_state_t audio_pb_state;
int codec_id;
int sample_rate;
int channels;
int sample_fmt;
pl_decoder_handle_t hnd;
struct pl_decoder_itf_t *itf;
_Bool b_running;
_Bool b_eos;
undefined field12_0x2a;
undefined field13_0x2b;
struct duss_osal_task_handle_t *thread_dec;
_Bool b_aout_running;
undefined field16_0x31;
undefined field17_0x32;
undefined field18_0x33;
struct duss_osal_task_handle_t *thread_aout;
audio_resampler_ptr resampler;
uint8_t *resample_buffer;
int resample_buf_size;
int frames_received;
int frames_decoded;
FILE *f_a_out;
struct duss_osal_mutex_handle_t *list_mutex;
struct duss_list_head frame_list;
int num_in_list;
struct duss_osal_mutex_handle_t *pcm_mutex;
struct duss_list_head pcm_list;
int num_pcm;
_Bool b_started;
undefined field33_0x71;
undefined field34_0x72;
undefined field35_0x73;
struct timespec tstamp;
int samples_played;
_Bool b_first_frm_sent;
undefined field39_0x81;
undefined field40_0x82;
undefined field41_0x83;
undefined field42_0x84;
undefined field43_0x85;
undefined field44_0x86;
undefined field45_0x87;
int64_t play_tm_offset_ms;
int64_t play_tm_ms;
};
struct AVCodecDefault
{
};
struct AVCodecTag
{
};
struct AVProgram
{
int id;
int flags;
enum AVDiscard discard;
uint *stream_index;
uint nb_stream_indexes;
struct AVDictionary *metadata;
int program_num;
int pmt_pid;
int pcr_pid;
undefined field9_0x24;
undefined field10_0x25;
undefined field11_0x26;
undefined field12_0x27;
int64_t start_time;
int64_t end_time;
int64_t pts_wrap_reference;
int pts_wrap_behavior;
undefined field17_0x44;
undefined field18_0x45;
undefined field19_0x46;
undefined field20_0x47;
};
struct AVInputFormat
{
char *name;
char *long_name;
int flags;
char *extensions;
struct AVCodecTag **codec_tag;
struct AVClass *priv_class;
char *mime_type;
struct AVInputFormat *next;
int raw_codec_id;
int priv_data_size;
int (*read_probe)(struct AVProbeData *);
int (*read_header)(struct AVFormatContext *);
int (*read_packet)(struct AVFormatContext *, struct AVPacket *);
int (*read_close)(struct AVFormatContext *);
int (*read_seek)(struct AVFormatContext *, int, int64_t, int);
int64_t (*read_timestamp)(struct AVFormatContext *, int, int64_t *, int64_t);
int (*read_play)(struct AVFormatContext *);
int (*read_pause)(struct AVFormatContext *);
int (*read_seek2)(struct AVFormatContext *, int, int64_t, int64_t, int64_t, int);
int (*get_device_list)(struct AVFormatContext *, struct AVDeviceInfoList *);
int (*create_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
int (*free_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
};
struct anon_struct_conflict69b3c_for_info
{
int64_t last_dts;
int64_t duration_gcd;
int duration_count;
undefined field3_0x14;
undefined field4_0x15;
undefined field5_0x16;
undefined field6_0x17;
int64_t rfps_duration_sum;
double *duration_error[2][373];
undefined field9_0x24;
undefined field10_0x25;
undefined field11_0x26;
undefined field12_0x27;
int64_t codec_info_duration;
int64_t codec_info_duration_fields;
int found_decoder;
undefined field16_0x3c;
undefined field17_0x3d;
undefined field18_0x3e;
undefined field19_0x3f;
int64_t last_duration;
int64_t fps_first_dts;
int fps_first_dts_idx;
undefined field23_0x54;
undefined field24_0x55;
undefined field25_0x56;
undefined field26_0x57;
int64_t fps_last_dts;
int fps_last_dts_idx;
undefined field29_0x64;
undefined field30_0x65;
undefined field31_0x66;
undefined field32_0x67;
};
struct duss_storage_client
{
_Bool start_flag;
undefined field1_0x1;
undefined field2_0x2;
undefined field3_0x3;
struct duss_hal_storage_params param;
struct duss_hal_storage_info info;
};
struct duss_osal_timer_handle_t
{
struct duss_osal_timer_attrib_t attrib;
pthread_t thread;
int32_t repeat_cnt;
_Bool suspend;
_Bool restart;
undefined field5_0x22;
undefined field6_0x23;
struct sem_t sema;
struct sem_t lock;
struct timespec saved_time;
};
struct rc_set_warning_mode_pack_t
{
uint8_t ext_flag : 1;
uint8_t warning_tid : 4;
uint8_t warning_mode : 3;
uint16_t ext_val;
};
struct AVClass
{
char *class_name;
char *(*item_name)(void *);
struct AVOption *option;
int version;
int log_level_offset_offset;
int parent_log_context_offset;
void *(*child_next)(void *, void *);
AVClass *(*child_class_next)(struct AVClass *);
enum AVClassCategory category;
AVClassCategory (*get_category)(void *);
int (*query_ranges)(struct AVOptionRanges **, void *, char *, int);
};
struct AVIOInterruptCB
{
int (*callback)(void *);
void *opaque;
};
struct AVPacketSideData
{
uint8_t *data;
int size;
enum AVPacketSideDataType type;
};
struct product_shm_info
{
uint16_t frm_width;
uint16_t frm_height;
uint8_t fps;
uint8_t enc_strategy;
uint16_t lcdc_underflow_cnt;
uint32_t enc_sto_frm_dropped;
uint32_t enc_lv_frm_dropped;
uint32_t mipi_csi_frm_dropped;
uint32_t display_frm_dropped;
uint64_t audio_pts;
uint32_t local_fps_num;
uint32_t local_fps_den;
uint16_t frame_delay_e2e;
uint16_t cam_frame_interval;
uint16_t outliner_frame_interval;
uint16_t outliner_frame_interval_cnt;
uint16_t max_frame_delay_e2e;
uint16_t min_frame_delay_e2e;
uint16_t avg_frame_delay_e2e;
uint16_t if_switch;
uint8_t if_change_pipe;
uint8_t if_pb_pause;
uint8_t liveview_pipeline_running;
uint8_t avIn_pipeline_running;
uint8_t avIn_stream_type;
uint8_t pb_flush;
uint8_t disp_pannel_need_reset;
undefined field27_0x3f;
};
struct debug_temp_osd_info
{
debug_osd_item_t items[11];
int n_items;
};
struct pl_muxer_itf_t
{
int (*create)(p1_muxer_handle_t *, void *, void *);
int (*destroy)(p1_muxer_handle_t);
int (*prepare)(p1_muxer_handle_t);
int (*release)(p1_muxer_handle_t);
int (*add_stream)(p1_muxer_handle_t, enum AVCodecID, void *);
int (*write)(p1_muxer_handle_t, void *, uint32_t, int);
void *(*get_stream)(p1_muxer_handle_t, uint);
int (*config_stream)(p1_muxer_handle_t, enum pl_muxer_config_index_t, void *);
};
struct AVFrame
{
uint8_t *data[8];
int linesize[8];
uint8_t **extended_data;
int width;
int height;
int nb_samples;
int format;
int key_frame;
enum AVPictureType pict_type;
uint8_t *base[8];
struct AVRational sample_aspect_ratio;
undefined field11_0x84;
undefined field12_0x85;
undefined field13_0x86;
undefined field14_0x87;
int64_t pts;
int64_t pkt_pts;
int64_t pkt_dts;
int coded_picture_number;
int display_picture_number;
int quality;
int reference;
int8_t *qscale_table;
int qstride;
int qscale_type;
uint8_t *mbskip_table;
int16_t *motion_val[2];
uint32_t *mb_type;
short *dct_coeff;
int8_t *ref_index[2];
void *opaque;
undefined field31_0xdc;
undefined field32_0xdd;
undefined field33_0xde;
undefined field34_0xdf;
uint64_t error[8];
int type;
int repeat_pict;
int interlaced_frame;
int top_field_first;
int palette_has_changed;
int buffer_hints;
struct AVPanScan *pan_scan;
undefined field43_0x13c;
undefined field44_0x13d;
undefined field45_0x13e;
undefined field46_0x13f;
int64_t reordered_opaque;
void *hwaccel_picture_private;
struct AVCodecContext *owner;
void *thread_opaque;
uint8_t motion_subsample_log2;
undefined field52_0x155;
undefined field53_0x156;
undefined field54_0x157;
int sample_rate;
undefined field56_0x15c;
undefined field57_0x15d;
undefined field58_0x15e;
undefined field59_0x15f;
uint64_t channel_layout;
struct AVBufferRef *buf[8];
struct AVBufferRef **extended_buf;
int nb_extended_buf;
struct AVFrameSideData **side_data;
int nb_side_data;
int flags;
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
enum AVColorSpace colorspace;
enum AVChromaLocation chroma_location;
int64_t best_effort_timestamp;
int64_t pkt_pos;
int64_t pkt_duration;
struct AVDictionary *metadata;
int decode_error_flags;
int channels;
int pkt_size;
struct AVBufferRef *qp_table_buf;
undefined field80_0x1dc;
undefined field81_0x1dd;
undefined field82_0x1de;
undefined field83_0x1df;
};
struct vdec_local_player
{
gs_playback_listener_t pb_listener;
void *gs_info;
uint32_t state;
uint32_t cur_time;
_Bool b_pending_seek;
undefined field5_0x15;
undefined field6_0x16;
undefined field7_0x17;
uint32_t seek_pos;
struct AVFormatContext *fmt_ctx;
_Bool b_f_eof;
undefined field11_0x21;
undefined field12_0x22;
undefined field13_0x23;
int audio_stream_index;
audio_dec_t *adec;
_Bool b_a_eos;
undefined field17_0x2d;
undefined field18_0x2e;
undefined field19_0x2f;
int video_stream_index;
cp_vdec_t *vdec;
_Bool b_v_eos;
_Bool b_running;
undefined field24_0x3a;
undefined field25_0x3b;
struct duss_osal_task_handle_t *task_player;
struct duss_osal_mutex_handle_t *vmutex;
vdec_video_file_info_t file_info;
int64_t first_pts;
};
struct AVDeviceInfoList
{
};
struct AVCodecDescriptor
{
enum AVCodecID id;
enum AVMediaType type;
char *name;
char *long_name;
int props;
char **mime_types;
};
struct AVFormatContext
{
struct AVClass *av_class;
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
void *priv_data;
struct AVIOContext *pb;
int ctx_flags;
uint nb_streams;
struct AVStream **streams;
char filename[1024];
int64_t start_time;
int64_t duration;
int bit_rate;
uint packet_size;
int max_delay;
int flags;
uint probesize;
int max_analyze_duration;
uint8_t *key;
int keylen;
uint nb_programs;
struct AVProgram **programs;
enum AVCodecID video_codec_id;
enum AVCodecID audio_codec_id;
enum AVCodecID subtitle_codec_id;
uint max_index_size;
uint max_picture_buffer;
uint nb_chapters;
struct AVChapter **chapters;
struct AVDictionary *metadata;
int64_t start_time_realtime;
int fps_probe_size;
int error_recognition;
struct AVIOInterruptCB interrupt_callback;
int debug;
undefined field34_0x494;
undefined field35_0x495;
undefined field36_0x496;
undefined field37_0x497;
int64_t max_interleave_delta;
int strict_std_compliance;
int event_flags;
int max_ts_probe;
int avoid_negative_ts;
int ts_id;
int audio_preload;
int max_chunk_duration;
int max_chunk_size;
int use_wallclock_as_timestamps;
int avio_flags;
enum AVDurationEstimationMethod duration_estimation_method;
undefined field50_0x4cc;
undefined field51_0x4cd;
undefined field52_0x4ce;
undefined field53_0x4cf;
int64_t skip_initial_bytes;
uint correct_ts_overflow;
int seek2any;
int flush_packets;
int probe_score;
int format_probesize;
char *codec_whitelist;
char *format_whitelist;
struct AVPacketList *packet_buffer;
struct AVPacketList *packet_buffer_end;
undefined field64_0x4fc;
undefined field65_0x4fd;
undefined field66_0x4fe;
undefined field67_0x4ff;
int64_t data_offset;
struct AVPacketList *raw_packet_buffer;
struct AVPacketList *raw_packet_buffer_end;
struct AVPacketList *parse_queue;
struct AVPacketList *parse_queue_end;
int raw_packet_buffer_remaining_size;
undefined field74_0x51c;
undefined field75_0x51d;
undefined field76_0x51e;
undefined field77_0x51f;
int64_t offset;
struct AVRational offset_timebase;
struct AVFormatInternal *internal;
int io_repositioned;
struct AVCodec *video_codec;
struct AVCodec *audio_codec;
struct AVCodec *subtitle_codec;
int metadata_header_padding;
void *opaque;
int (*control_message_cb)(struct AVFormatContext *, int, void *, size_t);
int64_t output_ts_offset;
int64_t max_analyze_duration2;
int64_t probesize2;
uint8_t *dump_separator;
undefined field92_0x56c;
undefined field93_0x56d;
undefined field94_0x56e;
undefined field95_0x56f;
};
struct duss_event_client
{
uint16_t this_host;
undefined field1_0x2;
undefined field2_0x3;
duss_mb_client_handle_t mb_client_obj;
struct duss_osal_msgq_handle_t *msgq;
struct duss_osal_task_handle_t *task;
struct duss_osal_mutex_handle_t *wait_ack_mutex;
struct duss_osal_event_handle_t *wait_ack_event;
duss_event_ack_identify_t wait_ack_list[32];
uint32_t wait_ack_cnt;
_Bool finish;
undefined field11_0x29d;
undefined field12_0x29e;
undefined field13_0x29f;
duss_event_cmd_desc_t *cmd_desc_tbl[79];
uint32_t cmd_desc_num[79];
void *userdata;
};
struct MpegEncContext
{
};
struct AVPacketList
{
struct AVPacket pkt;
struct AVPacketList *next;
undefined field2_0x5c;
undefined field3_0x5d;
undefined field4_0x5e;
undefined field5_0x5f;
};
struct AVProbeData
{
char *filename;
uchar *buf;
int buf_size;
char *mime_type;
};
struct AVStream
{
int index;
int id;
struct AVCodecContext *codec;
void *priv_data;
struct AVFrac pts;
struct AVRational time_base;
int64_t start_time;
int64_t duration;
int64_t nb_frames;
int disposition;
enum AVDiscard discard;
struct AVRational sample_aspect_ratio;
struct AVDictionary *metadata;
struct AVRational avg_frame_rate;
undefined field14_0x64;
undefined field15_0x65;
undefined field16_0x66;
undefined field17_0x67;
struct AVPacket attached_pic;
struct AVPacketSideData *side_data;
int nb_side_data;
int event_flags;
struct anon_struct_conflict69b3c_for_info *info;
int pts_wrap_bits;
undefined field24_0xd4;
undefined field25_0xd5;
undefined field26_0xd6;
undefined field27_0xd7;
int64_t first_dts;
int64_t cur_dts;
int64_t last_IP_pts;
int last_IP_duration;
int probe_packets;
int codec_info_nb_frames;
enum AVStreamParseType need_parsing;
struct AVCodecParserContext *parser;
struct AVPacketList *last_in_packet_buffer;
struct AVProbeData probe_data;
int64_t pts_buffer[17];
struct AVIndexEntry *index_entries;
int nb_index_entries;
uint index_entries_allocated_size;
struct AVRational r_frame_rate;
int stream_identifier;
int64_t interleaver_chunk_size;
int64_t interleaver_chunk_duration;
int request_probe;
int skip_to_keyframe;
int skip_samples;
undefined field49_0x1d4;
undefined field50_0x1d5;
undefined field51_0x1d6;
undefined field52_0x1d7;
int64_t first_discard_sample;
int64_t last_discard_sample;
int nb_decoded_frames;
undefined field56_0x1ec;
undefined field57_0x1ed;
undefined field58_0x1ee;
undefined field59_0x1ef;
int64_t mux_ts_offset;
int64_t pts_wrap_reference;
int pts_wrap_behavior;
int update_initial_durations_done;
int64_t pts_reorder_error[17];
uint8_t pts_reorder_error_count[17];
undefined field66_0x2a1;
undefined field67_0x2a2;
undefined field68_0x2a3;
undefined field69_0x2a4;
undefined field70_0x2a5;
undefined field71_0x2a6;
undefined field72_0x2a7;
int64_t last_dts_for_order_check;
uint8_t dts_ordered;
uint8_t dts_misordered;
undefined field76_0x2b2;
undefined field77_0x2b3;
int inject_global_side_data;
char *recommended_encoder_configuration;
struct AVRational display_aspect_ratio;
undefined field81_0x2c4;
undefined field82_0x2c5;
undefined field83_0x2c6;
undefined field84_0x2c7;
};
struct duss_hal_mem_buf
{
};
struct AVSubtitleRect
{
int x;
int y;
int w;
int h;
int nb_colors;
struct AVPicture pict;
enum AVSubtitleType type;
char *text;
char *ass;
int flags;
};
struct AVIOContext
{
struct AVClass *av_class;
uchar *buffer;
int buffer_size;
uchar *buf_ptr;
uchar *buf_end;
void *opaque;
int (*read_packet)(void *, uint8_t *, int);
int (*write_packet)(void *, uint8_t *, int);
int64_t (*seek)(void *, int64_t, int);
undefined field9_0x24;
undefined field10_0x25;
undefined field11_0x26;
undefined field12_0x27;
int64_t pos;
int must_flush;
int eof_reached;
int write_flag;
int max_packet_size;
ulong checksum;
uchar *checksum_ptr;
ulong (*update_checksum)(ulong, uint8_t *, uint);
int error;
int (*read_pause)(void *, int);
int64_t (*read_seek)(void *, int, int64_t, int);
int seekable;
undefined field25_0x5c;
undefined field26_0x5d;
undefined field27_0x5e;
undefined field28_0x5f;
int64_t maxsize;
int direct;
undefined field31_0x6c;
undefined field32_0x6d;
undefined field33_0x6e;
undefined field34_0x6f;
int64_t bytes_read;
int seek_count;
int writeout_count;
int orig_buffer_size;
undefined field39_0x84;
undefined field40_0x85;
undefined field41_0x86;
undefined field42_0x87;
};
struct AVOption
{
char *name;
char *help;
int offset;
enum AVOptionType type;
union anon_union_conflictac8f4_for_default_val default_val;
double min;
double max;
int flags;
char *unit;
};
struct AVSubtitle
{
uint16_t format;
undefined field1_0x2;
undefined field2_0x3;
uint32_t start_display_time;
uint32_t end_display_time;
uint num_rects;
struct AVSubtitleRect **rects;
undefined field7_0x14;
undefined field8_0x15;
undefined field9_0x16;
undefined field10_0x17;
int64_t pts;
};
struct duss_osal_task_handle_t
{
struct duss_osal_task_attrib_t attrib;
pthread_t thread;
};
struct modem_shmem_info_t
{
uint32_t frm_idx;
uint16_t frm_isI;
uint16_t frm_len;
uint16_t frm_dsti;
uint16_t frm_dstf;
uint16_t channel_status;
uint16_t dec_err_status;
uint16_t cur_time;
uint16_t delta_time;
uint16_t dbg_msc;
uint8_t dbg_ap_ready;
uint8_t dbg_cp_ready;
uint32_t local_id;
uint8_t cp_state;
uint8_t cp_report;
uint8_t cp_report_seq;
uint8_t client_type;
uint32_t cp_boot_status0;
uint32_t cp_boot_status1;
uint32_t board_version;
uint32_t board_sub_version;
uint8_t machine_role;
uint8_t is_reverse;
int8_t cp_tx_power;
uint8_t gnd_type;
uint32_t cp_sssfn;
uint32_t ulow_en;
uint8_t mipi_rx_response;
uint8_t liveview_broken_status;
uint8_t reserved01;
uint8_t reserved02;
uint8_t ap_reboot_flag;
uint8_t ap_reboot_ack_flag;
uint8_t secure_sync_flag;
uint8_t reserve00;
uint8_t area_state;
uint8_t area_substate;
uint8_t GsCtrl;
uint8_t GsSubState;
uint8_t WaterLevel[4];
uint16_t RxCntStastic[4];
uint16_t TxCntStastic[4];
uint8_t Reserved[8];
uint32_t com_uart_status;
uint32_t fcr_rx_status;
uint32_t fcr_tx_status;
uint32_t frm_idx_for_display;
uint32_t frm_delay_for_display;
uint16_t wifi_sdr_mode;
uint16_t frm_isI_for_display;
uint32_t country_code;
uint16_t frm_len_for_display;
uint16_t delta_time_for_display;
uint8_t uint8_t_reboot_reason;
undefined field54_0x85;
undefined field55_0x86;
undefined field56_0x87;
uint64_t cpa7_version;
uint64_t dsp_version;
uint8_t u8_dual_band_capability;
undefined field60_0x99;
undefined field61_0x9a;
undefined field62_0x9b;
undefined field63_0x9c;
undefined field64_0x9d;
undefined field65_0x9e;
undefined field66_0x9f;
};
struct sqlite3
{
};
struct cp_vdec
{
void *gs_info;
uint32_t width;
uint32_t height;
void *cb_ctx;
int (*cb_func)(void *, dec_event_t);
vdec_state_t vdec_state;
_Bool b_running;
_Bool b_eos;
_Bool b_first_frm_sent;
undefined field9_0x1b;
gs_media_cmd_chnl_t *mcc;
int dmi_data_fd;
struct duss_osal_task_handle_t *thread_tx;
int frames_received;
int frames_sent;
struct duss_osal_mutex_handle_t *list_mutex;
struct duss_list_head frame_list;
int num_in_list;
int64_t play_tm_ms;
};
struct AVOutputFormat
{
char *name;
char *long_name;
char *mime_type;
char *extensions;
enum AVCodecID audio_codec;
enum AVCodecID video_codec;
enum AVCodecID subtitle_codec;
int flags;
struct AVCodecTag **codec_tag;
struct AVClass *priv_class;
struct AVOutputFormat *next;
int priv_data_size;
int (*write_header)(struct AVFormatContext *);
int (*write_packet)(struct AVFormatContext *, struct AVPacket *);
int (*write_trailer)(struct AVFormatContext *);
int (*interleave_packet)(struct AVFormatContext *, struct AVPacket *, struct AVPacket *, int);
int (*query_codec)(enum AVCodecID, int);
void (*get_output_timestamp)(struct AVFormatContext *, int, int64_t *, int64_t *);
int (*control_message)(struct AVFormatContext *, int, void *, size_t);
int (*write_uncoded_frame)(struct AVFormatContext *, int, struct AVFrame **, uint);
int (*get_device_list)(struct AVFormatContext *, struct AVDeviceInfoList *);
int (*create_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
int (*free_device_capabilities)(struct AVFormatContext *, struct AVDeviceCapabilitiesQuery *);
};
struct rc_set_function_pack_t
{
uint8_t arm_channel : 2;
uint8_t arm_setval : 2;
uint8_t flip_channel : 2;
uint8_t flip_setval : 2;
};
struct pl_muxer_t
{
struct pl_muxer_itf_t *itf;
void *context;
void *me;
};
struct AVCodec
{
char *name;
char *long_name;
enum AVMediaType type;
enum AVCodecID id;
int capabilities;
struct AVRational *supported_framerates;
enum AVPixelFormat *pix_fmts;
int *supported_samplerates;
enum AVSampleFormat *sample_fmts;
uint64_t *channel_layouts;
uint8_t max_lowres;
undefined field11_0x29;
undefined field12_0x2a;
undefined field13_0x2b;
struct AVClass *priv_class;
struct AVProfile *profiles;
int priv_data_size;
struct AVCodec *next;
int (*init_thread_copy)(struct AVCodecContext *);
int (*update_thread_context)(struct AVCodecContext *, struct AVCodecContext *);
struct AVCodecDefault *defaults;
void (*init_static_data)(struct AVCodec *);
int (*init)(struct AVCodecContext *);
int (*encode_sub)(struct AVCodecContext *, uint8_t *, int, struct AVSubtitle *);
int (*encode2)(struct AVCodecContext *, struct AVPacket *, struct AVFrame *, int *);
int (*decode)(struct AVCodecContext *, void *, int *, struct AVPacket *);
int (*close)(struct AVCodecContext *);
void (*flush)(struct AVCodecContext *);
};
struct snd_pcm_mmap_control
{
};
================================================
FILE: jni/rec/rec_util.c
================================================
#include "rec_util.h"
#include
void rec_util_osd_path_from_video_path(const char *video_path, char *osd_path, size_t osd_path_size)
{
char *file_basename = strrchr(video_path, '/') + 1;
char *file_ext = strrchr(file_basename, '.');
uint8_t file_dir_len = file_basename - video_path - 1;
uint8_t file_stem_len = file_ext - file_basename;
snprintf(
osd_path,
osd_path_size,
"%.*s/%.*s.osd",
file_dir_len,
video_path,
file_stem_len,
file_basename);
}
================================================
FILE: jni/rec/rec_util.h
================================================
#pragma once
#include
void rec_util_osd_path_from_video_path(const char *video_path, char *osd_path, size_t osd_path_size);
================================================
FILE: jni/toast/toast.c
================================================
#include
#include
#include
#include
#include
#include
#include "json/osd_config.h"
#include "util/time_util.h"
#define DATASIZE 20
#define TOP_ROW 5
#define BOTTOM_ROW 8
#define TOAST_DISABLE_KEY "hide_diagnostics"
#include
int toast_enabled = 1;
typedef struct ToastItem
{
char data[DATASIZE];
struct timespec shown;
struct ToastItem *nextNode;
} ToastItem;
ToastItem *bottomStackPointer = NULL;
ToastItem *topStackPointer = NULL;
struct timespec now, lasttoastremoval;
void toast_load_config()
{
if (get_boolean_config_value(TOAST_DISABLE_KEY))
{
toast_enabled = 0;
}
}
int toast_pop()
{
if (bottomStackPointer == NULL)
return 0;
char data[DATASIZE];
strcpy(data, bottomStackPointer->data);
bottomStackPointer->shown = (struct timespec){0, 0};
ToastItem *TempPointer = bottomStackPointer;
bottomStackPointer = bottomStackPointer->nextNode;
free(TempPointer);
if (bottomStackPointer == NULL)
topStackPointer = NULL;
return 1;
}
int toast(char *data, ...)
{
if (!toast_enabled) {
return 0;
}
ToastItem *TempPointer = malloc(sizeof(ToastItem));
if (TempPointer == NULL)
return 0;
// printf
va_list va;
va_start(va, data);
char copy[DATASIZE];
vsnprintf(copy, DATASIZE, data, va);
// our fonts are caps only....
for (int i = 0; copy[i]; i++)
{
copy[i] = toupper(copy[i]);
}
strncpy(TempPointer->data, copy, DATASIZE);
TempPointer->shown = (struct timespec){0, 0};
TempPointer->nextNode = NULL;
if (bottomStackPointer == NULL)
bottomStackPointer = TempPointer;
else
topStackPointer->nextNode = TempPointer;
topStackPointer = TempPointer;
return 1;
}
void do_toast(void (*display_print_string)(uint8_t init_x, uint8_t y, const char *string, uint8_t len))
{
if (!toast_enabled) {
return;
}
int numberoffNodes = 0;
clock_gettime(CLOCK_MONOTONIC, &now);
// if the last item has been up 3 seconds; chop it
// but only remove 1 per second
ToastItem *TempPointer = bottomStackPointer;
if (
timespec_subtract_ns(&now, &lasttoastremoval) > NSEC_PER_SEC // 1ce per second limit
&& TempPointer != NULL && TempPointer->shown.tv_sec > 0 // guards
&& timespec_subtract_ns(&now, &TempPointer->shown) > ((uint64_t)NSEC_PER_SEC * 3) // item has been on screen for long enough
)
{
clock_gettime(CLOCK_MONOTONIC, &lasttoastremoval);
TempPointer = TempPointer->nextNode;
toast_pop();
}
// loop and display
for (int row = BOTTOM_ROW; row >= TOP_ROW; row--)
{
// if we ran out of items blank out the rest of our rows
if (TempPointer == NULL) {
char empty[DATASIZE] = {0};
display_print_string(0, row, empty, DATASIZE);
} else {
// set shown time if not set (ie: item not yet shown)
if (TempPointer->shown.tv_sec == 0)
{
clock_gettime(CLOCK_MONOTONIC, &TempPointer->shown);
}
// draw the current item in the current row
display_print_string(0, row, TempPointer->data, DATASIZE);
TempPointer = TempPointer->nextNode;
}
};
return;
}
================================================
FILE: jni/toast/toast.h
================================================
#pragma once
int toast(char*, ...);
void toast_load_config();
void do_toast(void (*display_print_string)(uint8_t init_x, uint8_t y, const char *string, uint8_t len));
================================================
FILE: jni/util/debug.h
================================================
// #pragma once
#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) fprintf(stderr, fmt, ## args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
================================================
FILE: jni/util/display_info.h
================================================
#pragma once
#include
#define NUM_CHARS 256
#define NUM_FONT_PAGES 4
typedef struct display_info_s {
uint8_t char_width;
uint8_t char_height;
uint8_t font_width;
uint8_t font_height;
uint16_t x_offset;
uint16_t y_offset;
void *fonts[NUM_FONT_PAGES];
} display_info_t;
================================================
FILE: jni/util/fs_util.c
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "fs_util.h"
#define DICTIONARY_PATH "/opt/mspdictionaries"
int32_t get_int_from_fs(char* path) {
int32_t val;
char read_buffer[32];
memset(read_buffer, 0, 32);
int fd = open(path, O_RDONLY, 0);
if(fd < 0) {
return -1;
}
int read_count = read(fd, read_buffer, 32);
if(read_count > 0) {
val = atoi(read_buffer);
}
close(fd);
return val;
}
void *open_dict(int dict_version, int *size) {
char file_path[255];
snprintf(file_path, 255, "%s/dictionary_%d.bin", DICTIONARY_PATH, dict_version);
struct stat st;
memset(&st, 0, sizeof(st));
stat(file_path, &st);
size_t filesize = st.st_size;
int fd = open(file_path, O_RDONLY, 0);
if (!fd) {
return NULL;
}
void* dict = malloc(filesize);
void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
*size = filesize;
memcpy(dict, mmappedData, filesize);
close(fd);
munmap(mmappedData, filesize);
return dict;
}
================================================
FILE: jni/util/fs_util.h
================================================
#pragma once
#include
int32_t get_int_from_fs(char*);
void *open_dict(int, int *);
================================================
FILE: jni/util/time_util.h
================================================
#pragma once
#include
#include
#define NSEC_PER_SEC 1000000000
#define MSEC_PER_SEC 1000
static inline void timespec_subtract(struct timespec *res, const struct timespec *a, const struct timespec *b)
{
res->tv_sec = a->tv_sec - b->tv_sec;
res->tv_nsec = a->tv_nsec - b->tv_nsec;
if (res->tv_nsec < 0) {
res->tv_sec--;
res->tv_nsec += NSEC_PER_SEC;
}
}
static inline int64_t timespec_subtract_ns(const struct timespec *a, const struct timespec *b)
{
struct timespec res;
timespec_subtract(&res, a, b);
return (int64_t)res.tv_sec * NSEC_PER_SEC + res.tv_nsec;
}
================================================
FILE: libshims/duml_hal.c
================================================
#include "jni/hw/duml_hal.h"
duss_result_t duss_hal_initialize(duss_hal_device_desc_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_deinitialize()
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_device_open(char *device_name, void *unknown, duss_hal_obj_handle_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_device_start(duss_hal_obj_handle_t, void *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_device_close(duss_hal_obj_handle_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_device_stop(duss_hal_obj_handle_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_mem_alloc(duss_hal_obj_handle_t, duss_hal_mem_handle_t *, uint32_t size, uint32_t, uint32_t, uint32_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_mem_get_phys_addr(duss_hal_mem_handle_t, void **)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_mem_map(duss_hal_mem_handle_t, void **)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_mem_free(duss_hal_mem_handle_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_mem_sync(duss_hal_mem_handle_t, uint32_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_open(duss_hal_obj_handle_t, duss_disp_instance_handle_t **, duss_disp_vop_id_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_close(duss_hal_obj_handle_t, duss_disp_instance_handle_t **)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_aquire_plane(duss_disp_instance_handle_t *, duss_disp_plane_type_t, duss_disp_plane_id_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_reset(duss_disp_instance_handle_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_register_frame_cycle_callback(duss_disp_instance_handle_t *, duss_disp_plane_id_t, frame_pop_handler *, void *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_timing_detail_get(duss_disp_instance_handle_t *, duss_disp_timing_detail_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_port_enable(duss_disp_instance_handle_t *, duss_disp_port_id_t, uint8_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_plane_blending_set(duss_disp_instance_handle_t *, duss_disp_plane_id_t, duss_disp_plane_blending_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_release_plane(duss_disp_instance_handle_t *, duss_disp_plane_id_t)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_display_push_frame(duss_disp_instance_handle_t *, duss_disp_plane_id_t, duss_frame_buffer_t *)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_attach_disp(char *param_1, duss_hal_obj **param_2)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_attach_ion_mem(char *param_1, duss_hal_obj **param_2)
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_detach_ion_mem()
{
duss_result_t dummy;
return dummy;
}
duss_result_t duss_hal_detach_disp()
{
duss_result_t dummy;
return dummy;
}