Skip to content

Commit f054b06

Browse files
committed
General: Keep the previously-focused app out of the declaration video
record.sh started the screen capture and then launched Amply, so the first ~2.5s of every take showed whatever app was previously in the foreground during the launch animation. In the current take that was Octi, listing the owner's devices and sync server. Launch and settle before the capture starts instead. postprocess.sh gains TRIM_START (or a trim.txt) to salvage an existing take by dropping N seconds off the front, shifting caption timings to match; this take renders with 2.5s trimmed. The trim happens inside the filtergraph with an explicit timestamp reset — input seeking on screenrecord's variable-rate output made the fps filter pad the cut back out with ~60 duplicate frames, which silently lengthened the title card. The render now lands next to its DECLARATION.md and is git-ignored, so it can be kept on disk ready to upload without entering the repository.
1 parent 49c68d2 commit f054b06

5 files changed

Lines changed: 45 additions & 12 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ captures/
99
.kotlin/
1010
# Ephemeral screenshot-test reference renders (regenerated by fastlane/generate_screenshots.sh)
1111
app/src/screenshotTest*/reference/
12+
# Rendered declaration screencasts (regenerated by fastlane/play-declarations/*/record.sh).
13+
# Kept on disk for upload to the Play Console, never committed — they are large and
14+
# they capture whatever was on the recording device's screen.
15+
fastlane/play-declarations/**/*.mp4
1216

fastlane/play-declarations/README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ unlisted YouTube video or a link-shared Google Drive file.
2626

2727
The video is regenerated on a device with no manual screen recording: the recorder drives Amply
2828
with label-based taps (robust to layout changes) while `screenrecord` captures, then post-processing
29-
adds a title card, burned-in captions, and an end card. **The scripts are committed; the `.mp4`
30-
output is not** — it is written to `/tmp/amply-demo/` and regenerating it is one command.
29+
adds a title card, burned-in captions, and an end card. **The scripts are committed; the `.mp4` is
30+
not**`.gitignore` excludes `fastlane/play-declarations/**/*.mp4`, so the render can live next to
31+
its `DECLARATION.md` (ready to upload) without ever entering the repository. Intermediate artifacts
32+
(`raw.mp4`, `captions.txt`, the card text) stay in `/tmp/amply-demo/`.
3133

3234
| File | Role |
3335
|------|------|
@@ -40,11 +42,19 @@ output is not** — it is written to `/tmp/amply-demo/` and regenerating it is o
4042
# Requires a PHYSICAL, capability-gated device (an emulator fails the gate and
4143
# would record the diagnostics-only UI). Default serial is the Pixel 7a.
4244
./foreground-service-special-use/record.sh 31071JEHN17531
43-
./postprocess.sh /tmp/amply-demo/foreground-service-special-use
45+
./postprocess.sh /tmp/amply-demo/foreground-service-special-use \
46+
foreground-service-special-use/declaration.mp4
4447

