File: _record_chunks.tcl

package info (click to toggle)
openmsx 21.0%2Bdfsg-2
  • links: PTS
  • area: main
  • in suites: forky
  • size: 28,132 kB
  • sloc: cpp: 244,928; xml: 54,344; tcl: 15,603; python: 5,335; perl: 281; sh: 78; makefile: 57
file content (254 lines) | stat: -rw-r--r-- 7,398 bytes parent folder | download | duplicates (6)
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
namespace eval record_chunks {

set_help_text record_chunks \
{Records videos in doublesize format, but has extra options to record for a
limited amount of time and to chop up videos in chunks of a certain number of
seconds.

Usage:
    record_chunks [-chunktime <chunktime>] [-totaltime <totaltime> | -numchunks <numchunks>] start filename
    record_chunks stop

The chunktime parameter controls the maximum time for each chunk (default:
14:59 for YouTube) in seconds and the totaltime parameter controls the total
time to record in seconds (default: infinite). Instead of the totalchunks
parameter, you can also use the numchunks parameter to control the total time
as a multiple of the chunk time. The options are mutually exclusive.

The chunks are recorded with a chunk-number suffix behind filename. Do not use
an extension with filename, just a name is enough. Always provide the options
first.

Examples:
    record_chunks -numchunks 1 start simplegame
        Records a movie of 14:59 (maximum time for YouTube) to simplegame.avi
    record_chunks start longgame
        Records an infinite number of movies of 14:59 (maximum time for
        YouTube), until you use record_chunks stop. Names are longname01.avi,
        longname02.avi, etc.
    record_chunks -chunktime 60 -totaltime 230 start partgame
        Records movies of 1 minute until the total recorded time is 3:50, names
        are partgame01.avi, partgame02.avi, partgame03.avi and partgame04.avi.
}

variable filenamebase
variable iteration
variable chunk_time
variable total_time
variable next_after_id

proc record_chunks {args} {
	variable filenamebase
	variable iteration
	variable chunk_time
	variable total_time
	variable next_after_id

	# the defaults
	set chunk_time 899 ;# max time for a YouTube video
	set total_time -1  ;# record until someone says stop ..
	set num_chunks -1  ;# .. or till we recorded this many chunks

	while (1) {
		switch -- [lindex $args 0] {
			"-chunktime" {
				set chunk_time [lindex $args 1]
				set args [lrange $args 2 end]
			}
			"-totaltime" {
				set total_time [lindex $args 1]
				set args [lrange $args 2 end]
			}
			"-numchunks" {
				set num_chunks [lindex $args 1]
				set args [lrange $args 2 end]
			}
			"default" {
				break
			}
		}
	}

	if {($total_time > 0) && ($num_chunks > 0)} {
		error "You can't use both -numchunks and -totaltime options at the same time."
	}

	# do this outside of the loop, so that the order of options isn't too strict
	if {$num_chunks > 0} {
		set total_time [expr {$num_chunks * $chunk_time}]
	}

	switch -- [lindex $args 0] {
		"start" {
			if {[llength $args] != 2} {
				error "Expected another argument: the name of your video!"
			}
			if {[dict get [record status] status] ne "idle"} {
				error "Already recording!"
			}
			set filenamebase [lindex $args 1]
			set iteration 0
			record_next_part
		}
		"stop" {
			if {[llength $args] != 1} {
				error "Too many arguments. Stop is just stop."
			}
			if {![info exists record_chunks::iteration]} {
				error "No recording in progress..."
			}
			after cancel $next_after_id
			stop_recording
		}
		"default" {
			error "Syntax error in command."
		}
	}
}

proc stop_recording {} {
	variable iteration
	unset iteration
	record stop
	puts "Stopped recording..."
}

proc record_next_part {} {
	variable iteration
	variable next_after_id
	variable filenamebase
	variable chunk_time
	variable total_time

	set cmd record_chunks::record_next_part
	set time_to_record $chunk_time
	if {$total_time > 0} {
		set time_left [expr {$total_time - ($chunk_time * $iteration)}]
		if {$time_left <= $chunk_time} {
			set cmd record_chunks::stop_recording
			set time_to_record $time_left
		}
	}

	record stop

	incr iteration
	if {$iteration == 1 && $cmd eq "record_chunks::stop_recording"} {
		# if we're only going to record one movie, no need to number it
		set fname $filenamebase
	} else {
		set fname [format "%s%02d" $filenamebase $iteration]
	}
	record start -doublesize $fname

	set next_after_id [after time $time_to_record $cmd]
	puts "Recording to $fname for [utils::format_time $time_to_record]..."
}

namespace export record_chunks

set_help_text record_chunks_on_framerate_changes \
{Records videos as with the normal record command, but starts recording to a
new video file if the framerate of the VDP changes.
Note that this will only work if you use either use the -prefix option for the
command, or the automatic file name generation. If you specify an exact
filename without the -prefix option, openMSX will record all chunks to the same
file.

Stop recording with the "record_chunks_on_framerate_changes stop" command.

Examples:
    record_chunks_on_framerate_changes start -triplesize -prefix UR
        Records a movie in triplesize in files UR0001.avi, UR0002.avi etc.
        Each subsequent AVI has a different frame rate.
    record_chunks_on_framerate_changes stop
        Stop recording and clean up frame rate detection stuff internally.
}


variable vdp_write_watchpoint ""
variable after_reset_id ""
variable prev_was_ntsc_mode true
variable record_args ""

proc vdp_write_check {} {
	variable prev_was_ntsc_mode

	set safety_check_output [safety_check]
	if {$safety_check_output ne ""} {
		puts $safety_check_output
		return
	}

	set ntsc_mode [expr {([vdpreg 9] & 2) == 0}]
	if {$ntsc_mode != $prev_was_ntsc_mode} {
		restart_recording "from [expr {$prev_was_ntsc_mode ? 60 : 50}] to [expr {$ntsc_mode ? 60 : 50}] Hz"
		set prev_was_ntsc_mode $ntsc_mode
	}
}

proc safety_check {} {
	if {[dict get [record status] status] eq "idle"} {
		# oh, we weren't even recording... let's shut down
		disable_monitoring
		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..."
	}
	return ""
}

proc restart_recording { reason } {
	variable record_args
	puts "Frame rate change detected $reason. Starting recording to next video file."
	record stop
	puts [eval [linsert $record_args 0 record]]
}

proc on_reset {} {
	variable after_reset_id
	set after_reset_id [after boot [namespace code on_reset]]
	set safety_check_output [safety_check]
	if {$safety_check_output ne ""} {
		puts $safety_check_output
		return
	}
	restart_recording "due to a reset"
}

proc disable_monitoring {} {
	variable vdp_write_watchpoint
	variable after_reset_id
	debug remove_watchpoint $vdp_write_watchpoint
	set vdp_write_watchpoint ""
	after cancel $after_reset_id
	set after_reset_id ""
}

proc record_chunks_on_framerate_changes { args } {
	variable record_args
	variable vdp_write_watchpoint
	variable after_reset_id
	variable prev_was_ntsc_mode

	if {$args eq "stop" && $vdp_write_watchpoint ne ""} {
		disable_monitoring
		record stop
		return "Stopped recording chunks on framerate changes."
	}

	set record_args $args
	set response [eval [linsert $record_args 0 record]]
	if {$vdp_write_watchpoint eq "" && [dict get [record status] status] eq "recording"} {
		set prev_was_ntsc_mode [expr {([vdpreg 9] & 2) == 0}]
		set vdp_write_watchpoint [debug set_watchpoint write_io 0x99 1 {
			record_chunks::vdp_write_check
		}]
		set after_reset_id [after boot [namespace code on_reset]]
		return "Started recording chunks on framerate changes...\n$response"
	}
}

namespace export record_chunks_on_framerate_changes

} ;# namespace record_chunks

namespace import record_chunks::*