- Branches
- CI
- Unit tests
- Versions and releases
- Testing a branch build
- Stack traces and mapping.txt
- Building locally
- Device tests
| Branch | Status |
|---|---|
v2 |
Main development branch (default). Modular code in android/. Built as decoder-v2.apk |
basic |
Legacy single-file version (1.0x), kept for reference and older setups. Built as decoder.apk. Its version history is in the basic README |
Base pull requests on v2.
.github/workflows/build.yml runs on every push and pull request, and by hand (workflow_dispatch, optionally for a given commit). A newer push to the same branch cancels the older run, so latest always gets the build of the newest commit.
| Job | Runs on | What it does |
|---|---|---|
test |
every push and pull request, forks included (no secrets) | ./gradlew testDebugUnitTest, then ./gradlew lintDebug against app/lint-baseline.xml (only new findings fail). Reports are uploaded when it fails |
build |
pushes and manual runs, not pull requests | signed assembleRelease (R8 minify and resource shrinking), the R8 mapping.txt kept as a workflow artifact |
What a build run publishes:
| Branch | Output |
|---|---|
v2 |
decoder-v2.apk and decoder-v2-mapping.txt in the latest release (replaced by every build); the APK with its version and commit to the OpenIPC Telegram channel (new versions only) |
basic |
decoder.apk in latest; nothing to Telegram |
| any other | workflow artifacts decoder-<branch, / → -> (APK) and …-mapping on the run page |
Every run, v2 and basic included, also keeps its mapping as the artifact …-mapping. Artifacts expire after 90 days.
A pull request therefore gets tests and lint only; its APK comes from the push run of its branch.
JVM tests under android/app/src/test/java/com/openipc/decoder/ (JUnit 4, Android stubs return defaults). They cover the parts that are pure logic: RTSP responses, URLs, authentication and sessions against a fake server (RtspSession…, RtspAuth…, RtspUrl…), SDP and transports, RTP reordering and udp:// URLs, NAL and JPEG assembly, HTTP MJPEG parsing, audio formats and decoding, the decode planner, the credential store, the debug server (JSON, request parsing, redaction), stream checks, the grid layout, video fit and the buffer pool. No test needs a device; UI and MediaCodec behaviour are tested on the bench (Device tests).
CI is the reference: the project does not depend on local builds.
-
Versions are
MAJOR.MINOR.PATCH(versionNameinandroid/app/build.gradle): PATCH for fixes and small changes, MINOR for a new feature, MAJOR for an incompatible change or a large update, only when the maintainer decides. Every pull request that changes the app or its build (android/, except the unit tests and Markdown files, and.github/workflows/build.yml, which holds build steps) bumps the version as its last commit before the merge:versionNameby that rule andversionCode+1. Docs-only and test-only pull requests do not. A pull request that changes the app or the build but not the APKs (a test or publication step in the workflow, a comment) takes theno-releaselabel instead and keeps the version. Numbers are not reserved for open pull requests: each one takes the next number when it is merged. CI checks this on every pull request tov2(.github/check-version.shin theversionworkflow,.github/workflows/version.yml; adding or removing a label re-runs only this check, not the build): a new number must be canonical (no leading zeros), greater than the base one, and come with its ownCHANGELOG.mdsection. -
Until 2.17
v2counted 2.xx (+0.01 per pull request); 2.17 is followed by 2.17.1 or 2.18.0.basickeeps its 1.7.x line (1.07.1 is 1.7.1). -
CHANGELOG.mdis the change history: the version bump commit of a pull request that changes the app adds the version's section (## <version> — <date>, newest first) in the same pull request. -
Development is continuous, so there is no release per version (#194). The one release,
latest, holds the newestv2andbasicbuilds and is updated in place by both (.github/publish-latest.sh, #229): each branch replaces only its own files; av2build moves thelatesttag to the commit it built and sets the titleDecoder <version>; the notes have one line per branch with its version and commit. The APK of every newv2version (not every build) is posted to the OpenIPC Telegram channel, where the older builds can be found;basicposts nothing there. Builds of other branches are only workflow artifacts of their runs (90 days). -
Test pre-releases: to let a reporter test a branch, a pre-release named after the issue is made by hand from the branch's CI artifact (never from a local build), e.g.
toys-187-test2for #187. It is deleted when the test is over, or replaced by the next one.B=feat/my-branch # the branch to test RUN=$(gh run list -R OpenIPC/decoder -b "$B" -e push -w Build -L 1 --json databaseId -q '.[0].databaseId') gh run download "$RUN" -R OpenIPC/decoder -n "decoder-${B//\//-}" # gives decoder-feat-my-branch.apk cp "decoder-${B//\//-}.apk" decoder-my-test1.apk # the asset name the reporter sees gh release create my-187-test1 decoder-my-test1.apk --prerelease \ --target "$(gh run view "$RUN" -R OpenIPC/decoder --json headSha -q .headSha)" \ --title "… test build 1 (#187)" --notes "…"
A test build never takes the place of
latestor of another test build: always--prerelease(so it is never marked Latest andreleases/latestkeeps pointing atlatest), a tag and an asset name of its own for every build (<device>-<issue>-test<N>,decoder-<device>-test<N>.apk, neverdecoder-v2.apkordecoder.apk); the artifact is always named after the branch. -
The change history is
CHANGELOG.md; the details are in the merged pull requests. -
The installed version and build hash show in the menu (under Exit,
v2.10, <hash>) and in/stats.json(version,git). -
Every build is
com.openipc.decoderwith the release signature, so a branch build installs over a release and keeps the settings.
B=feat/my-branch
RUN=$(gh run list -R OpenIPC/decoder -b "$B" -e push -L 1 --json databaseId -q '.[0].databaseId')
gh run download "$RUN" -R OpenIPC/decoder -n "decoder-${B//\//-}"
adb install -r "decoder-${B//\//-}.apk"The current v2 build: gh release download latest -R OpenIPC/decoder -p decoder-v2.apk. Check the hash under Exit after installing.
Release builds are shrunk and obfuscated by R8: classes, fields and methods get short names, and line numbers survive only through a map. A crash from a release build reads like this (an example):
java.lang.IllegalStateException: …
at a.u.a(SourceFile:12)
at com.openipc.decoder.Decoder.u(SourceFile:48)
R8 writes that map as mapping.txt, one per build:
# compiler: R8
# compiler_version: 8.13.17
# min_api: 21
# pg_map_id: 8df440e8… ← ties the file to exactly one build
com.openipc.decoder.DecodePlanner -> a.u:
# {"id":"sourceFile","fileName":"DecodePlanner.java"}
long capacity -> a
long proven -> c
long failedLoad -> d
1:5:void <init>(long,boolean,long,long):147:147 -> <init>
For every class, field and method it gives original -> obfuscated, with the line ranges that map back to the source. It also covers the library classes that went into the APK, which is why it is about 9,000 lines (700 KB). It holds no secrets (the code is open), and devices don't need it: it only matters for reading crashes.
A mapping fits only the build it came from. Use the one whose build matches the hash under Exit or git in /stats.json:
| Build | Mapping |
|---|---|
the current v2 build |
decoder-v2-mapping.txt in latest |
an older v2 build, or any branch build |
the artifact decoder-v2-mapping (or decoder-<branch>-mapping) of the CI run that built it, kept 90 days. A push run is found by its commit (below). A manual run (Run workflow with commit_hash) is listed under the branch it was started on, not under the commit it built: gh run list -R OpenIPC/decoder -w Build -e workflow_dispatch -b <branch>, then pick the run by its time or ID |
| builds before 2.08 | none: they were made before the mapping was kept (#181) |
RUN=$(gh run list -R OpenIPC/decoder -c <full commit hash> -e push -w Build -L 1 --json databaseId -q '.[0].databaseId')
gh run download "$RUN" -R OpenIPC/decoder -n decoder-v2-mapping
retrace decoder-v2-mapping.txt stacktrace.txt # Android SDK command-line toolsAfter retrace, the frames read at com.openipc.decoder.DecodePlanner.step(DecodePlanner.java:215).
Not needed for contributing (CI builds every push), but possible: JDK 17 and the Android SDK (platform 36), with ANDROID_HOME or android/local.properties.
cd android
./gradlew testDebugUnitTest # unit tests
./gradlew assembleDebug # → app/build/outputs/apk/debug/app-debug.apk, no keys neededA release build needs a keystore at android/key.jks and KEYSTORE_PASS, KEY_ALIAS, KEY_PASS in the environment (./gradlew assembleRelease). Debug and release builds are signed with different keys: to switch, uninstall first, which removes the saved camera URLs.
Test rounds run as GitHub issues that follow the structure of #53. Reports from users: devices.md.