forked from amiaopensource/vrecord
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvrecord
executable file
·1610 lines (1463 loc) · 71.8 KB
/
vrecord
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
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# vrecord
# Open-source software for capturing a video signal and turning it into a digital file.
SCRIPTNAME=$(basename "${0}")
SCRIPTDIR=$(dirname "${0}")
CONFIG_FILE="${HOME}/.${SCRIPTNAME}.conf"
GTK_RECENT_DIR="${HOME}/.local/share"
unset VERSION
if [[ $(dirname "$(command -v "${0}")") = "/usr/local/bin" ]] ; then
VERSION=$(TMP=$(brew info vrecord | grep ".*\*$" | grep -Eo "/vrecord/.* \(") ; echo "${TMP:9:(${#TMP}-11)}")
RESOURCE_PATH="$(brew --prefix vrecord)"
else
RESOURCE_PATH="${SCRIPTDIR}/Resources"
fi
unset INPUTOPTIONS
unset MIDDLEOPTIONS
unset SUFFIX
unset DURATION
unset TECHNICIAN
RUNTYPE="record"
CAPTURELOGSUFFIX="_ffmpeg_decklink_input.log"
TIMECODELOGSUFFIX="_frame_timecodes.txt"
unset EXTRAOUTPUTS
SAT_OUTLIER_THRSHLD=14
AUD_OUTLIER_THRSHLD=10
BRNG_OUTLIER_THRSHLD=14
BREW_PREFIX=$(brew --prefix ffmpegdecklink 2>/dev/null)
FFMPEG_DECKLINK="${BREW_PREFIX}/bin/ffmpeg-dl"
FFPLAY_DECKLINK="${BREW_PREFIX}/bin/ffplay-dl"
FFPROBE_DECKLINK="${BREW_PREFIX}/bin/ffprobe-dl"
MPVOPTS=(--no-osc)
MPVOPTS+=(--load-scripts=no)
MPVOPTS+=(--script "${RESOURCE_PATH}/qcview.lua")
MPVOPTS+=(--really-quiet)
if [[ "$("${FFMPEG_DECKLINK}" -version 2>&1 | grep "Library not loaded" >/dev/null)" || ! -f "${FFMPEG_DECKLINK}" || ! -f "${FFPLAY_DECKLINK}" ]] ; then
echo "Please reinstall 'ffmpegdecklink':"
echo " brew reinstall amiaopensource/amiaos/ffmpegdecklink"
echo "Exiting."
exit 1
fi
_usage(){
cat <<EOF
${SCRIPTNAME} ${VERSION}
${SCRIPTNAME} will record a file via the Blackmagic SDK and ffmpeg. It is an
interactive script and will create 10 or 8-bit video files.
Dependencies: cowsay, amiaopensource/amiaos/decklinksdk,
amiaopensource/amiaos/ffmpegdecklink, amiaopensource/amiaos/gtkdialog,
freetype, gnuplot, mediaconch, mkvtoolnix, mpv, qcli, sdl and xmlstarlet
Usage: ${SCRIPTNAME} [ -g | -e | -r | -p | -a | -x | -v | -h ]
-g use the GUI
-e edit the configuration file before recording
-r enable record mode [default]
-p enable passthrough mode where the video signal coming into the
computer can be monitored, but not written to a file. Useful for
testing equipment and setting up a tape to bars.
-a enable audio passthrough mode. Identical to passthrough except for
the addition of audio bars. Note: Will eventually lag and crash if
left on too long.
-x reset the configuration: this will replace the default configuration
file at '${CONFIG_FILE}' with an empty one.
-v Run ffmpeg with '-loglevel debug'. Using this option creates a very
large log file, so avoid using this option with 'Visual + Numerical'
or any playback option that display the log as part of the view.
-h display this help menu
Advanced options
-I Provide a string of input options for the recording ffmpeg to use.
For example "vrecord -I '-loglevel trace'" would force an ffmpeg
logging level that is usually unaccessible via vrecord preferences.
-O Provide a string of output options for the recording ffmpeg to use.
-i Provide a file as an input to vrecord, rather than using the
decklink device. For testing without a decklink device.
See also the man page: man ${SCRIPTNAME}
EOF
}
# local functions
_get_iso8601(){
date +%FT%T
}
_report(){
local RED="$(tput setaf 1)" # Red - For Warnings
local GREEN="$(tput setaf 2)" # Green - For Declarations
local BLUE="$(tput setaf 4)" # Blue - For Questions
local NC="$(tput sgr0)" # No Color
local COLOR=""
local STARTMESSAGE=""
local ECHOOPT=""
OPTIND=1
while getopts "qdwstn" opt ; do
case "${opt}" in
q) COLOR="${BLUE}" ;; # question mode, use color blue
d) COLOR="${GREEN}" ;; # declaration mode, use color green
w) COLOR="${RED}" ;; # warning mode, use color red
s) STARTMESSAGE+=([${SCRIPTNAME}] ) ;; # prepend scriptname to the message
t) STARTMESSAGE+=($(_get_iso8601) '- ' ) ;; # prepend timestamp to the message
n) ECHOOPT="-n" ;; # to avoid line breaks after echo
esac
done
shift "$((OPTIND-1))"
MESSAGE="${1}"
echo ${ECHOOPT} "${COLOR}${STARTMESSAGE[@]}${MESSAGE}${NC}"
}
_parse_report(){
KEY="${1}"
REPORT="${2}"
echo "${REPORT}" | grep -i "^[ ]*${KEY}:" | cut -d : -f 2- | awk '{$1=$1;print}'
}
_cleanup(){
_report -wt "Vrecord is being stopped at $(_get_iso8601), noting this in the capture log."
_writeingestlog "exit status" "vrecord was forced to quit early at $(_get_iso8601). Some processing may be incomplete."
}
_check_mpv(){
if ! mpv > /dev/null ; then
mpv
_report -wt "mpv doesn't appear to be running correctly. Try 'brew reinstall mpv'."
exit 1
fi
}
# Set variables for system differences
QUERY_OS_TYPE="$(uname -s)"
if [[ "${QUERY_OS_TYPE}" = "Darwin" ]] ; then
OS_TYPE="macOS"
if [[ -f "/Library/Fonts/Andale Mono.ttf" ]] ; then
DEFAULTFONT="/Library/Fonts/Andale Mono.ttf"
elif [[ -f "/System/Library/Fonts/Supplemental/Andale Mono.ttf" ]] ; then
DEFAULTFONT="/System/Library/Fonts/Supplemental/Andale Mono.ttf"
elif [[ -f "/System/Library/Fonts/Monaco.dfont" ]] ; then
DEFAULTFONT="/System/Library/Fonts/Monaco.dfont"
elif [[ -f "/Library/Fonts/Microsoft/Lucida Console.ttf" ]] ; then
DEFAULTFONT="/Library/Fonts/Microsoft/Lucida\Console.ttf"
elif [[ -f "/Library/Fonts/LetterGothicStd.otf" ]] ; then
DEFAULTFONT="/Library/Fonts/LetterGothicStd.otf"
else
_report -wt "vrecord can't find a preferred font to use, please report this error to https://github.com/amiaopensource/vrecord/issues"
fi
HARDWARE_REPORT=$(system_profiler SPHardwareDataType)
CORE_COUNT="$(_parse_report "Total Number of Cores" "${HARDWARE_REPORT}")"
OPEN_COMMAND="open"
ZCAT_COMMAND="gzcat"
elif [[ "${QUERY_OS_TYPE}" = "Linux" ]] ; then
OS_TYPE="linux"
DEFAULTFONT="/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"
HARDWARE_REPORT=$(lscpu)
CORE_COUNT="$(nproc --all)"
OPEN_COMMAND="xdg-open"
ZCAT_COMMAND="zcat"
else
echo "Unsupported OS, ${QUERY_OS_TYPE}, detected. Exiting."
exit 1
fi
# command-line options to set media id and original variables
OPTIND=1
while getopts ":herpaxgvI:O:i:" opt ; do
case "${opt}" in
h) _usage ; exit 0 ;;
e) RUNTYPE="edit" ;;
r) RUNTYPE="record" ;;
p) RUNTYPE="passthrough" ;;
a) RUNTYPE="audiopassthrough" ;;
x) RUNTYPE="reset" ;;
g) RUNTYPE="GUI" ; GUI=1 ;;
v) VERBOSE="true" ;;
I) EXTRAINPUTOPTIONS=(${OPTARG}) ;;
O) EXTRAOUTPUTOPTIONS=(${OPTARG}) ;;
i) ALT_INPUT="${OPTARG}" ;;
:) _report -w "Option -${OPTARG} requires an argument" ; _usage ; exit 1 ;;
*) _report -w "Error: bad option -${OPTARG}" ; _usage ; exit 1 ;;
esac
done
shift "$((OPTIND-1))"
_verify_conf_items(){
# some options to handle situations where an older configuration file needs to store updated options
CONF_VARIABLE="${1}"
CONF_FILE="${2}"
if [[ "$(grep -c "${CONF_VARIABLE}" "${CONF_FILE}")" = "0" ]] ; then
if [[ "${CONF_VARIABLE}" = "FFV1_SLICE_CHOICE" ]] ; then
echo "${CONF_VARIABLE}=\"16\"" >> "${CONF_FILE}"
else
echo "${CONF_VARIABLE}=\"\"" >> "${CONF_FILE}"
fi
fi
}
if [[ -f "${CONFIG_FILE}" ]] ; then
_verify_conf_items "FFV1_SLICE_CHOICE" "${CONFIG_FILE}"
. "${CONFIG_FILE}"
elif [[ "${RUNTYPE}" = "record" || "${RUNTYPE}" = "edit" ]] ; then
_report -d "No configuration file, restarting in edit mode."
touch "${CONFIG_FILE}"
exec "${SCRIPTNAME}" -e
fi
# local functions
_get_decklink_inputs(){
DECKLINK_INPUTS=$("${FFMPEG_DECKLINK}" -f decklink -list_devices 1 -i dummy 2>&1 | grep -o "^\[decklink[^\]*][^']*'.*" | cut -d "'" -f2- | sed "s/'$//g")
if [[ -z "${DECKLINK_INPUTS}" ]] ; then
_report -w "No decklink inputs were found. Running \`${FFMPEG_DECKLINK} -hide_banner -f decklink -list_devices 1 -i dummy\` results in:"
DECKLINK_RESULT=$("${FFMPEG_DECKLINK}" -hide_banner -f decklink -list_devices 1 -i dummy)
echo "${DECKLINK_RESULT}"
_report -w "Please check connections and drivers and try again."
exit 1
else
FIRST_DECKLINK_INPUT="$(echo "${DECKLINK_INPUTS}" | head -n 1 )"
fi
}
_gtk_vbox_list() {
VARIABLE_NAME="${1}"
LABEL="${2}"
shift 2
OPTION_LIST=("${@}")
SELECTION="$(_get_index_of_value "${!VARIABLE_NAME}" "${OPTION_LIST[@]}")"
LIST="$(_expand_list2items "${OPTION_LIST[@]}")"
_get_list_extras(){
if [[ "${VARIABLE_NAME}" == "VIDEO_CODEC_CHOICE" ]] ; then
echo '<action condition="command_is_true( [ \"$VIDEO_CODEC_CHOICE\" = \"FFV1 version 3\" ] && echo true)">enable:FFV1_SLICE_CHOICE</action>
<action condition="command_is_true( [ \"$VIDEO_CODEC_CHOICE\" != \"FFV1 version 3\" ] && echo true)">disable:FFV1_SLICE_CHOICE</action>'
elif [[ "${VARIABLE_NAME}" == "CONTAINER_CHOICE" ]] ;then
echo '<action condition="command_is_true( [ \"$CONTAINER_CHOICE\" = \"Matroska\" ] && echo true)">enable:EMBED_LOGS_CHOICE</action>
<action condition="command_is_true( [ \"$CONTAINER_CHOICE\" != \"Matroska\" ] && echo true)">disable:EMBED_LOGS_CHOICE</action>'
fi
}
_get_vbox_extras(){
if [[ "${VARIABLE_NAME}" == "TIMECODE_CHOICE" ]] ; then
echo "
<button>
<label>Scan timecode types</label>
<action>echo TIMECODE_SCAN=1</action>
<action type=\"exit\">TIMECODE_SCAN</action>
</button>"
fi
}
echo "<vbox>
<text>
<label>${LABEL}</label>
</text>
<list selection-mode=\"1\" selected-row=\"${SELECTION}\">
<variable>${VARIABLE_NAME}</variable>
${LIST}
$(_get_list_extras)
</list>
$(_get_vbox_extras)
</vbox>"
}
_edit_prefs() {
_expand_list2items(){
LIST=( "$@" )
for i in "${LIST[@]}" ; do
echo "<item>${i}</item>"
done
}
_get_index_of_value(){
# run with function, value to look for as first argument, and array to look in as 2nd argument, such function as
# _get_index_of_value "${VIDEO_INPUT_CHOICE}" "${VIDEO_INPUT_OPTIONS[@]}"
VALUE="${1}"
shift
LIST=( "$@" )
INDEX=0
MATCH=""
for ITEM in "${LIST[@]}" ; do
if [[ "${VALUE}" = "${ITEM}" ]] ; then
MATCH="$INDEX"
fi
(( ++INDEX ))
done
if [[ -n "${MATCH}" ]] ; then
echo -n "${MATCH}"
fi
}
DECKLINK_INPUT_GUI="
<frame Decklink input options>
<vbox>
<hbox space-expand=\"true\">
$(_gtk_vbox_list "VIDEO_INPUT_CHOICE" "Select Video Input" "${VIDEO_INPUT_OPTIONS[@]}")
$(_gtk_vbox_list "AUDIO_INPUT_CHOICE" "Select Audio Input" "${AUDIO_INPUT_OPTIONS[@]}")
$(_gtk_vbox_list "AUDIO_MAPPING_CHOICE" "Select Audio Channel Mapping" "${CHANNEL_MAPPING_OPTIONS[@]}" )
$(_gtk_vbox_list "STANDARD_CHOICE" "Select Standard" "${STANDARD_OPTIONS[@]}")
$(_gtk_vbox_list "VIDEO_BIT_DEPTH_CHOICE" "Select Video Bit Depth" "${VIDEO_BITDEPTH_OPTIONS[@]}")
$(_gtk_vbox_list "TIMECODE_CHOICE" "Select timecode format" "${TIMECODE_OPTIONS[@]}")
</hbox>
<checkbox>
<label>Invert Second Channel of Audio (WARNING: Do not use this option unless required)</label>
<default>false</default>
<variable>INVERT_PHASE</variable>
</checkbox>
<frame Output file options>
<hbox space-expand=\"true\">
$(_gtk_vbox_list "EMBED_LOGS_CHOICE" "Embed digitization logs in video file (Matroska ONLY)" "${EMBED_LOGS_OPTIONS[@]}")
$(_gtk_vbox_list "CONTAINER_CHOICE" "Select File Format" "${CONTAINER_OPTIONS[@]}")
$(_gtk_vbox_list "FFV1_SLICE_CHOICE" "FFV1 Slice Count" "${FFV1_SLICE_OPTIONS[@]}")
$(_gtk_vbox_list "VIDEO_CODEC_CHOICE" "Select Video Codec" "${VIDEO_CODEC_OPTIONS[@]}")
$(_gtk_vbox_list "AUDIO_CODEC_CHOICE" "Select Audio Codec" "${AUDIO_CODEC_OPTIONS[@]}")
</hbox>
</frame>
</vbox>
</frame>"
unset AVFOUNDATION_DEVICES
while read avf_device ; do
AVFOUNDATION_DEVICES+=("${avf_device}")
done < <("${FFMPEG_DECKLINK}" -nostdin -hide_banner -f avfoundation -list_devices 1 -i dummy 2>&1 | grep -o "\[[0-9]\].*" | cut -d " " -f2-)
AVFOUNDATION_INPUT_GUI="
<frame AVFoundation input options>
<vbox>
<hbox space-expand=\"true\">
$(_gtk_vbox_list "AVF_INPUT_CHOICE" "Select a DV Device" "${AVFOUNDATION_DEVICES[@]}")
</hbox>
</vbox>
</frame>"
export MAIN_DIALOG="<window title=\"vrecord configuration\">
<vbox>
<text>
<label>Set file recording options.</label>
</text>
<frame Input Options>
<notebook page=\"${DEVICE_INPUT_CHOICE}\" tab-labels=\"Decklink|DV|\">
${DECKLINK_INPUT_GUI}
${AVFOUNDATION_INPUT_GUI}
<variable>DEVICE_INPUT_CHOICE</variable>
</notebook>
</frame>
<hbox>
<frame Playback options>
<hbox space-expand=\"true\">
$(_gtk_vbox_list "PLAYBACKVIEW_CHOICE" "Select View (for recording)" "${PLAYBACKVIEW_OPTIONS[@]}")
$(_gtk_vbox_list "PLAYBACKVIEW_CHOICE_PASS" "Select View (for passthrough)" "${PLAYBACKVIEW_OPTIONS[@]}")
</hbox>
</frame>
<frame Sidecar file options>
<hbox space-expand=\"true\">
$(_gtk_vbox_list "FRAMEMD5_CHOICE" "Create frame-level MD5 checksums? (recommended)" "${FRAMEMD5_OPTIONS[@]}")
$(_gtk_vbox_list "QCTOOLSXML_CHOICE" "Create QCTools XML?" "${QCTOOLSXML_OPTIONS[@]}")
</hbox>
</frame>
</hbox>
<hbox>
<frame File naming options>
<hbox space-expand=\"true\">
<vbox>
<text>
<label>Enter an optional file prefix</label>
</text>
<entry>
<default>\"${PREFIX}\"</default>
<variable>PREFIX</variable>
</entry>
</vbox>
<vbox>
<text>
<label>Enter an optional file suffix</label>
</text>
<entry>
<default>\"${USER_SUFFIX}\"</default>
<variable>USER_SUFFIX</variable>
</entry>
</vbox>
<vbox>
<checkbox>
<label>Check this to turn off appending of suffixes</label>
<default>\"${NO_SUFFIX}\"</default>
<variable>NO_SUFFIX</variable>
</checkbox>
</vbox>
</hbox>
</frame>
<frame Recording event options>
<hbox space-expand=\"true\">
<vbox>
<text>
<label>Set recording time (integer or decimal) in minutes</label>
</text>
<entry activates_default=\"true\" tooltip-text=\"Suggested:23 33 63 93. Leave blank for indefinite recording time\">
<default>\"${DURATION}\"</default>
<variable>DURATION</variable>
</entry>
</vbox>
<vbox>
<text>
<label>Enter the name of the person digitizing this tape.</label>
</text>
<entry>
<variable>TECHNICIAN</variable>
</entry>
</vbox>
</hbox>
</frame>
</hbox>
<frame Select a recording directory>
<hbox>
<entry accept=\"directory\">
<variable>DIR</variable>
<default>\"${DIR}\"</default>
</entry>
<button>
<input file stock=\"gtk-open\"></input>
<variable>DIRBROWSE</variable>
<action type=\"fileselect\">DIR</action>
</button>
</hbox>
</frame>
<frame Select a directory for auxiliary files (leave blank to match the recording directory).>
<hbox>
<entry accept=\"directory\" tooltip-text=\"select the directory for automatically generated logs and checksums\">
<variable>LOGDIR</variable>
<default>\"${LOGDIR}\"</default>
</entry>
<button>
<input file stock=\"gtk-open\"></input>
<variable>LOGDIRBROWSE</variable>
<action type=\"fileselect\">LOGDIR</action>
</button>
</hbox>
</frame>
<vbox>
<hbox>
<button ok>
<variable>OKBUTTON</variable>
</button>
<button cancel>
</button>
</hbox>
</vbox>
</vbox>
</window>"
eval $(gtkdialog --center --program MAIN_DIALOG)
}
_home_gui(){
export HOME_DIALOG="<window title=\"Welcome to Vrecord!\">
<vbox>
<vbox>
<hbox>
<button>
<label>Record</label>
<input file>${RESOURCE_PATH}/vrecord logo.png</input>
<action type=\"exit\">record</action>
</button>
<button>
<label>Passthrough</label>
<input file>${RESOURCE_PATH}/vrecord logo playback.png</input>
<action type=\"exit\">passthrough</action>
</button>
<button>
<label>Audio Check</label>
<input file>${RESOURCE_PATH}/vrecord logo audio.png</input>
<action type=\"exit\">audiopassthrough</action>
</button>
</hbox>
<hbox>
<button>
<label>Edit Settings</label>
<input file>${RESOURCE_PATH}/vrecord logo edit.png</input>
<action type=\"exit\">edit</action>
</button>
<button>
<label>Help</label>
<input file>${RESOURCE_PATH}/vrecord logo help.png</input>
<action>man vrecord</action>
</button>
<button>
<label>Documentation</label>
<input file>${RESOURCE_PATH}/vrecord logo documentation.png</input>
<action>open https://github.com/amiaopensource/vrecord#vrecord-documentation</action>
</button>
</hbox>
</vbox>
<hbox>
<button cancel></button>
</hbox>
</vbox>
</window>"
RUNTYPE=$(gtkdialog --center --program=HOME_DIALOG | grep "^EXIT=" | cut -d= -f2 | sed 's/"//g')
if [[ "${RUNTYPE}" = "Cancel" ]] ; then
echo "Exiting Vrecord. Goodbye!" && exit 0
fi
}
# relaunch GUI in GUI mode/exit script in terminal mode
_gui_return(){
if [[ "${GUI}" = 1 ]] ; then
_home_gui
else
exit 0
fi
}
# edit mode
_edit_mode(){
if [[ ! -d "${GTK_RECENT_DIR}" ]] ; then
mkdir -p "${GTK_RECENT_DIR}"
fi
_edit_prefs
# check that gtkdialog _edit_prefs was exited with OK button
if [[ "${EXIT}" = "OK" ]] ; then
_duration_check
# report back options
if [[ -z "${LOGDIR}" ]] ; then
LOGDIR="${DIR}"
echo "As auxiliary files directory was left blank, logs will be written to recording directory at ${DIR}."
fi
echo "Variables set:"
echo " DEVICE_INPUT_CHOICE = ${DEVICE_INPUT_CHOICE}"
echo " AVF_INPUT_CHOICE = ${AVF_INPUT_CHOICE}"
echo " DIR = ${DIR}"
echo " LOGDIR = ${LOGDIR}"
echo " CONTAINER_CHOICE = ${CONTAINER_CHOICE}"
echo " VIDEO_INPUT_CHOICE = ${VIDEO_INPUT_CHOICE}"
echo " AUDIO_INPUT_CHOICE = ${AUDIO_INPUT_CHOICE}"
echo " VIDEO_CODEC_CHOICE = ${VIDEO_CODEC_CHOICE}"
if [[ "${VIDEO_CODEC_CHOICE}" = "FFV1 version 3" ]] ; then
echo " FFV1_SLICE_CHOICE = ${FFV1_SLICE_CHOICE}"
fi
echo " AUDIO_CODEC_CHOICE = ${AUDIO_CODEC_CHOICE}"
echo " VIDEO_BIT_DEPTH_CHOICE = ${VIDEO_BIT_DEPTH_CHOICE}"
echo " AUDIO_MAPPING_CHOICE = ${AUDIO_MAPPING_CHOICE}"
echo " TIMECODE_CHOICE = ${TIMECODE_CHOICE}"
echo " STANDARD_CHOICE = ${STANDARD_CHOICE}"
echo " QCTOOLSXML_CHOICE = ${QCTOOLSXML_CHOICE}"
echo " FRAMEMD5_CHOICE = ${FRAMEMD5_CHOICE}"
echo " EMBED_LOGS_CHOICE = ${EMBED_LOGS_CHOICE}"
echo " PLAYBACKVIEW_CHOICE = ${PLAYBACKVIEW_CHOICE}"
echo " PLAYBACKVIEW_CHOICE_PASS = ${PLAYBACKVIEW_CHOICE_PASS}"
echo " DURATION = ${DURATION}"
echo " TECHNICIAN = ${TECHNICIAN}"
echo " PREFIX = ${PREFIX}"
echo " USER_SUFFIX = ${USER_SUFFIX}"
echo " NO_SUFFIX = ${NO_SUFFIX}"
if [[ "${INVERT_PHASE}" = 'true' ]] ; then
echo -e " \033[101mWARNING: Option to invert phase of second audio channel has been selected\033[0m"
fi
if [ "${VIDEO_CODEC_CHOICE}" = "FFV1 version 3" -a "${CONTAINER_CHOICE}" = "MXF" ] ; then
echo -e " \033[101mWARNING: Incompatible video codecs and CONTAINERs have been selected\033[0m"
elif [ "${VIDEO_CODEC_CHOICE}" = "ProRes" -a "${CONTAINER_CHOICE}" = "MXF" ] ; then
echo -e " \033[101mWARNING: Incompatible video codecs and CONTAINERs have been selected\033[0m"
elif [ "${AUDIO_CODEC_CHOICE}" = "24-bit FLAC" -a "${CONTAINER_CHOICE}" = "QuickTime" ] ; then
echo -e " \033[101mWARNING: Incompatible audio codecs and CONTAINERs have been selected\033[0m"
fi
if [ "${EMBED_LOGS_CHOICE}" = "Yes" -a "${CONTAINER_CHOICE}" != "Matroska" ] ; then
_report -w "WARNING: Logs cannot be embedded in non-Matroska files. This vrecord session will generate logs in ${LOGDIR}, but will not embed them in your video file."
fi
echo ""
# write config file
{
echo "# Set these variables to a valid option or leave as empty quotes (like \"\") to request each run."
for COMMENT_LINE in DEVICE_INPUT_CHOICE VIDEO_INPUT_CHOICE AUDIO_INPUT_CHOICE CONTAINER_CHOICE VIDEO_CODEC_CHOICE FFV1_SLICE_CHOICE AUDIO_CODEC_CHOICE\
VIDEO_BIT_DEPTH_CHOICE AUDIO_MAPPING_CHOICE TIMECODE_CHOICE STANDARD_CHOICE QCTOOLSXML_CHOICE FRAMEMD5_CHOICE \
EMBED_LOGS_CHOICE PLAYBACKVIEW_CHOICE PLAYBACKVIEW_CHOICE_PASS DIR LOGDIR INVERT_PHASE DURATION PREFIX USER_SUFFIX NO_SUFFIX TECHNICIAN \
AVF_INPUT_CHOICE ; do
echo "${COMMENT_LINE}=\"${!COMMENT_LINE}\""
done
} > "${CONFIG_FILE}"
. "${CONFIG_FILE}"
else
_report -d "Editing of preferences was canceled by the user."
fi
if [[ "${GUI}" = 1 ]] ; then
_home_gui
elif [[ "${TIMECODE_SCAN}" = 1 ]] ; then
_report -dt "Scanning each supported type of timecode."
RUNTYPE="passthrough"
else
RUNTYPE="record"
_report -nd "Press [q] to quit, [p] to enter passthrough mode or any other key to proceed: "
read AFTEREDITRESPONSE
if [[ "${AFTEREDITRESPONSE}" = "q" ]] ; then
_report -d "Bye then"
exit 0
elif [[ "${AFTEREDITRESPONSE}" = "p" ]] ; then
RUNTYPE="passthrough"
fi
fi
}
# passthrough and audiopassthrough modes
_passthrough_mode(){
_set_ffplay_options
if [[ "${MEDIA_PLAYER_CHOICE}" = "mpv" ]] ; then
_check_mpv
"${FFMPEG_DECKLINK}" -nostats "${GRAB_INPUT[@]}" 2> >(tee /tmp/vrecord_input.log 1>&2) \
"${PIPE_OUTPUT[@]}" | \
mpv "${MPVOPTS[@]}" --title="${WINDOW_NAME}" -
else
"${FFPLAY_DECKLINK}" "${FFPLAY_OPTIONS[@]}" 2> >(tee /tmp/vrecord_input.log 1>&2)
fi
_gui_return
}
_audiopassthrough_mode(){
PLAYBACKFILTER="\
[aid1]asplit=6[a][b][c][d][e][ao],\
[a]showvolume=t=0:h=17:w=200[a1],\
[b]pan=stereo|c0=c0|c1=c1,avectorscope[b1],\
[c]pan=stereo|c0=c2|c1=c3,avectorscope[c1],\
[d]showspectrum=s=535x672:color=rainbow:legend=1[d1],\
[e]showwaves=split_channels=1:s=500x500:mode=cline[e1],\
[vid1][b1][c1][e1][d1]xstack=inputs=5:layout=0_0|0_h0|0_h0+h1|w0_0|w1_h0[abcde1],\
[abcde1][a1]overlay=10:10[vo]"
echo "ESC quit" > ~/.config/mpv/input.conf
_check_mpv
"${FFMPEG_DECKLINK}" -nostdin -hide_banner -nostats "${INPUTOPTIONS[@]}" "${GRAB_INPUT[@]}" "${PIPE_OUTPUT[@]}" 2> /tmp/vrecord_input.log | \
mpv - --title="${WINDOW_NAME}" -lavfi-complex "${PLAYBACKFILTER}"
_gui_return
}
# check validity of duration value
_duration_check(){
# Sets up function to verify validity of duration settings
if [[ -n "${DURATION}" ]] ; then
if ! [[ "${DURATION}" =~ ^$|^[0-9]+$|^[0-9]+\.[0-9]*$|^\.[0-9]+$ ]] ; then
_report -w "Illegal value for recording time. Input must only be numbers."
exit 1
fi
if (( $(bc <<< "${DURATION} == 0") )) ; then
_report -w "A recording duration of zero is invalid."
exit 1
fi
fi
}
# create a capture log of decisions made in vrecord
_writeingestlog(){
if [[ "${INGESTLOG}" ]] ; then
KEY="${1}"
shift
VALUE="${@}"
# need to add yaml style escaping
echo "${KEY}: ${VALUE}" >> "${INGESTLOG}"
else
_report -wt "The _writeingestlog function was called, but the ingestlog file (${INGESTLOG}) is not declared."
fi
}
# create a jpeg of qc data graphs for quick assessment
_qcgraphimage(){
# get audio data
qctools_CONFIGFILE_A="$(/usr/bin/mktemp /tmp/astats_A_XXXXXXXXX).csv"
"${ZCAT_COMMAND}" "$1" | perl -nle 'print if not m{lavfi.(?!astats.Overall.Min_level|astats.Overall.Max_level|astats.Overall.Peak_level|aphasemeter.phase)}' | xmlstarlet select -t -m "//ffprobe:ffprobe/frames/frame[@media_type='audio']" \
-v "@pkt_pts_time" -o " " \
-v "tag[@key='lavfi.astats.Overall.Max_level']/@value" -o " " \
-v "tag[@key='lavfi.astats.Overall.Min_level']/@value" -o " " \
-v "tag[@key='lavfi.astats.Overall.Peak_level']/@value" -o " " \
-v "tag[@key='lavfi.aphasemeter.phase']/@value" -n > "${qctools_CONFIGFILE_A}"
# get video data
qctools_CONFIGFILE_V="$(/usr/bin/mktemp /tmp/signalstats_V_XXXXXXXXX).csv"
"${ZCAT_COMMAND}" "$1" | perl -nle 'print if not m{lavfi.(?!signalstats.BRNG|signalstats.TOUT|signalstats.SATMAX|signalstats.SATAVG|ssim.Y|ssim.U|ssim.V)}' | xmlstarlet select -t -m "//ffprobe:ffprobe/frames/frame[@media_type='video']" \
-v "@pkt_pts_time" -o " " \
-v "tag[@key='lavfi.signalstats.TOUT']/@value" -o " " \
-v "tag[@key='lavfi.signalstats.BRNG']/@value" -o " " \
-v "tag[@key='lavfi.signalstats.SATMAX']/@value" -o " " \
-v "tag[@key='lavfi.signalstats.SATAVG']/@value" -o " " \
-v "tag[@key='lavfi.ssim.Y']/@value" -o " " \
-v "tag[@key='lavfi.ssim.U']/@value" -o " " \
-v "tag[@key='lavfi.ssim.V']/@value" -n > "${qctools_CONFIGFILE_V}"
# determine SATURATION scale
VBitdepth="$("${ZCAT_COMMAND}" "$1" | perl -nle 'print if not m{<frame|</frame|<tag}' | xmlstarlet sel -t -v "//stream[@codec_type='video']/@pix_fmt" -n
)"
if [[ "${VBitdepth}" = "uyvy422" ]] || [[ "${VBitdepth}" = "yuv422p" ]] ; then
SATpalette="(0'#a0a0a0',20'#bebebe',44.35'#c8c800',80'#a0ff20',88.7'#00ff00',103.45'#006400',108.2'#00ff00',113.2'#a0ff20',118.2'#ffa500',124'#ff0000')"
SATcbrange="[0:124]"
SATyrange="[0:183]"
elif [[ "${VBitdepth}" = "yuv422p10le" ]] ; then
SATpalette="(0'#a0a0a0',80'#bebebe',177.4'#c8c800',322'#a0ff20',354.8'#00ff00',413.8'#006400',433.46'#00ff00',453.13'#a0ff20',472.8'#ffa500',496'#ff0000')"
SATcbrange="[0:496]"
SATyrange="[0:725]"
fi
echo "set terminal jpeg size 1920, 1080
set output '${LOGDIR}/${FULL_OUTPUT_ID}_QC_output_graphs.jpeg'
set term jpeg font 'times,12'
set multiplot layout 7, 1 title '${ID} QC Data Graphs (ver.2)' margins screen .05,.93, .05, .93 spacing screen 0, char .7
set style line 11 lc rgb '#808080' lt 1
set border 15 back ls 11
set format x '%tH:%tM:%.1tS' time
set format x2 '%tH:%tM:%.1tS' time
set x2tics border out nomirror
set xtics border mirror in scale 1.5,.7 format ''
set ytics border out nomirror font 'times,10'
set grid y
set grid x
set grid mxtics
set colorbox vertical back user origin graph 1.01,0 size char 1,6
set cbtics font 'times,10'
set key inside right top reverse samplen .00
unset key
set yrange [-2147483648.:2147483648.]
set palette model RGB defined (-0.8'#ff0000',-0.6'#ffa500',-0.4'#a0ff20',-0.2'#00ff00',0'#006400',0.2'#00ff00',0.4'#a0ff20',.6'#ffa500',0.8'#ff0000') maxcolors 128
set cbrange [-2147483648.:2147483648.]
set style fill solid
set label 1 'Min/Max Level' at graph .5,.9 center front
plot '${qctools_CONFIGFILE_A}' using 1:2:2 with boxes palette title 'Max Level', '' using 1:3:3 with boxes palette title 'Min Level'
unset cbrange
unset x2tics
set yrange [-70:0]
set cbrange [0:-50.]
set palette model RGB defined (-50'#006400',-35'#00ff00',-15'#a0ff20',-5'#ffa500',-0.5'#ff0000') maxcolors 128
set style line 15 linecolor palette lw 4
set label 1 'Peak Level (dB)' at graph .5,.9 front
plot '' using 1:4:4 with lines ls 15 title 'Peak Level (dB)'
unset yrange
unset cbrange
set palette model RGB defined (-0.8'#ff0000',-0.6'#ffa500',-0.4'#a0ff20',0'#00ff00',1'#006400') maxcolors 128
set cbrange [-0.8:1]
set yrange [-1:1]
set style line 25 linecolor palette lw 4
set label 1 'Audio Phase' at graph .5,.9 front
plot '' using 1:5:5 with lines ls 25 title 'Audio Phase'
unset yrange
unset cbrange
set palette model RGB defined (0'#006400',0.01'#00ff00',0.02'#fffb20',.04'#ffa500',0.05'#ff0000') maxcolors 128
set cbrange [0:.05]
set yrange [0:.1]
set label 1 '% Outside of Broadcast Range' at graph .5,.9 front
plot '${qctools_CONFIGFILE_V}' using 1:3:3 with boxes palette title '% Outside of Broadcast Range'
unset yrange
set style line 10 linecolor '#804080' lw 2
set label 1 '% Temporal OUTliers' at graph .5,.9 front
plot '' using 1:2 with lines ls 10 title '% Temporal OUTliers'
unset yrange
unset cbrange
set style line 1 linecolor '#f03232' lw 2
set style line 2 linecolor '#006400' lw 2
set style line 3 linecolor '#00ff00' lw 2
set style line 4 linecolor '#f03232' lw 2
set style line 5 linecolor '#000000' lw 2
set style line 6 linecolor '#00008b' lw 2
set yrange ${SATyrange}
set palette model RGB defined ${SATpalette} maxcolors 128
set cbrange ${SATcbrange}
unset label
set key at graph 1.06,.5 center vertical Left noopaque noreverse font 'times,9'
set label 1 'Saturation Level' at graph .5,.9 center front
plot '' using 1:4:4 with boxes palette title 'SatMax', '' using 1:5 with lines ls 5 title 'SatAvg'
unset yrange
unset cbrange
unset xtics
unset key
set key at graph 1.06,.5 right vertical Right noopaque noreverse
set x2tics border mirror in scale 1.5,.7 format ''
set xtics border out nomirror format '%tH:%tM:%.1tS' time
set label 1 'Structural SImilarity Metric' at graph .5,.1 center front
plot '' using 1:6 with lines ls 5 title 'SSIM-Y', '' using 1:7 with lines ls 2 title 'SSIM-U', '' using 1:8 with lines ls 6 title 'SSIM-V'
" | gnuplot
}
# decipher vrecord options as specified by user
_lookup_choice(){
case "${2}" in
"quit"|"Quit"|"QUIT"|"q"|"Q") _report -dt "Bye." ; exit ;;
esac
case "${1}" in
# video inputs
"Composite") VIDEO_INPUT="composite" ;;
"SDI") VIDEO_INPUT="sdi" ;;
"Component") VIDEO_INPUT="component" ;;
"S-Video") VIDEO_INPUT="s_video" ;;
# audio inputs
"Analog") AUDIO_INPUT="analog" ;;
"SDI Embedded Audio") AUDIO_INPUT="embedded" ;;
"Digital Audio (AES/EBU)") AUDIO_INPUT="aes_ebu" ;;
# container
"QuickTime")
EXTENSION="mov"
MIDDLEOPTIONS+=(-movflags write_colr)
FORMAT="mov" ;;
"Matroska")
EXTENSION="mkv"
FORMAT="matroska" ;;
"AVI")
EXTENSION="avi"
FORMAT="avi" ;;
"MXF")
EXTENSION="mxf"
FORMAT="mxf" ;;
# video codec
"Uncompressed Video")
if [[ "${PIXEL_FORMAT}" = "yuv422p10" ]] ; then
VIDEOCODECNAME="Uncompressed 10-bit 4:2:2"
MIDDLEOPTIONS+=(-c:v v210)
elif [[ "${PIXEL_FORMAT}" = "uyvy422" ]] ; then
VIDEOCODECNAME="Uncompressed 8-bit 4:2:2"
MIDDLEOPTIONS+=(-c:v rawvideo -pix_fmt uyvy422 -tag:v 2vuy)
fi ;;
"FFV1 version 3")
VIDEOCODECNAME="FFV1 version 3"
MIDDLEOPTIONS+=(-c:v ffv1 -level 3 -g 1 -slices "${FFV1_SLICE_CHOICE}" -slicecrc 1)
SUFFIX="_ffv1" ;;
"JPEG2000")
VIDEOCODECNAME="JPEG2000"
MIDDLEOPTIONS+=(-c:v libopenjpeg)
SUFFIX="_j2k" ;;
"ProRes")
VIDEOCODECNAME="Apple ProRes 422"
MIDDLEOPTIONS+=(-c:v prores_ks -flags +ildct)
SUFFIX="_prores" ;;
# video pixel format and bit depth
"10 bit") PIXEL_FORMAT="yuv422p10" ;;
"8 bit") PIXEL_FORMAT="uyvy422" ;;
# audio codec
"24-bit PCM")
MIDDLEOPTIONS+=(-c:a pcm_s24le) ;;
"24-bit FLAC")
MIDDLEOPTIONS+=(-c:a flac) ;;
# audio mappings
"2 Stereo Tracks (Channels 1 & 2 -> 1st Track Stereo, Channels 3 & 4 -> 2nd Track Stereo)")
AUDIOMAP="[0:a:0]pan=stereo| c0=c0 | c1=${PHASE_VALUE}c1[stereo1];[0:a:0]pan=stereo| c0=c2 | c1=c3[stereo2]"
AUDIO_CHANNEL_MAP+=(-map "[stereo1]")
AUDIO_CHANNEL_MAP+=(-map "[stereo2]") ;;
"1 Stereo Track (From Channels 1 & 2)")
AUDIOMAP="[0:a:0]pan=stereo| c0=c0 | c1=${PHASE_VALUE}c1[stereo1]"
AUDIO_CHANNEL_MAP+=(-map "[stereo1]") ;;
"1 Stereo Track (From Channels 3 & 4)")
AUDIOMAP="[0:a:0]pan=stereo| c0=c2 | c1=${PHASE_VALUE}c3[stereo1]"
AUDIO_CHANNEL_MAP+=(-map "[stereo1]") ;;
"Channel 1 -> 1st Track Mono, Channel 2 -> 2nd Track Mono")
AUDIOMAP="[0:a:0]pan=mono| c0=c0[mono1];[0:a:0]pan=mono| c0=${PHASE_VALUE}c1[mono2]"
AUDIO_CHANNEL_MAP+=(-map "[mono1]")
AUDIO_CHANNEL_MAP+=(-map "[mono2]") ;;
"Channel 2 -> 1st Track Mono, Channel 1 -> 2nd Track Mono")
AUDIOMAP="[0:a:0]pan=mono| c0=${PHASE_VALUE}c1[mono1];[0:a:0]pan=mono| c0=c0[mono2]"
AUDIO_CHANNEL_MAP+=(-map "[mono1]")
AUDIO_CHANNEL_MAP+=(-map "[mono2]") ;;
"Channel 1 -> Single Track Mono")
AUDIOMAP="[0:a:0]pan=mono| c0=c0[mono1]"
AUDIO_CHANNEL_MAP+=(-map "[mono1]") ;;
"Channel 2 -> Single Track Mono")
AUDIOMAP="[0:a:0]pan=mono| c0=c1[mono1]"
AUDIO_CHANNEL_MAP+=(-map "[mono1]") ;;
# timecode options
"none"|"rp188vitc"|"rp188vitc2"|"rp188ltc"|"rp188any"|"vitc"|"vitc2"|"serial")
TC_TYPE="${1}"
EXTRAINPUTOPTIONS+=(-timecode_format "${TC_TYPE}")
MIDDLEOPTIONS+=(-map_metadata 0:s:v:0)
if [[ "${TC_TYPE}" != "none" ]] ; then
if [[ "${RUNTYPE}" = "passthrough" ]] ; then
TIMECODE_OVERLAY=",drawtext=fontfile=${DEFAULTFONT}:x=(w-text_w)*0.5:y=(h-text_h)*0.75:fontcolor=white:box=1:boxcolor=gray@0.7:boxborderw=4:fontsize=22:shadowx=1:shadowy=1:text=%{metadata\\\:timecode\\\:no ${TC_TYPE} timecode}"
fi
TC_TMP=$(/usr/bin/mktemp /tmp/timecode_XXXXXXXXX)
TC_WRITE=",metadata=mode=print:file=${TC_TMP}"
fi
declare TC_TYPE "${TC_TYPE}"
;;
# video standard
"NTSC")
STANDARD="ntsc"
DECKLINK_FPS="30000/1001"
RECORDINGFILTER="setsar=40/27,setdar=4/3"
MIDDLEOPTIONS+=(-color_primaries smpte170m)
MIDDLEOPTIONS+=(-color_trc bt709)
MIDDLEOPTIONS+=(-colorspace smpte170m) ;;
"PAL")
STANDARD="pal "
DECKLINK_FPS="25000/1000"
RECORDINGFILTER="setsar=16/15,setdar=4/3"
MIDDLEOPTIONS+=(-color_primaries bt470bg)
MIDDLEOPTIONS+=(-color_trc bt709)
MIDDLEOPTIONS+=(-colorspace bt470bg) ;;
# playback views
"Unfiltered") PLAYBACKFILTER="" ;;
"Quality Control View (mpv)") MEDIA_PLAYER_CHOICE="mpv" ;;
"Broadcast Range Visual")
PLAYBACKFILTER="\
${PLAYBACK_FILTER_ADJUSTMENT}split=5[a][b][c][d][e];\
[a]copy${TIMECODE_OVERLAY}[a1];\
[b]field=top,${WAVEFORM_FILTER}[b1];\
[c]field=bottom,${WAVEFORM_FILTER}[c1];\
[d]${VECTORSCOPE_FILTER}[d1];\
[e]scale=512:ih,signalstats=out=brng[e1];\
[a1][b1][c1][e1][d1]xstack=inputs=5:layout=0_0|0_h0|0_h0+h1|w0_0|w0_h0" ;;
"Full Range Visual")
PLAYBACKFILTER="\
${PLAYBACK_FILTER_ADJUSTMENT}split=5[a][b][c][d][e];\
[a]copy${TIMECODE_OVERLAY}[a1];\
[b]field=top,${WAVEFORM_FILTER}[b1];\
[c]field=bottom,${WAVEFORM_FILTER}[c1];\
[d]${VECTORSCOPE_FILTER}[d1];\
[e]format=yuv444p,pseudocolor=if(between(1\,val\,amax)+between(val\,254\,amax)\,65\,-1):if(between(1\,val\,amax)+between(val\,254\,amax)\,100\,-1):if(between(1\,val\,amax)+between(val\,254\,amax)\,212\,-1),scale=512:ih[e1];\
[a1][b1][c1][e1][d1]xstack=inputs=5:layout=0_0|0_h0|0_h0+h1|w0_0|w0_h0" ;;
"Visual + Numerical")
PLAYBACKFILTER="\
${PLAYBACK_FILTER_ADJUSTMENT}split=6[a][b][c][d][e][f];\
[a]copy${TIMECODE_OVERLAY}[a1];\
[b]field=top,${WAVEFORM_FILTER}[b1];\
[c]field=bottom,${WAVEFORM_FILTER}[c1];\
[d]${VECTORSCOPE_FILTER}[d1];\
[e]signalstats=out=brng:stat=brng+vrep+tout,scale=512:ih,split[e1][e2];\
[e2]format=yuv422p,geq=lum=60:cb=128:cr=128,\
scale=180:ih+512,setsar=1/1,\
drawtext=fontcolor=white:fontsize=22:\
fontfile=${DEFAULTFONT}:textfile=/tmp/drawtext.txt,\
drawtext=fontcolor=white:fontsize=17:\
fontfile=${DEFAULTFONT}:textfile=/tmp/drawtext2.txt,\
drawtext=fontcolor=white:fontsize=52:\
fontfile=${DEFAULTFONT}:textfile=/tmp/drawtext3.txt[e3];\
[f]scale=iw+512+180:82,format=yuv422p,geq=lum=60:cb=128:cr=128,drawtext=fontcolor=white:fontsize=22:\
fontfile=${DEFAULTFONT}:textfile=/tmp/vrecord_input.log:\
reload=1:y=82-th[f1];\
[e3][a1][b1][c1][e1][d1][f1]xstack=inputs=7:layout=0_0|w0_0|w0_h1|w0_h1+h2|w0+w1_0|w0+w1_h1|0_h0" ;;
"Color Matrix")
HUE=20
SAT=0.3
PLAYBACKFILTER="\
${PLAYBACK_FILTER_ADJUSTMENT}scale=iw/4:ih/4,\
split=9[x][hm][hp][sm][sp][hmsm][hmsp][hpsm][hpsp];\
[hm]hue=h=-${HUE}[hm1];\
[hp]hue=h=${HUE}[hp1];\
[sm]hue=s=1-${SAT}[sm1];\
[sp]hue=s=1+${SAT}[sp1];\
[hmsm]hue=h=-${HUE}:s=1-${SAT}[hmsm1];\
[hmsp]hue=h=-${HUE}:s=1+${SAT}[hmsp1];\
[hpsm]hue=h=${HUE}:s=1-${SAT}[hpsm1];\
[hpsp]hue=h=${HUE}:s=1+${SAT}[hpsp1];\
[hpsm1][hp1][hpsp1][sm1][x][sp1][hmsm1][hm1][hmsp1]xstack=inputs=9:layout=0_0|0_h0|0_h0+h1|w0_0|w0_h0|w0_h0+h1|w0+w1_0|w0+w1_h0|w0+w1_h0+h1" ;;
"Bit Planes")
if [[ "${PIXEL_FORMAT}" = "uyvy422" ]] ; then
BITS=8
SPLIT="8[b0][b1][b2][b3][b4][b5][b6][b7]"
STACK="[b0c][b1c][b2c][b3c][b4c][b5c][b6c][b7c]hstack=8,format=yuv444p,drawgrid=w=iw/8:h=ih:t=2:c=green@0.5"
elif [[ "${PIXEL_FORMAT}" = "yuv422p10" ]] ; then
BITS=10
SPLIT="10[b0][b1][b2][b3][b4][b5][b6][b7][b8][b9]"
STACK="\
[b8]bitplanenoise=bitplane=2,crop=iw/10:ih:(iw/10)*8:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-9))*pow(2\\,9),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.2}:y=0:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.2}:y=20:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.2}:y=40:fontcolor=white:fontsize=20[b8c];\
[b9]bitplanenoise=bitplane=1,crop=iw/10:ih:(iw/10)*9:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-10))*pow(2\\,10),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.1}:y=0:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.1}:y=20:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.1}:y=40:fontcolor=silver:fontsize=20[b9c];\
[b0c][b1c][b2c][b3c][b4c][b5c][b6c][b7c][b8c][b9c]hstack=10,format=yuv444p,drawgrid=w=iw/10:h=ih:t=2:c=green@0.5"
fi
PLAYBACKFILTER="\
${PLAYBACK_FILTER_ADJUSTMENT}format=yuv420p10le|yuv422p10le|yuv444p10le|yuv440p10le,split=${SPLIT};\
[b0]bitplanenoise=bitplane=10,crop=iw/${BITS}:ih:(iw/${BITS})*0:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-1))*pow(2\\,1),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.10}:y=0:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.10}:y=20:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.10}:y=40:fontcolor=white:fontsize=20[b0c];\
[b1]bitplanenoise=bitplane=9,crop=iw/${BITS}:ih:(iw/${BITS})*1:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-2))*pow(2\\,2),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.9}:y=0:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.9}:y=20:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.9}:y=40:fontcolor=silver:fontsize=20[b1c];\
[b2]bitplanenoise=bitplane=8,crop=iw/${BITS}:ih:(iw/${BITS})*2:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-3))*pow(2\\,3),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.8}:y=0:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.8}:y=20:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.8}:y=40:fontcolor=white:fontsize=20[b2c];\
[b3]bitplanenoise=bitplane=7,crop=iw/${BITS}:ih:(iw/${BITS})*3:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-4))*pow(2\\,4),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.7}:y=0:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.7}:y=20:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.7}:y=40:fontcolor=silver:fontsize=20[b3c];\
[b4]bitplanenoise=bitplane=6,crop=iw/${BITS}:ih:(iw/${BITS})*4:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-5))*pow(2\\,5),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.6}:y=0:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.6}:y=20:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.6}:y=40:fontcolor=white:fontsize=20[b4c];\
[b5]bitplanenoise=bitplane=5,crop=iw/${BITS}:ih:(iw/${BITS})*5:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-6))*pow(2\\,6),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.5}:y=0:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.5}:y=20:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.5}:y=40:fontcolor=silver:fontsize=20[b5c];\
[b6]bitplanenoise=bitplane=4,crop=iw/${BITS}:ih:(iw/${BITS})*6:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-7))*pow(2\\,7),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.4}:y=0:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.4}:y=20:fontcolor=white:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.4}:y=40:fontcolor=white:fontsize=20[b6c];\
[b7]bitplanenoise=bitplane=3,crop=iw/${BITS}:ih:(iw/${BITS})*7:0,lutyuv=u=(maxval/2):v=(maxval/2):y=bitand(val\\,pow(2\\,10-8))*pow(2\\,8),pad=iw:ih+64:0:64,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.0.3}:y=0:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.1.3}:y=20:fontcolor=silver:fontsize=20,drawtext=fontfile=${DEFAULTFONT}:text=%{metadata\\\:lavfi.bitplanenoise.2.3}:y=40:fontcolor=silver:fontsize=20[b7c];\
${STACK}" ;;
# others
"Yes"|"Yes, after recording"|"Yes, concurrent with recording"|"No") ;;
*) _report -w "Error: Not a valid option, select a valid number." ; return 1 ;;
esac
}
_frames_to_hhmmss(){
H=$(echo "${i} / (60 * 60 * ${DECKLINK_FPS})" | bc)
M=$(echo "(${i} - (${H}*60 * 60 * ${DECKLINK_FPS})) / (60 * ${DECKLINK_FPS})" | bc)
S="$(echo "scale=3;(${i} - (${H}*60 * 60 * ${DECKLINK_FPS})-($M*60*${DECKLINK_FPS})) / (${DECKLINK_FPS})" | bc)"
Ss=$(echo "${S}" | cut -d. -f1 | bc)
Sm=$(echo "${S}" | cut -d. -f2 | bc)
printf "%02d:%02d:%02d.%03d\n" "${H}" "${M}" "${Ss}" "${Sm}"
}
# set up drawtext.txt files for Visual + Numerical playback view
_set_up_drawtext(){
echo -e "%{pts:hms}
Y
Low %{metadata:lavfi.signalstats.YLOW}
Avg %{metadata:lavfi.signalstats.YAVG}
High %{metadata:lavfi.signalstats.YHIGH}
Diff %{metadata:lavfi.signalstats.YDIF}
U
Low %{metadata:lavfi.signalstats.ULOW}
Avg %{metadata:lavfi.signalstats.UAVG}
High %{metadata:lavfi.signalstats.UHIGH}