Skip to content

Commit f0f8518

Browse files
authored
Merge pull request #935 from amiaopensource/live-captions-are-so-easy
this adds a live display to the caption viewer
2 parents ae39a48 + 881d24a commit f0f8518

2 files changed

Lines changed: 202 additions & 6 deletions

File tree

cchex_to_display

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env bash
2+
# cc_to_srt_safe.sh
3+
# Convert EIA-608 hex dump to readable text (simplified version)
4+
bash --version
5+
INPUT_FILE="$1"
6+
OUTPUT_FILE="$2"
7+
OUTPUT_FILE_TMP="${OUTPUT_FILE}.tmp"
8+
9+
if [[ -z "$INPUT_FILE" || -z "$OUTPUT_FILE" ]]; then
10+
echo "Usage: $0 <input_file> <output_file>"
11+
exit 1
12+
fi
13+
14+
declare -A char_map=(
15+
["20"]=" "
16+
["A1"]="!"
17+
["A2"]="\""
18+
["23"]="#"
19+
["A4"]="$"
20+
["25"]="%"
21+
["26"]="&"
22+
["A7"]="'"
23+
["A8"]="("
24+
["29"]=")"
25+
["2A"]="á"
26+
["AB"]="+"
27+
["2C"]=","
28+
["AD"]="-"
29+
["AE"]="."
30+
["2F"]="/"
31+
["B0"]="0"
32+
["31"]="1"
33+
["32"]="2"
34+
["B3"]="3"
35+
["34"]="4"
36+
["B5"]="5"
37+
["B6"]="6"
38+
["37"]="7"
39+
["38"]="8"
40+
["B9"]="9"
41+
["BA"]=":"
42+
["3B"]=";"
43+
["BC"]="<"
44+
["3D"]="="
45+
["3E"]=">"
46+
["BF"]="?"
47+
["40"]="@"
48+
["C1"]="A"
49+
["C2"]="B"
50+
["43"]="C"
51+
["C4"]="D"
52+
["45"]="E"
53+
["46"]="F"
54+
["C7"]="G"
55+
["C8"]="H"
56+
["49"]="I"
57+
["4A"]="J"
58+
["CB"]="K"
59+
["4C"]="L"
60+
["CD"]="M"
61+
["CE"]="N"
62+
["4F"]="O"
63+
["D0"]="P"
64+
["51"]="Q"
65+
["52"]="R"
66+
["D3"]="S"
67+
["54"]="T"
68+
["D5"]="U"
69+
["D6"]="V"
70+
["57"]="W"
71+
["58"]="X"
72+
["D9"]="Y"
73+
["DA"]="Z"
74+
["5B"]="["
75+
["DC"]="é"
76+
["5D"]="]"
77+
["5E"]="í"
78+
["DF"]="ó"
79+
["E0"]="ú"
80+
["61"]="a"
81+
["62"]="b"
82+
["E3"]="c"
83+
["64"]="d"
84+
["E5"]="e"
85+
["E6"]="f"
86+
["67"]="g"
87+
["68"]="h"
88+
["E9"]="i"
89+
["EA"]="j"
90+
["6B"]="k"
91+
["EC"]="l"
92+
["6D"]="m"
93+
["6E"]="n"
94+
["EF"]="o"
95+
["70"]="p"
96+
["F1"]="q"
97+
["F2"]="r"
98+
["73"]="s"
99+
["F4"]="t"
100+
["75"]="u"
101+
["76"]="v"
102+
["F7"]="w"
103+
["F8"]="x"
104+
["79"]="y"
105+
["7A"]="z"
106+
["FB"]="ç"
107+
["7C"]="÷"
108+
["FD"]="Ñ"
109+
["FE"]="ñ"
110+
["7F"]=" "
111+
)
112+
113+
# --- Fast lookup ---
114+
lookup() {
115+
local key="$1"
116+
echo -n "${char_map[$key]}"
117+
}
118+
119+
unset START_FRAME
120+
# --- Start buffer to hold last 30 chars ---
121+
TAIL_SCAN="60"
122+
CAPTION_LINE_LENGTH="64"
123+
BUFFER="$(printf "%*s" "${CAPTION_LINE_LENGTH}" "")"
124+
if [[ ! -f "${OUTPUT_FILE}" ]] ; then
125+
echo -n "${BUFFER}" > "${OUTPUT_FILE_TMP}"
126+
mv "${OUTPUT_FILE_TMP}" "${OUTPUT_FILE}"
127+
fi
128+
while true ; do
129+
LAST_METADATA="$(tail -n "${TAIL_SCAN}" "${INPUT_FILE}")"
130+
if [[ -z "$START_FRAME" ]] ; then
131+
METADATA_TO_PROCESS="${LAST_METADATA}"
132+
else
133+
METADATA_TO_PROCESS="$(echo "${LAST_METADATA}" | grep -A "${TAIL_SCAN}" "frame:${START_FRAME} ")"
134+
fi
135+
START_FRAME="$(echo "${LAST_METADATA}" | grep -o 'frame:[0-9]*' | tail -n1 | cut -d: -f2)"
136+
NEW_CCS="$(echo "${METADATA_TO_PROCESS}" | grep "lavfi.readeia608.0.cc=" | cut -d "=" -f2)"
137+
((START_FRAME++))
138+
while IFS= read -r CC_HEX ; do
139+
CC_HEX="${CC_HEX//0x}"
140+
if [[ "${CC_HEX}" != "8080" && -n "${CC_HEX}" ]] ; then
141+
CC1="$(lookup "${CC_HEX:0:2}")"
142+
else
143+
unset CC1
144+
fi
145+
if [[ -n "${CC1}" ]] ; then
146+
CC2="$(lookup "${CC_HEX:2:2}")"
147+
BUFFER+="${CC1}${CC2}"
148+
BUFFER="${BUFFER: -${CAPTION_LINE_LENGTH}}"
149+
echo -n "${BUFFER}" > "${OUTPUT_FILE_TMP}"
150+
mv "${OUTPUT_FILE_TMP}" "${OUTPUT_FILE}"
151+
else
152+
BUFFER+=" "
153+
if [[ -z "${BUFFER: -${CAPTION_LINE_LENGTH}}" ]] ; then
154+
BUFFER="$(printf "%*s" "${CAPTION_LINE_LENGTH}" "")"
155+
else
156+
BUFFER="${BUFFER: -${CAPTION_LINE_LENGTH}}"
157+
if [[ -n "${BUFFER// }" || "${CLEAR}" == "1" ]] ; then
158+
echo -n "${BUFFER}" > "${OUTPUT_FILE_TMP}"
159+
mv "${OUTPUT_FILE_TMP}" "${OUTPUT_FILE}"
160+
CLEAR="0"
161+
else
162+
CLEAR="1"
163+
fi
164+
fi
165+
fi
166+
echo "BUFFER=(${BUFFER}) CC1=${CC1} CC2=${CC2}"
167+
unset CC1 CC2
168+
done <<< "${NEW_CCS}"
169+
sleep 0.2
170+
done

