| 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 |
|
| 48 |
set chunk_time 899 ; |
| 49 |
set total_time -1 ; |
| 50 |
set num_chunks -1 ; |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
} ; |
| 253 |
|
| 254 |
namespace import record_chunks::* |