OXYGENHUB
◆ main openmsx / share / scripts / _record_chunks.tcl
7.2 KiB · Tcl · last changed chore(): initial commit 1 month ago source blame history raw
1
namespace eval record_chunks {
2
3
set_help_text record_chunks \
4
{Records videos in doublesize format, but has extra options to record for a
5
limited amount of time and to chop up videos in chunks of a certain number of
6
seconds.
7
8
Usage:
9
    record_chunks [-chunktime <chunktime>] [-totaltime <totaltime> | -numchunks <numchunks>] start filename
10
    record_chunks stop
11
12
The chunktime parameter controls the maximum time for each chunk (default:
13
14:59 for YouTube) in seconds and the totaltime parameter controls the total
14
time to record in seconds (default: infinite). Instead of the totalchunks
15
parameter, you can also use the numchunks parameter to control the total time
16
as a multiple of the chunk time. The options are mutually exclusive.
17
18
The chunks are recorded with a chunk-number suffix behind filename. Do not use
19
an extension with filename, just a name is enough. Always provide the options
20
first.
21
22
Examples:
23
    record_chunks -numchunks 1 start simplegame
24
        Records a movie of 14:59 (maximum time for YouTube) to simplegame.avi
25
    record_chunks start longgame
26
        Records an infinite number of movies of 14:59 (maximum time for
27
        YouTube), until you use record_chunks stop. Names are longname01.avi,
28
        longname02.avi, etc.
29
    record_chunks -chunktime 60 -totaltime 230 start partgame
30
        Records movies of 1 minute until the total recorded time is 3:50, names
31
        are partgame01.avi, partgame02.avi, partgame03.avi and partgame04.avi.
32
}
33
34
variable filenamebase
35
variable iteration
36
variable chunk_time
37
variable total_time
38
variable next_after_id
39
40
proc record_chunks {args} {
41
	variable filenamebase
42
	variable iteration
43
	variable chunk_time
44
	variable total_time
45
	variable next_after_id
46
47
	# the defaults
48
	set chunk_time 899 ;# max time for a YouTube video
49
	set total_time -1  ;# record until someone says stop ..
50
	set num_chunks -1  ;# .. or till we recorded this many chunks
51
52
	while (1) {
53
		switch -- [lindex $args 0] {
54
			"-chunktime" {
55
				set chunk_time [lindex $args 1]
56
				set args [lrange $args 2 end]
57
			}
58
			"-totaltime" {
59
				set total_time [lindex $args 1]
60
				set args [lrange $args 2 end]
61
			}
62
			"-numchunks" {
63
				set num_chunks [lindex $args 1]
64
				set args [lrange $args 2 end]
65
			}
66
			"default" {
67
				break
68
			}
69
		}
70
	}
71
72
	if {($total_time > 0) && ($num_chunks > 0)} {
73
		error "You can't use both -numchunks and -totaltime options at the same time."
74
	}
75
76
	# do this outside of the loop, so that the order of options isn't too strict
77
	if {$num_chunks > 0} {
78
		set total_time [expr {$num_chunks * $chunk_time}]
79
	}
80
81
	switch -- [lindex $args 0] {
82
		"start" {
83
			if {[llength $args] != 2} {
84
				error "Expected another argument: the name of your video!"
85
			}
86
			if {[dict get [record status] status] ne "idle"} {
87
				error "Already recording!"
88
			}
89
			set filenamebase [lindex $args 1]
90
			set iteration 0
91
			record_next_part
92
		}
93
		"stop" {
94
			if {[llength $args] != 1} {
95
				error "Too many arguments. Stop is just stop."
96
			}
97
			if {![info exists record_chunks::iteration]} {
98
				error "No recording in progress..."
99
			}
100
			after cancel $next_after_id
101
			stop_recording
102
		}
103
		"default" {
104
			error "Syntax error in command."
105
		}
106
	}
107
}
108
109
proc stop_recording {} {
110
	variable iteration
111
	unset iteration
112
	record stop
113
	puts "Stopped recording..."
114
}
115
116
proc record_next_part {} {
117
	variable iteration
118
	variable next_after_id
119
	variable filenamebase
120
	variable chunk_time
121
	variable total_time
122
123
	set cmd record_chunks::record_next_part
124
	set time_to_record $chunk_time
125
	if {$total_time > 0} {
126
		set time_left [expr {$total_time - ($chunk_time * $iteration)}]
127
		if {$time_left <= $chunk_time} {
128
			set cmd record_chunks::stop_recording
129
			set time_to_record $time_left
130
		}
131
	}
132
133
	record stop
134
135
	incr iteration
136
	if {$iteration == 1 && $cmd eq "record_chunks::stop_recording"} {
137
		# if we're only going to record one movie, no need to number it
138
		set fname $filenamebase
139
	} else {
140
		set fname [format "%s%02d" $filenamebase $iteration]
141
	}
142
	record start -doublesize $fname
143
144
	set next_after_id [after time $time_to_record $cmd]
145
	puts "Recording to $fname for [utils::format_time $time_to_record]..."
146
}
147
148
namespace export record_chunks
149
150
set_help_text record_chunks_on_framerate_changes \
151
{Records videos as with the normal record command, but starts recording to a
152
new video file if the framerate of the VDP changes.
153
Note that this will only work if you use either use the -prefix option for the
154
command, or the automatic file name generation. If you specify an exact
155
filename without the -prefix option, openMSX will record all chunks to the same
156
file.
157
158
Stop recording with the "record_chunks_on_framerate_changes stop" command.
159
160
Examples:
161
    record_chunks_on_framerate_changes start -triplesize -prefix UR
162
        Records a movie in triplesize in files UR0001.avi, UR0002.avi etc.
163
        Each subsequent AVI has a different frame rate.
164
    record_chunks_on_framerate_changes stop
165
        Stop recording and clean up frame rate detection stuff internally.
166
}
167
168
169
variable vdp_write_watchpoint ""
170
variable after_reset_id ""
171
variable prev_was_ntsc_mode true
172
variable record_args ""
173
174
proc vdp_write_check {} {
175
	variable prev_was_ntsc_mode
176
177
	set safety_check_output [safety_check]
178
	if {$safety_check_output ne ""} {
179
		puts $safety_check_output
180
		return
181
	}
182
183
	set ntsc_mode [expr {([vdpreg 9] & 2) == 0}]
184
	if {$ntsc_mode != $prev_was_ntsc_mode} {
185
		restart_recording "from [expr {$prev_was_ntsc_mode ? 60 : 50}] to [expr {$ntsc_mode ? 60 : 50}] Hz"
186
		set prev_was_ntsc_mode $ntsc_mode
187
	}
188
}
189
190
proc safety_check {} {
191
	if {[dict get [record status] status] eq "idle"} {
192
		# oh, we weren't even recording... let's shut down
193
		disable_monitoring
194
		return "Stopped recording chunks on frame rate changes. I found out late, please use record_chunks_on_framerate_changes stop next time, so I'll find out sooner..."
195
	}
196
	return ""
197
}
198
199
proc restart_recording { reason } {
200
	variable record_args
201
	puts "Frame rate change detected $reason. Starting recording to next video file."
202
	record stop
203
	puts [eval [linsert $record_args 0 record]]
204
}
205
206
proc on_reset {} {
207
	variable after_reset_id
208
	set after_reset_id [after boot [namespace code on_reset]]
209
	set safety_check_output [safety_check]
210
	if {$safety_check_output ne ""} {
211
		puts $safety_check_output
212
		return
213
	}
214
	restart_recording "due to a reset"
215
}
216
217
proc disable_monitoring {} {
218
	variable vdp_write_watchpoint
219
	variable after_reset_id
220
	debug remove_watchpoint $vdp_write_watchpoint
221
	set vdp_write_watchpoint ""
222
	after cancel $after_reset_id
223
	set after_reset_id ""
224
}
225
226
proc record_chunks_on_framerate_changes { args } {
227
	variable record_args
228
	variable vdp_write_watchpoint
229
	variable after_reset_id
230
	variable prev_was_ntsc_mode
231
232
	if {$args eq "stop" && $vdp_write_watchpoint ne ""} {
233
		disable_monitoring
234
		record stop
235
		return "Stopped recording chunks on framerate changes."
236
	}
237
238
	set record_args $args
239
	set response [eval [linsert $record_args 0 record]]
240
	if {$vdp_write_watchpoint eq "" && [dict get [record status] status] eq "recording"} {
241
		set prev_was_ntsc_mode [expr {([vdpreg 9] & 2) == 0}]
242
		set vdp_write_watchpoint [debug set_watchpoint write_io 0x99 1 {
243
			record_chunks::vdp_write_check
244
		}]
245
		set after_reset_id [after boot [namespace code on_reset]]
246
		return "Started recording chunks on framerate changes...\n$response"
247
	}
248
}
249
250
namespace export record_chunks_on_framerate_changes
251
252
} ;# namespace record_chunks
253
254
namespace import record_chunks::*