vrecord

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,11 +1560,19 @@ _passthrough_mode(){
15601560
echo "RECORD ${RECORD_COMMAND[@]}"
15611561
fi
15621562

1563+
if [[ "${PLAYBACKVIEW_CHOICE_PASS}" == "Captions" ]] && [[ "${RUNTYPE}" == "passthrough" ]] ; then
1564+
"${SCRIPTDIR}/cchex_to_display" "${FFREPORT_TMP}" "${CAP_DISPLAY_TMP}" > /dev/null 2> /dev/null &
1565+
CC_PID=$!
1566+
export FFREPORT="file=${FFREPORT_TMP}:level=32"
1567+
fi
15631568
if [[ "${VRECORD_STEPS}" = "1" ]] ; then
15641569
"${PLAYER_COMMAND[@]}" 2> "${VRECORD_INPUT_TMP}"
15651570
elif [[ "${VRECORD_STEPS}" = "2" ]] ; then
15661571
"${RECORD_COMMAND[@]}" 2> "${VRECORD_INPUT_TMP}" | "${PLAYER_COMMAND[@]}"
15671572
fi
1573+
if [[ "${PLAYBACKVIEW_CHOICE_PASS}" == "Captions" ]] && [[ "${RUNTYPE}" == "passthrough" ]] ; then
1574+
kill "$CC_PID"
1575+
fi
15681576
}
15691577