4548
# NOREC=1 ./foreground-service-special-use/record.sh # validate the tap chain, no recording
49+
# TRIM_START=2.5 ./postprocess.sh … # drop N seconds off the front
4650
```
4751

52+
`TRIM_START` (or a `trim.txt` in the output dir) exists to salvage a take whose opening frames
53+
caught something they shouldn't have; caption timings are shifted to match. It should not normally
54+
be needed — `record.sh` launches Amply and lets it settle *before* the capture starts, because
55+
launching inside the recording puts the previously-focused app on screen for the duration of the
56+
launch animation.
57+
4858
### What the recorder guarantees
4959

5060
- **It refuses a bad take.** It aborts if the device does not start at the 80% limit (the restore

fastlane/play-declarations/foreground-service-special-use/DECLARATION.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ promptly, and interrupting it causes user-visible harm) but matches no defined t
9595
## 6. Demo video
9696

9797
Produced by `./record.sh` (see [`../README.md`](../README.md)). Recorded on a physical Pixel 7a,
98-
portrait 720×1600, ~68 s including title and end cards.
98+
portrait 720×1600, ~64 s including title and end cards. The rendered file sits next to this
99+
document as `declaration.mp4` and is git-ignored — upload it, don't commit it.
99100

100101
The video demonstrates the **unplug** stop condition rather than the 100% one. That is deliberate:
101102
it is the condition that cannot work without a running foreground service, because Android does not

fastlane/play-declarations/foreground-service-special-use/record.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,15 @@ if ! have "Charge to 100% once"; then
5959
echo " adb -s $SERIAL shell pm grant $PKG android.permission.WRITE_SECURE_SETTINGS" >&2
6060
exit 1
6161
fi
62-
# Restart into a clean dashboard so the recording opens on a settled screen.
62+
# Restart into a clean dashboard and let it settle BEFORE the capture starts.
63+
# Launching inside the recording would put the previously-focused app on screen
64+
# for the ~2.5s of the launch animation — whatever the user happened to have open.
6365
app_stop; pause 1
66+
app_launch; pause 4
6467

6568
# ---- recording --------------------------------------------------------------
6669
rec_start
67-
app_launch; pause 4
70+
pause 2.5
6871
cap "Plugged in — the battery is held\nat the 80% protection limit"
6972
pause 3.5
7073

fastlane/play-declarations/postprocess.sh

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
#!/usr/bin/env bash
22
# Assemble a declaration video from a recorder's output dir:
3-
# <OUTDIR>/raw.mp4 + captions.txt + title.txt + end.txt -> <OUTDIR>/declaration.mp4
3+
# <OUTDIR>/raw.mp4 + captions.txt + title.txt + end.txt -> <OUTFILE>
44
#
5-
# Usage: ./postprocess.sh [OUTDIR]
5+
# Usage: ./postprocess.sh [OUTDIR] [OUTFILE]
6+
#
7+
# Trimming: seconds to drop from the START of raw.mp4, read from <OUTDIR>/trim.txt
8+
# or $TRIM_START (default 0). Caption timings are shifted to match. This salvages a
9+
# take whose opening frames caught the previously-focused app during the launch
10+
# animation; record.sh now launches the app before recording starts, so a fresh
11+
# take should not need it.
612
set -euo pipefail
713

814
OUTDIR="${1:-/tmp/amply-demo/foreground-service-special-use}"
9-
RAW="$OUTDIR/raw.mp4"; CAPS="$OUTDIR/captions.txt"; OUT="$OUTDIR/declaration.mp4"
15+
RAW="$OUTDIR/raw.mp4"; CAPS="$OUTDIR/captions.txt"; OUT="${2:-$OUTDIR/declaration.mp4}"
1016
TITLE="$OUTDIR/title.txt"; END="$OUTDIR/end.txt"
17+
TRIM_START="${TRIM_START:-$( [ -f "$OUTDIR/trim.txt" ] && cat "$OUTDIR/trim.txt" || echo 0 )}"
1118

1219
FONT=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
1320
FONTB=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf
@@ -18,16 +25,24 @@ W=720; H=1600; FPS=30; BG=0x00332B # Amply brand dark teal
1825
[ -f "$END" ] || printf 'Protection is restored\nautomatically.' > "$END"
1926
TXTDIR="$(mktemp -d)"; trap 'rm -rf "$TXTDIR"' EXIT
2027

21-
dur=$(ffprobe -v error -select_streams v:0 -show_entries format=duration -of csv=p=0 "$RAW")
28+
raw_dur=$(ffprobe -v error -select_streams v:0 -show_entries format=duration -of csv=p=0 "$RAW")
29+
dur=$(awk -v d="$raw_dur" -v t="$TRIM_START" 'BEGIN{printf "%.3f", d-t}')
30+
[ "$(awk -v d="$dur" 'BEGIN{print (d>1)?1:0}')" = 1 ] || { echo "TRIM_START=$TRIM_START leaves nothing of $RAW"; exit 1; }
31+
[ "$TRIM_START" = "0" ] || echo "Trimming ${TRIM_START}s from the start of raw.mp4"
2232

2333
# --- timed-caption drawtext chain from captions.txt --------------------------
34+
# Caption timestamps are relative to the untrimmed raw, so shift them by TRIM_START
35+
# and drop any caption whose window closes before the new start.
2436
chain=""
2537
if [ -f "$CAPS" ]; then
2638
mapfile -t lines < "$CAPS"
2739
n=${#lines[@]}
2840
for ((i=0;i<n;i++)); do
2941
start=${lines[i]%%|*}; text=${lines[i]#*|}
30-
if (( i+1 < n )); then end=${lines[i+1]%%|*}; else end=$dur; fi
42+
if (( i+1 < n )); then end=${lines[i+1]%%|*}; else end=$raw_dur; fi
43+
start=$(awk -v v="$start" -v t="$TRIM_START" 'BEGIN{v-=t; printf "%.2f", (v<0)?0:v}')
44+
end=$(awk -v v="$end" -v t="$TRIM_START" 'BEGIN{printf "%.2f", v-t}')
45+
awk -v s="$start" -v e="$end" 'BEGIN{exit !(e>0 && e>s)}' || continue
3146
printf '%b' "$text" > "$TXTDIR/c$i.txt" # %b expands a literal \n into a line break
3247
chain+=",drawtext=expansion=none:fontfile=${FONT}:textfile=${TXTDIR}/c$i.txt:fontcolor=white:fontsize=27:line_spacing=10:box=1:boxcolor=0x000000C0:boxborderw=16:x=(w-text_w)/2:y=h-190:enable='between(t,${start},${end})'"
3348
done
@@ -39,7 +54,7 @@ ffmpeg -y -loglevel error -stats \
3954
-f lavfi -i "color=c=${BG}:s=${W}x${H}:d=4:r=${FPS}" \
4055
-filter_complex "
4156
[0:v]format=yuv420p,drawtext=expansion=none:fontfile=${FONTB}:textfile=${TITLE}:fontcolor=white:fontsize=32:line_spacing=18:x=(w-text_w)/2:y=(h-text_h)/2[t];
42-
[1:v]fps=${FPS},scale=${W}:${H},setsar=1,format=yuv420p${chain}[m];
57+
[1:v]trim=start=${TRIM_START},setpts=PTS-STARTPTS,fps=${FPS},scale=${W}:${H},setsar=1,format=yuv420p${chain}[m];
4358
[2:v]format=yuv420p,drawtext=expansion=none:fontfile=${FONTB}:textfile=${END}:fontcolor=white:fontsize=36:line_spacing=18:x=(w-text_w)/2:y=(h-text_h)/2[e];
4459
[t][m][e]concat=n=3:v=1:a=0[v]
4560
" -map "[v]" -c:v libx264 -preset medium -crf 20 -pix_fmt yuv420p -movflags +faststart "$OUT"

0 commit comments

Comments
 (0)