moshy 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|