15701578
_audiopassthrough_mode(){
@@ -2150,13 +2158,23 @@ color=color=darkgray,scale=${CORNER_W}*${CORNER_ZOOM}*2:${CORNER_H}*2,split=4[l1
21502158
[c1b][d1b]hstack,scale=${CORNER_W}*16*2:${CORNER_H}*16:flags=neighbor,drawgrid=w=iw/${CORNER_W}/2:h=ih/${CORNER_H}:t=1:c=green@0.5[cdb];\
21512159
[l1t][abt][l2t][abb][l3t][cdt][l4t][cdb]vstack=8,setsar=1/1" ;;
21522160
"Captions")
2161+
if [[ "${RUNTYPE}" = "record" ]] ; then
21532162
PLAYBACKFILTER="\
2154-
${PLAYBACK_FILTER_ADJUSTMENT}format=yuv420p10le|yuv422p10le|yuv444p10le|yuv440p10le,split=4[f1][f2][f3][f4];\
2155-
[f1]readeia608=scan_max=2:spw=0.30,drawtext=fontfile=${DEFAULTFONT}:fontcolor=white:fontsize=36:box=1:boxcolor=black@0.5:x=(w-tw)/2:y=h*3/4-ascent:text=Line %{metadata\\\:lavfi.readeia608.0.line\\\:-} %{metadata\\\:lavfi.readeia608.0.cc\\\:------} - Line %{metadata\\\:lavfi.readeia608.1.line\\\:-} %{metadata\\\:lavfi.readeia608.1.cc\\\:------}[f1b];\
2156-
[f2]crop=iw:1:0:1,scale=iw:2:flags=neighbor,tile=layout=1x120:overlap=119:init_padding=119[caption_scroll];\
2157-
[f3]format=gray,crop=iw:1:0:1,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 1':fontcolor=white:fontsize=18:x=20:y=20[wvf];\
2158-
[f4]format=gray,crop=iw:1:0:2,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 2':fontcolor=white:fontsize=18:x=20:y=20[wvf2];\
2159-
[caption_scroll][f1b][wvf][wvf2]vstack=4" ;;
2163+
${PLAYBACK_FILTER_ADJUSTMENT}format=yuv420p10le|yuv422p10le|yuv444p10le|yuv440p10le,split=4[f1][f2][f3][f4];\
2164+
[f1]readeia608=scan_max=2:spw=0.30,drawtext=fontfile=${DEFAULTFONT}:fontcolor=white:fontsize=36:box=1:boxcolor=black@0.5:x=(w-tw)/2:y=h*3/4-ascent:text=Line %{metadata\\\:lavfi.readeia608.0.line\\\:-} %{metadata\\\:lavfi.readeia608.0.cc\\\:------} - Line %{metadata\\\:lavfi.readeia608.1.line\\\:-} %{metadata\\\:lavfi.readeia608.1.cc\\\:------}[f1b];\
2165+
[f2]crop=iw:1:0:1,scale=iw:2:flags=neighbor,tile=layout=1x120:overlap=119:init_padding=119[caption_scroll];\
2166+
[f3]format=gray,crop=iw:1:0:1,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 1':fontcolor=white:fontsize=18:x=20:y=20[wvf];\
2167+
[f4]format=gray,crop=iw:1:0:2,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 2':fontcolor=white:fontsize=18:x=20:y=20[wvf2];\
2168+
[caption_scroll][f1b][wvf][wvf2]vstack=4"
2169+
else
2170+
PLAYBACKFILTER="\
2171+
${PLAYBACK_FILTER_ADJUSTMENT}format=yuv420p10le|yuv422p10le|yuv444p10le|yuv440p10le,split=4[f1][f2][f3][f4];\
2172+
[f1]readeia608=scan_max=2:spw=0.30,metadata=mode=print:key=lavfi.readeia608.0.cc,drawtext=fontfile=${DEFAULTFONT}:fontcolor=white:fontsize=36:box=1:boxcolor=black@0.5:x=(w-tw)/2:y=h*3/4-ascent:text=Line %{metadata\\\:lavfi.readeia608.0.line\\\:-} %{metadata\\\:lavfi.readeia608.0.cc\\\:------} - Line %{metadata\\\:lavfi.readeia608.1.line\\\:-} %{metadata\\\:lavfi.readeia608.1.cc\\\:------},drawtext=fontfile=${DEFAULTFONT}:textfile=${CAP_DISPLAY_TMP}:reload=1:fontcolor=white:fontsize=18:x=(w-text_w)/2:y=h-100:shadowcolor=blue:shadowx=1:shadowy=1[f1b];\
2173+
[f2]crop=iw:1:0:1,scale=iw:2:flags=neighbor,tile=layout=1x120:overlap=119:init_padding=119[caption_scroll];\
2174+
[f3]format=gray,crop=iw:1:0:1,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 1':fontcolor=white:fontsize=18:x=20:y=20[wvf];\
2175+
[f4]format=gray,crop=iw:1:0:2,waveform=i=1:g=green,drawtext=fontfile=${DEFAULTFONT}:text='Line 2':fontcolor=white:fontsize=18:x=20:y=20[wvf2];\
2176+
[caption_scroll][f1b][wvf][wvf2]vstack=4"
2177+
fi ;;
21602178
# others
21612179
"Yes"|"Yes, after recording"|"Yes, concurrent with recording"|"No") ;;
21622180
*) _report -w "Error: ${1} is not a valid option." ; return 1 ;;
@@ -2529,6 +2547,8 @@ elif [[ -n "${1}" ]] && [[ -z "${RUNTYPE}" ]] ; then
25292547
fi
25302548

