moshy 1.0.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +22 -22
- data/DEVNOTES.md +7 -0
- data/Gemfile +4 -4
- data/LICENSE.txt +22 -22
- data/README.md +94 -87
- data/Rakefile +2 -2
- data/bin/moshy +106 -104
- data/lib/moshy/bake.rb +68 -68
- data/lib/moshy/inspect.rb +110 -110
- data/lib/moshy/isplit.rb +129 -129
- data/lib/moshy/pdupe.rb +85 -85
- data/lib/moshy/ppulse.rb +153 -153
- data/lib/moshy/prep.rb +78 -78
- data/lib/moshy/version.rb +3 -3
- data/lib/moshy.rb +13 -13
- data/moshy.gemspec +27 -27
- metadata +22 -22
data/lib/moshy/bake.rb
CHANGED
@@ -1,68 +1,68 @@
|
|
1
|
-
module Moshy
|
2
|
-
class Bake
|
3
|
-
def cli(args)
|
4
|
-
opts = Slop::Options.new
|
5
|
-
opts.separator 'Required Parameters:'
|
6
|
-
opts.string '-i', '--input', 'Input file path - can be anything that ffmpeg supports.'
|
7
|
-
opts.string '-o', '--output', 'File output path - should end in .avi.'
|
8
|
-
opts.separator 'Optional Parameters:'
|
9
|
-
opts.integer '-b', '--bitrate', 'Bitrate amount (kb/s). Defaults to 4196. Larger number means higher quality, but larger size.'
|
10
|
-
opts.string '-p', '--pframes', 'Makes sure that there are only P-Frames (no B-Frames). Set this true if you plan to mosh your baked file again. Defaults false.'
|
11
|
-
opts.integer '-n', '--iframe-interval', 'Ideal interval for I-Frames to be distributed. Set this to a high number (600) if you plan to mosh your baked file again.'
|
12
|
-
|
13
|
-
default = {
|
14
|
-
:bitrate => 4196
|
15
|
-
}
|
16
|
-
|
17
|
-
parser = Slop::Parser.new(opts)
|
18
|
-
slop_options = parser.parse(ARGV)
|
19
|
-
@options = default.merge(slop_options) { |key, oldval, newval|
|
20
|
-
if newval.nil?
|
21
|
-
oldval
|
22
|
-
else
|
23
|
-
newval
|
24
|
-
end
|
25
|
-
}
|
26
|
-
|
27
|
-
if @options[:pframes] == "false"
|
28
|
-
@options[:pframes] = false
|
29
|
-
else
|
30
|
-
@options[:pframes] = true
|
31
|
-
end
|
32
|
-
|
33
|
-
# Check mandatory params
|
34
|
-
mandatory = [:input, :output]
|
35
|
-
missing = mandatory.select{ |param| @options[param].nil? }
|
36
|
-
unless missing.empty?
|
37
|
-
puts "Missing options: #{missing.join(', ')}"
|
38
|
-
puts slop_options
|
39
|
-
exit
|
40
|
-
end
|
41
|
-
|
42
|
-
prep @options[:input], @options[:output], @options[:pframes], @options[:'iframe-interval'], @options[:bitrate]
|
43
|
-
end
|
44
|
-
|
45
|
-
def prep(file, output, pframes, iframe_interval, bitrate)
|
46
|
-
ffmpeg = Av::Commands::Ffmpeg.new
|
47
|
-
ffmpeg.add_source file
|
48
|
-
ffmpeg.add_destination output
|
49
|
-
|
50
|
-
# Ensures all frames come out as P-frames, B-frames don't
|
51
|
-
# dupe or mosh properly
|
52
|
-
if pframes
|
53
|
-
ffmpeg.add_output_param ['bf', 0]
|
54
|
-
end
|
55
|
-
|
56
|
-
# Keyframe interval, sets as few I-frames as possible.
|
57
|
-
# ffmpeg will complain about anything over 600 and cap it.
|
58
|
-
if iframe_interval
|
59
|
-
ffmpeg.add_output_param ['g', iframe_interval.to_s]
|
60
|
-
end
|
61
|
-
|
62
|
-
# Bitrate
|
63
|
-
ffmpeg.add_output_param ['b:v', bitrate.to_s + 'k']
|
64
|
-
|
65
|
-
ffmpeg.run
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
1
|
+
module Moshy
|
2
|
+
class Bake
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.separator 'Required Parameters:'
|
6
|
+
opts.string '-i', '--input', 'Input file path - can be anything that ffmpeg supports.'
|
7
|
+
opts.string '-o', '--output', 'File output path - should end in .avi.'
|
8
|
+
opts.separator 'Optional Parameters:'
|
9
|
+
opts.integer '-b', '--bitrate', 'Bitrate amount (kb/s). Defaults to 4196. Larger number means higher quality, but larger size.'
|
10
|
+
opts.string '-p', '--pframes', 'Makes sure that there are only P-Frames (no B-Frames). Set this true if you plan to mosh your baked file again. Defaults false.'
|
11
|
+
opts.integer '-n', '--iframe-interval', 'Ideal interval for I-Frames to be distributed. Set this to a high number (600) if you plan to mosh your baked file again.'
|
12
|
+
|
13
|
+
default = {
|
14
|
+
:bitrate => 4196
|
15
|
+
}
|
16
|
+
|
17
|
+
parser = Slop::Parser.new(opts)
|
18
|
+
slop_options = parser.parse(ARGV)
|
19
|
+
@options = default.merge(slop_options) { |key, oldval, newval|
|
20
|
+
if newval.nil?
|
21
|
+
oldval
|
22
|
+
else
|
23
|
+
newval
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
if @options[:pframes] == "false"
|
28
|
+
@options[:pframes] = false
|
29
|
+
else
|
30
|
+
@options[:pframes] = true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check mandatory params
|
34
|
+
mandatory = [:input, :output]
|
35
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
36
|
+
unless missing.empty?
|
37
|
+
puts "Missing options: #{missing.join(', ')}"
|
38
|
+
puts slop_options
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
prep @options[:input], @options[:output], @options[:pframes], @options[:'iframe-interval'], @options[:bitrate]
|
43
|
+
end
|
44
|
+
|
45
|
+
def prep(file, output, pframes, iframe_interval, bitrate)
|
46
|
+
ffmpeg = Av::Commands::Ffmpeg.new
|
47
|
+
ffmpeg.add_source file
|
48
|
+
ffmpeg.add_destination output
|
49
|
+
|
50
|
+
# Ensures all frames come out as P-frames, B-frames don't
|
51
|
+
# dupe or mosh properly
|
52
|
+
if pframes
|
53
|
+
ffmpeg.add_output_param ['bf', 0]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Keyframe interval, sets as few I-frames as possible.
|
57
|
+
# ffmpeg will complain about anything over 600 and cap it.
|
58
|
+
if iframe_interval
|
59
|
+
ffmpeg.add_output_param ['g', iframe_interval.to_s]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Bitrate
|
63
|
+
ffmpeg.add_output_param ['b:v', bitrate.to_s + 'k']
|
64
|
+
|
65
|
+
ffmpeg.run
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/moshy/inspect.rb
CHANGED
@@ -1,110 +1,110 @@
|
|
1
|
-
module Moshy
|
2
|
-
class Inspect
|
3
|
-
def cli(args)
|
4
|
-
opts = Slop::Options.new
|
5
|
-
opts.banner = "Usage: moshy -m inspect -i file.avi\nmoshy -m inspect --help for details"
|
6
|
-
opts.separator 'Required Parameters:'
|
7
|
-
opts.string '-i', '--input', 'Input file path - must be an .avi.'
|
8
|
-
opts.on '-h', '--help' do
|
9
|
-
puts opts
|
10
|
-
puts "\n"
|
11
|
-
puts \
|
12
|
-
"Reads an .avi file and prints which video frames are keyframes (I-Frames)
|
13
|
-
and which frames are delta frames (P-frames or B-frames). moshy can't
|
14
|
-
tell the difference between a P-frame or a B-frame, so you will want
|
15
|
-
to use avidemux or another program if you need to know.
|
16
|
-
|
17
|
-
This is most useful for identifying where I-Frames exist without having
|
18
|
-
to manually seek through them in a video player/editor. Works well with
|
19
|
-
moshy's \"isplit\" mode because you can use the I-frames from inspect's
|
20
|
-
output to decide what segment of clips you want to get by their I-frames.
|
21
|
-
|
22
|
-
The output reads like this:
|
23
|
-
|
24
|
-
0: keyframe
|
25
|
-
1..359: deltaframe
|
26
|
-
360: keyframe
|
27
|
-
361..441: deltaframe
|
28
|
-
|
29
|
-
Large video files will output a lot of text, so you may want to write the
|
30
|
-
output to an external file like this:
|
31
|
-
|
32
|
-
moshy -m inspect -i video.avi > inspect.txt"
|
33
|
-
exit
|
34
|
-
end
|
35
|
-
|
36
|
-
parser = Slop::Parser.new(opts)
|
37
|
-
@options = parser.parse(ARGV)
|
38
|
-
|
39
|
-
# Check mandatory params
|
40
|
-
mandatory = [:input]
|
41
|
-
missing = mandatory.select{ |param| @options[param].nil? }
|
42
|
-
unless missing.empty?
|
43
|
-
puts "Missing options: #{missing.join(', ')}"
|
44
|
-
puts @options
|
45
|
-
exit
|
46
|
-
end
|
47
|
-
|
48
|
-
puts "Opening file " + @options[:input] + "..."
|
49
|
-
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
50
|
-
puts "Opened!"
|
51
|
-
|
52
|
-
inspect(a)
|
53
|
-
end
|
54
|
-
|
55
|
-
def inspect(clip)
|
56
|
-
keyframe_counter = 0
|
57
|
-
video_frame_counter = 0
|
58
|
-
last_video_frame = 0
|
59
|
-
start_of_frame_segment = 0
|
60
|
-
last_type = nil
|
61
|
-
type = nil
|
62
|
-
# Harvest clip details
|
63
|
-
total_frame_count = clip.frames.count
|
64
|
-
clip.frames.each_with_index do |f, i|
|
65
|
-
if f.is_videoframe?
|
66
|
-
if f.is_keyframe?
|
67
|
-
type = "keyframe"
|
68
|
-
elsif f.is_deltaframe?
|
69
|
-
type = "deltaframe"
|
70
|
-
end
|
71
|
-
|
72
|
-
if video_frame_counter == 0
|
73
|
-
last_type = type
|
74
|
-
end
|
75
|
-
|
76
|
-
if type == last_type
|
77
|
-
last_video_frame = video_frame_counter
|
78
|
-
else
|
79
|
-
# Found a new type segment, print out what we've got
|
80
|
-
if start_of_frame_segment + 1 == last_video_frame
|
81
|
-
segment_string = start_of_frame_segment.to_s + ": " + last_type
|
82
|
-
else
|
83
|
-
segment_string = start_of_frame_segment.to_s + ".." + (last_video_frame - 1).to_s + ": " + last_type
|
84
|
-
end
|
85
|
-
# Let's not add this so we don't confuse the user by making
|
86
|
-
# them think they want to use isplit according to the keyframe count
|
87
|
-
# if last_type == "keyframe"
|
88
|
-
# segment_string += " " + keyframe_counter.to_s
|
89
|
-
# keyframe_counter += 1
|
90
|
-
# end
|
91
|
-
puts segment_string
|
92
|
-
|
93
|
-
# The new last type will be this type during the next frame segment
|
94
|
-
last_type = type
|
95
|
-
# Update start of the frame segment to this frame
|
96
|
-
start_of_frame_segment = video_frame_counter
|
97
|
-
end
|
98
|
-
end
|
99
|
-
video_frame_counter += 1
|
100
|
-
last_video_frame = video_frame_counter
|
101
|
-
end
|
102
|
-
if start_of_frame_segment + 1 == last_video_frame
|
103
|
-
puts start_of_frame_segment.to_s + ": " + last_type
|
104
|
-
else
|
105
|
-
puts start_of_frame_segment.to_s + ".." + (last_video_frame - 1).to_s + ": " + last_type
|
106
|
-
end
|
107
|
-
puts "All done!"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
1
|
+
module Moshy
|
2
|
+
class Inspect
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.banner = "Usage: moshy -m inspect -i file.avi\nmoshy -m inspect --help for details"
|
6
|
+
opts.separator 'Required Parameters:'
|
7
|
+
opts.string '-i', '--input', 'Input file path - must be an .avi.'
|
8
|
+
opts.on '-h', '--help' do
|
9
|
+
puts opts
|
10
|
+
puts "\n"
|
11
|
+
puts \
|
12
|
+
"Reads an .avi file and prints which video frames are keyframes (I-Frames)
|
13
|
+
and which frames are delta frames (P-frames or B-frames). moshy can't
|
14
|
+
tell the difference between a P-frame or a B-frame, so you will want
|
15
|
+
to use avidemux or another program if you need to know.
|
16
|
+
|
17
|
+
This is most useful for identifying where I-Frames exist without having
|
18
|
+
to manually seek through them in a video player/editor. Works well with
|
19
|
+
moshy's \"isplit\" mode because you can use the I-frames from inspect's
|
20
|
+
output to decide what segment of clips you want to get by their I-frames.
|
21
|
+
|
22
|
+
The output reads like this:
|
23
|
+
|
24
|
+
0: keyframe
|
25
|
+
1..359: deltaframe
|
26
|
+
360: keyframe
|
27
|
+
361..441: deltaframe
|
28
|
+
|
29
|
+
Large video files will output a lot of text, so you may want to write the
|
30
|
+
output to an external file like this:
|
31
|
+
|
32
|
+
moshy -m inspect -i video.avi > inspect.txt"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
parser = Slop::Parser.new(opts)
|
37
|
+
@options = parser.parse(ARGV)
|
38
|
+
|
39
|
+
# Check mandatory params
|
40
|
+
mandatory = [:input]
|
41
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
42
|
+
unless missing.empty?
|
43
|
+
puts "Missing options: #{missing.join(', ')}"
|
44
|
+
puts @options
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "Opening file " + @options[:input] + "..."
|
49
|
+
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
50
|
+
puts "Opened!"
|
51
|
+
|
52
|
+
inspect(a)
|
53
|
+
end
|
54
|
+
|
55
|
+
def inspect(clip)
|
56
|
+
keyframe_counter = 0
|
57
|
+
video_frame_counter = 0
|
58
|
+
last_video_frame = 0
|
59
|
+
start_of_frame_segment = 0
|
60
|
+
last_type = nil
|
61
|
+
type = nil
|
62
|
+
# Harvest clip details
|
63
|
+
total_frame_count = clip.frames.count
|
64
|
+
clip.frames.each_with_index do |f, i|
|
65
|
+
if f.is_videoframe?
|
66
|
+
if f.is_keyframe?
|
67
|
+
type = "keyframe"
|
68
|
+
elsif f.is_deltaframe?
|
69
|
+
type = "deltaframe"
|
70
|
+
end
|
71
|
+
|
72
|
+
if video_frame_counter == 0
|
73
|
+
last_type = type
|
74
|
+
end
|
75
|
+
|
76
|
+
if type == last_type
|
77
|
+
last_video_frame = video_frame_counter
|
78
|
+
else
|
79
|
+
# Found a new type segment, print out what we've got
|
80
|
+
if start_of_frame_segment + 1 == last_video_frame
|
81
|
+
segment_string = start_of_frame_segment.to_s + ": " + last_type
|
82
|
+
else
|
83
|
+
segment_string = start_of_frame_segment.to_s + ".." + (last_video_frame - 1).to_s + ": " + last_type
|
84
|
+
end
|
85
|
+
# Let's not add this so we don't confuse the user by making
|
86
|
+
# them think they want to use isplit according to the keyframe count
|
87
|
+
# if last_type == "keyframe"
|
88
|
+
# segment_string += " " + keyframe_counter.to_s
|
89
|
+
# keyframe_counter += 1
|
90
|
+
# end
|
91
|
+
puts segment_string
|
92
|
+
|
93
|
+
# The new last type will be this type during the next frame segment
|
94
|
+
last_type = type
|
95
|
+
# Update start of the frame segment to this frame
|
96
|
+
start_of_frame_segment = video_frame_counter
|
97
|
+
end
|
98
|
+
end
|
99
|
+
video_frame_counter += 1
|
100
|
+
last_video_frame = video_frame_counter
|
101
|
+
end
|
102
|
+
if start_of_frame_segment + 1 == last_video_frame
|
103
|
+
puts start_of_frame_segment.to_s + ": " + last_type
|
104
|
+
else
|
105
|
+
puts start_of_frame_segment.to_s + ".." + (last_video_frame - 1).to_s + ": " + last_type
|
106
|
+
end
|
107
|
+
puts "All done!"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/moshy/isplit.rb
CHANGED
@@ -1,129 +1,129 @@
|
|
1
|
-
module Moshy
|
2
|
-
class ISplit
|
3
|
-
def cli(args)
|
4
|
-
opts = Slop::Options.new
|
5
|
-
opts.banner = "Usage: moshy -m isplit -i file.avi -o file_out\nmoshy -m isplit --help for details"
|
6
|
-
opts.separator 'Required Parameters:'
|
7
|
-
opts.string '-i', '--input', 'Input file path - must be an .avi.'
|
8
|
-
opts.string '-o', '--output', 'Output file path - will be appended with -#.avi for each clip.'
|
9
|
-
opts.separator 'Optional Parameters:'
|
10
|
-
opts.integer '-b', '--begin', 'Index of the I-frame at which to begin clipping (inclusive)'
|
11
|
-
opts.integer '-e', '--end', 'Index of the I-frame at which to stop clipping (inclusive)'
|
12
|
-
opts.integer '-v', '--verbose', 'Noisy output (default: false)'
|
13
|
-
opts.on '-h', '--help' do
|
14
|
-
puts opts
|
15
|
-
puts "\n"
|
16
|
-
puts \
|
17
|
-
"Extracts individual clips from an AVI where each clip is separated
|
18
|
-
by I-frames in the original AVI. Great for getting specific clips out
|
19
|
-
of a larger video and later doing I-frame moshing.
|
20
|
-
|
21
|
-
Note that since this creates multiple clips, you should NOT specify
|
22
|
-
the .avi extension in your output (-o) parameter, as moshy will
|
23
|
-
automatically append \"-#.avi\" to the output parameter you pass
|
24
|
-
when it spits out individual clips.
|
25
|
-
|
26
|
-
If you want to only cut clips from a certain section of a larger
|
27
|
-
video file, you can set the in- and out-points of where to get clips
|
28
|
-
from by using the -b (--begin) and -e (--end) options, where the
|
29
|
-
values used in those parameters are the indexes of the I-Frames.
|
30
|
-
For example, if a video file has 12 I-Frames and you want the clips
|
31
|
-
between I-Frames 3 and 7, you would use the following command:
|
32
|
-
|
33
|
-
moshy -m isplit -i file.avi -o file_out -b 3 -e 7"
|
34
|
-
exit
|
35
|
-
end
|
36
|
-
|
37
|
-
parser = Slop::Parser.new(opts)
|
38
|
-
@options = parser.parse(ARGV)
|
39
|
-
# puts @options.to_hash
|
40
|
-
|
41
|
-
# Check mandatory params
|
42
|
-
mandatory = [:input, :output]
|
43
|
-
missing = mandatory.select{ |param| @options[param].nil? }
|
44
|
-
unless missing.empty?
|
45
|
-
puts "Missing options: #{missing.join(', ')}"
|
46
|
-
puts @options
|
47
|
-
exit
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
puts "Opening file " + @options[:input] + "..."
|
52
|
-
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
53
|
-
puts "Opened!"
|
54
|
-
|
55
|
-
split(a, @options[:output], @options[:begin], @options[:end], @options[:verbose])
|
56
|
-
end
|
57
|
-
|
58
|
-
def clip(frames, out_path, start_index, frame_count)
|
59
|
-
puts "Clipping " + frame_count.to_s + " frames starting at frame " + start_index.to_s
|
60
|
-
clip = frames.slice(start_index, frame_count)
|
61
|
-
o = AviGlitch.open clip
|
62
|
-
puts "Outputting " + out_path
|
63
|
-
o.output out_path
|
64
|
-
end
|
65
|
-
|
66
|
-
def split(clip, output, begin_point, end_point, verbose)
|
67
|
-
clip_cuts = {}
|
68
|
-
|
69
|
-
clip_count = 0
|
70
|
-
current_iframe = 0
|
71
|
-
iframe_index = 0
|
72
|
-
last_iframe_index = 0
|
73
|
-
frames_in_clip = 0
|
74
|
-
|
75
|
-
# Harvest clip details
|
76
|
-
total_frame_count = clip.frames.count
|
77
|
-
clip.frames.each_with_index do |f, i|
|
78
|
-
if f.is_keyframe?
|
79
|
-
iframe_index = i
|
80
|
-
# Don't process frames that are before our beginning
|
81
|
-
if current_iframe and begin_point and current_iframe < begin_point
|
82
|
-
# puts "skipping " + current_iframe.to_s
|
83
|
-
frames_in_clip = 0
|
84
|
-
current_iframe = current_iframe + 1
|
85
|
-
last_iframe_index = iframe_index
|
86
|
-
# puts "last_iframe_index: " + last_iframe_index.to_s
|
87
|
-
next
|
88
|
-
end
|
89
|
-
break if end_point and current_iframe > end_point
|
90
|
-
|
91
|
-
if current_iframe != 0
|
92
|
-
if verbose
|
93
|
-
puts "Storing clip details: iframe_number=" + current_iframe.to_s + "; index=" + last_iframe_index.to_s + "; frame_count=" + frames_in_clip.to_s
|
94
|
-
end
|
95
|
-
clip_cuts[current_iframe] = {
|
96
|
-
:index => last_iframe_index,
|
97
|
-
:frame_count => frames_in_clip
|
98
|
-
}
|
99
|
-
end
|
100
|
-
frames_in_clip = 0
|
101
|
-
current_iframe = current_iframe + 1
|
102
|
-
last_iframe_index = iframe_index
|
103
|
-
else
|
104
|
-
frames_in_clip = frames_in_clip + 1
|
105
|
-
# clip last piece manually if we're at the end, because there's
|
106
|
-
# no last iframe to detect and trigger the final clip
|
107
|
-
if i == total_frame_count - 1
|
108
|
-
if verbose
|
109
|
-
puts "Storing clip details: iframe_number=" + current_iframe.to_s + "; index=" + last_iframe_index.to_s + "; frame_count=" + frames_in_clip.to_s
|
110
|
-
end
|
111
|
-
clip_cuts[current_iframe] = {
|
112
|
-
:index => last_iframe_index,
|
113
|
-
:frame_count => frames_in_clip
|
114
|
-
}
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
puts clip_cuts
|
120
|
-
|
121
|
-
clip_cuts.keys.each do |f|
|
122
|
-
out_path = output + '-' + f.to_s + '.avi'
|
123
|
-
clip(clip.frames, out_path, clip_cuts[f][:index], clip_cuts[f][:frame_count])
|
124
|
-
end
|
125
|
-
|
126
|
-
puts "All done!"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
1
|
+
module Moshy
|
2
|
+
class ISplit
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.banner = "Usage: moshy -m isplit -i file.avi -o file_out\nmoshy -m isplit --help for details"
|
6
|
+
opts.separator 'Required Parameters:'
|
7
|
+
opts.string '-i', '--input', 'Input file path - must be an .avi.'
|
8
|
+
opts.string '-o', '--output', 'Output file path - will be appended with -#.avi for each clip.'
|
9
|
+
opts.separator 'Optional Parameters:'
|
10
|
+
opts.integer '-b', '--begin', 'Index of the I-frame at which to begin clipping (inclusive)'
|
11
|
+
opts.integer '-e', '--end', 'Index of the I-frame at which to stop clipping (inclusive)'
|
12
|
+
opts.integer '-v', '--verbose', 'Noisy output (default: false)'
|
13
|
+
opts.on '-h', '--help' do
|
14
|
+
puts opts
|
15
|
+
puts "\n"
|
16
|
+
puts \
|
17
|
+
"Extracts individual clips from an AVI where each clip is separated
|
18
|
+
by I-frames in the original AVI. Great for getting specific clips out
|
19
|
+
of a larger video and later doing I-frame moshing.
|
20
|
+
|
21
|
+
Note that since this creates multiple clips, you should NOT specify
|
22
|
+
the .avi extension in your output (-o) parameter, as moshy will
|
23
|
+
automatically append \"-#.avi\" to the output parameter you pass
|
24
|
+
when it spits out individual clips.
|
25
|
+
|
26
|
+
If you want to only cut clips from a certain section of a larger
|
27
|
+
video file, you can set the in- and out-points of where to get clips
|
28
|
+
from by using the -b (--begin) and -e (--end) options, where the
|
29
|
+
values used in those parameters are the indexes of the I-Frames.
|
30
|
+
For example, if a video file has 12 I-Frames and you want the clips
|
31
|
+
between I-Frames 3 and 7, you would use the following command:
|
32
|
+
|
33
|
+
moshy -m isplit -i file.avi -o file_out -b 3 -e 7"
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
parser = Slop::Parser.new(opts)
|
38
|
+
@options = parser.parse(ARGV)
|
39
|
+
# puts @options.to_hash
|
40
|
+
|
41
|
+
# Check mandatory params
|
42
|
+
mandatory = [:input, :output]
|
43
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
44
|
+
unless missing.empty?
|
45
|
+
puts "Missing options: #{missing.join(', ')}"
|
46
|
+
puts @options
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
puts "Opening file " + @options[:input] + "..."
|
52
|
+
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
53
|
+
puts "Opened!"
|
54
|
+
|
55
|
+
split(a, @options[:output], @options[:begin], @options[:end], @options[:verbose])
|
56
|
+
end
|
57
|
+
|
58
|
+
def clip(frames, out_path, start_index, frame_count)
|
59
|
+
puts "Clipping " + frame_count.to_s + " frames starting at frame " + start_index.to_s
|
60
|
+
clip = frames.slice(start_index, frame_count)
|
61
|
+
o = AviGlitch.open clip
|
62
|
+
puts "Outputting " + out_path
|
63
|
+
o.output out_path
|
64
|
+
end
|
65
|
+
|
66
|
+
def split(clip, output, begin_point, end_point, verbose)
|
67
|
+
clip_cuts = {}
|
68
|
+
|
69
|
+
clip_count = 0
|
70
|
+
current_iframe = 0
|
71
|
+
iframe_index = 0
|
72
|
+
last_iframe_index = 0
|
73
|
+
frames_in_clip = 0
|
74
|
+
|
75
|
+
# Harvest clip details
|
76
|
+
total_frame_count = clip.frames.count
|
77
|
+
clip.frames.each_with_index do |f, i|
|
78
|
+
if f.is_keyframe?
|
79
|
+
iframe_index = i
|
80
|
+
# Don't process frames that are before our beginning
|
81
|
+
if current_iframe and begin_point and current_iframe < begin_point
|
82
|
+
# puts "skipping " + current_iframe.to_s
|
83
|
+
frames_in_clip = 0
|
84
|
+
current_iframe = current_iframe + 1
|
85
|
+
last_iframe_index = iframe_index
|
86
|
+
# puts "last_iframe_index: " + last_iframe_index.to_s
|
87
|
+
next
|
88
|
+
end
|
89
|
+
break if end_point and current_iframe > end_point
|
90
|
+
|
91
|
+
if current_iframe != 0
|
92
|
+
if verbose
|
93
|
+
puts "Storing clip details: iframe_number=" + current_iframe.to_s + "; index=" + last_iframe_index.to_s + "; frame_count=" + frames_in_clip.to_s
|
94
|
+
end
|
95
|
+
clip_cuts[current_iframe] = {
|
96
|
+
:index => last_iframe_index,
|
97
|
+
:frame_count => frames_in_clip
|
98
|
+
}
|
99
|
+
end
|
100
|
+
frames_in_clip = 0
|
101
|
+
current_iframe = current_iframe + 1
|
102
|
+
last_iframe_index = iframe_index
|
103
|
+
else
|
104
|
+
frames_in_clip = frames_in_clip + 1
|
105
|
+
# clip last piece manually if we're at the end, because there's
|
106
|
+
# no last iframe to detect and trigger the final clip
|
107
|
+
if i == total_frame_count - 1
|
108
|
+
if verbose
|
109
|
+
puts "Storing clip details: iframe_number=" + current_iframe.to_s + "; index=" + last_iframe_index.to_s + "; frame_count=" + frames_in_clip.to_s
|
110
|
+
end
|
111
|
+
clip_cuts[current_iframe] = {
|
112
|
+
:index => last_iframe_index,
|
113
|
+
:frame_count => frames_in_clip
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
puts clip_cuts
|
120
|
+
|
121
|
+
clip_cuts.keys.each do |f|
|
122
|
+
out_path = output + '-' + f.to_s + '.avi'
|
123
|
+
clip(clip.frames, out_path, clip_cuts[f][:index], clip_cuts[f][:frame_count])
|
124
|
+
end
|
125
|
+
|
126
|
+
puts "All done!"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|