25312549
VRECORD_INPUT_TMP="$(_maketemp .vrecord_input.log)"
2550+
FFREPORT_TMP="$(_maketemp .cffreport.txt)"
2551+
CAP_DISPLAY_TMP="$(_maketemp .caption_scroll.txt)"
25322552

25332553
# manage an include file to pass variables to gtkdialog
25342554
VRECORD_VARS_FILE="/tmp/v_$(echo "${0}" | sed -e "s/[^A-Za-z0-9.]/_/g")_variables.txt"
@@ -2900,6 +2920,12 @@ fi
29002920
if [[ "${VRECORD_STEPS}" = "2" ]] ; then
29012921
_writeingestlog "Record command" "${RECORD_COMMAND[@]}"
29022922
_writeingestlog "Playback command" "${PLAYER_COMMAND[@]}"
2923+
2924+
# start background caption process
2925+
## disabling cchex_to_display during recording for now. If back here, please kill the CC_PID after use of it is done.
2926+
## "${SCRIPTDIR}/cchex_to_display" "${FFREPORT_TMP}" "${CAP_DISPLAY_TMP}" > /dev/null 2> /dev/null &
2927+
## CC_PID=$!
2928+
## export FFREPORT="file=${FFREPORT_TMP}:level=32"
29032929
"${RECORD_COMMAND[@]}" 2> >(tee "${VRECORD_INPUT_TMP}" 1>&2) | \
29042930
if [[ "${DEVICE_INPUT_CHOICE}" = 0 ]] && [[ "${QCTOOLSXML_CHOICE}" = "Yes, concurrent with recording" ]]; then
29052931
tee >("${PLAYER_COMMAND[@]}") | "${QCLI_COMMAND_PIPE[@]}"

0 commit comments

Comments
 (0)