moshy 0.1.0 → 1.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 +4 -4
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +2 -0
- data/bin/moshy +105 -0
- data/lib/moshy.rb +13 -0
- data/lib/moshy/bake.rb +68 -0
- data/lib/moshy/inspect.rb +110 -0
- data/lib/moshy/isplit.rb +129 -0
- data/lib/moshy/pdupe.rb +85 -0
- data/lib/moshy/ppulse.rb +153 -0
- data/lib/moshy/prep.rb +78 -0
- data/lib/moshy/version.rb +3 -0
- data/moshy.gemspec +27 -0
- metadata +94 -21
- data/moshy.rb +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0c17c458da7a8ea731db28eaf6a919469107b9d
|
4
|
+
data.tar.gz: fa5b4eea8ca1538790eaf989e77e40b058fcf01c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2692be61ccc6e9088d6d3ab9ad75af55b2c7ce0d9fa8d67dc20562b6ec2f7731f36424ce430a7cb6ccf17bea958cb19fcf116e2267981853e81043b812f81fa
|
7
|
+
data.tar.gz: aa247fc3c2115fb4a5ebaaf6b69ee25c33735311ef7a0a10a8ab26993a4d62322b5b546f3c2ee3440fc639be7e7c935c59a148aab2c5f7c87dd485cab69f9ab2
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Way Spurr-Chen <wayspurrchen@gmail.com> <http://wayspurrchen.com/>
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# moshy
|
2
|
+
|
3
|
+
moshy is a command line datamoshing utility kit for AVI files, based heavily on [aviglitch](https://github.com/ucnv/aviglitch).
|
4
|
+
It's designed to make common datamoshing tasks easier without having to open avidemux or other GUI tools. It lets you do stuff like:
|
5
|
+
|
6
|
+
- Convert video files into AVI video files with minimal I-Frames and no B-frames for ultimate moshability
|
7
|
+
- Create P-Frame duplication effects quickly
|
8
|
+
- Split a long video file into multiple clips based on its I-Frames
|
9
|
+
- "Bake" your datamoshed video, encoding the corruption as actual video content for uploading to video services or moshing even further!
|
10
|
+
- Identifying keyframe and deltaframe indexes in any AVI file
|
11
|
+
- ...and more!
|
12
|
+
|
13
|
+
If you don't know how to use the command line, this is a great opportunity to learn:
|
14
|
+
|
15
|
+
- Mac OS X Tutorial: http://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line
|
16
|
+
- Windows Tutorial: http://lifehacker.com/5633909/who-needs-a-mouse-learn-to-use-the-command-line-for-almost-anything
|
17
|
+
|
18
|
+
## Get it!
|
19
|
+
|
20
|
+
You'll need to install [Ruby](https://www.ruby-lang.org/en/). If you're on Mac OS X, you probably already have a local version of Ruby installed. Once that's done, you will need Bundler, a Ruby dependency manager, which you can install with the following command:
|
21
|
+
|
22
|
+
```
|
23
|
+
gem install bundler
|
24
|
+
```
|
25
|
+
|
26
|
+
Then you can use Bundler to install moshy:
|
27
|
+
|
28
|
+
```
|
29
|
+
bundle install moshy
|
30
|
+
```
|
31
|
+
|
32
|
+
From there, you can use `moshy` from the command line.
|
33
|
+
|
34
|
+
## What's it do?
|
35
|
+
|
36
|
+
Moshy currently has six different modes:
|
37
|
+
|
38
|
+
- `prep` - Preps a video file for datamoshing with moshy by converting it
|
39
|
+
into an AVI with no B-Frames (they're not good for moshing), and placing as
|
40
|
+
few I-Frames as possible. Requires ffmpeg be installed locally.
|
41
|
+
- `isplit` - Extracts individual clips from an AVI where each clip is
|
42
|
+
separated by I-frames in the original AVI. Great for getting specific
|
43
|
+
clips out of a larger video and later doing I-frame moshing.
|
44
|
+
- `inspect` - Reads an .avi file and prints which video frames are keyframes
|
45
|
+
(I-Frames) and which frames are delta frames (P-frames or B-frames). moshy
|
46
|
+
cannot tell the difference between a P-frame or a B-frame, so you will want
|
47
|
+
to use avidemux or another program if you need to know.
|
48
|
+
- `pdupe` - Duplicates a P-frame at a given frame a certain amount. To find
|
49
|
+
out which frames are P-frames, use software like avidemux to look at the
|
50
|
+
frame type. WARNING: This mode is a little glitchy. You may need to set
|
51
|
+
the interval 1 or 2 above or below the frame number you actually want to
|
52
|
+
duplicate. I'm not sure why this happens, but try it with a small
|
53
|
+
duplication amount first. NOTE: This can mode take a while to process
|
54
|
+
over 60-90 frame dupes.
|
55
|
+
- `ppulse` - Takes c number of frames and every n frames and duplicates them a
|
56
|
+
given amount, resulting in a consistent P-duplication datamosh that's
|
57
|
+
good for creating rhythmic effects. This was originally created to
|
58
|
+
create mosh effects in sync with a beat for a music video.
|
59
|
+
- `bake` - "Bakes" your datamosh by creating a new video file from your
|
60
|
+
datamoshed .avi, causing the datamosh effects to be treated as the actual
|
61
|
+
content of the new video instead of an error. Requires ffmpeg to be
|
62
|
+
installed locally.
|
63
|
+
|
64
|
+
You can access detailed info on how to use each of them from the command line with
|
65
|
+
the command `moshy -m <mode> --help`.
|
66
|
+
|
67
|
+
## Cool!
|
68
|
+
|
69
|
+
If you think this is cool, you'll probably find my list of [glitch art resources](http://www.glitchet.com/resources)
|
70
|
+
useful as well as the [Glitchet newsletter](http://www.glitchet.com/), a free weekly futuristic
|
71
|
+
news and glitch aesthetic e-zine.
|
data/Rakefile
ADDED
data/bin/moshy
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'slop'
|
5
|
+
require 'moshy'
|
6
|
+
|
7
|
+
module Moshy
|
8
|
+
def self.top_level_help
|
9
|
+
$options = {
|
10
|
+
|
11
|
+
}
|
12
|
+
opts = OptionParser.new do |opts|
|
13
|
+
opts.banner = "
|
14
|
+
moshy, a Ruby utility for making it easier to datamosh AVI files. It has
|
15
|
+
multiple modes that can be run with the -m or --mode option.
|
16
|
+
|
17
|
+
MODES DETAILS
|
18
|
+
-------------
|
19
|
+
|
20
|
+
\"prep\"
|
21
|
+
------
|
22
|
+
Preps a video file for datamoshing with moshy by converting it
|
23
|
+
into an AVI with no B-Frames (they're not good for moshing), and placing as
|
24
|
+
few I-Frames as possible. Requires ffmpeg be installed locally.
|
25
|
+
|
26
|
+
\"isplit\"
|
27
|
+
--------
|
28
|
+
Extracts individual clips from an AVI where each clip is
|
29
|
+
separated by I-frames in the original AVI. Great for getting specific
|
30
|
+
clips out of a larger video and later doing I-frame moshing.
|
31
|
+
|
32
|
+
\"pdupe\"
|
33
|
+
-------
|
34
|
+
Duplicates a P-frame at a given frame a certain amount. To find
|
35
|
+
out which frames are P-frames, use software like avidemux to look at the
|
36
|
+
frame type. WARNING: This mode is a little glitchy. You may need to set
|
37
|
+
the interval 1 or 2 above or below the frame number you actually want to
|
38
|
+
duplicate. I'm not sure why this happens, but try it with a small
|
39
|
+
duplication amount first. NOTE: This can mode take a while to process
|
40
|
+
over 60-90 frame dupes.
|
41
|
+
|
42
|
+
\"ppulse\"
|
43
|
+
--------
|
44
|
+
Takes c number of frames and every n frames and duplicates them a
|
45
|
+
given amount, resulting in a consistent P-duplication datamosh that's
|
46
|
+
good for creating rhythmic effects. This was originally created to
|
47
|
+
create mosh effects in sync with a beat for a music video.
|
48
|
+
|
49
|
+
\"bake\"
|
50
|
+
------
|
51
|
+
\"Bakes\" your datamosh by creating a new video file from your
|
52
|
+
datamoshed .avi, causing the datamosh effects to be treated as the actual
|
53
|
+
content of the new video instead of an error. Requires ffmpeg to be
|
54
|
+
installed locally.
|
55
|
+
|
56
|
+
\"inspect\"
|
57
|
+
---------
|
58
|
+
Reads an .avi file and prints which video frames are keyframes
|
59
|
+
(I-Frames) and which frames are delta frames (P-frames or B-frames). moshy
|
60
|
+
cannot tell the difference between a P-frame or a B-frame, so you will want
|
61
|
+
to use avidemux or another program if you need to know.
|
62
|
+
|
63
|
+
Run moshy with mode -m <mode> --help to see options for individual modes.
|
64
|
+
"
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
opts.parse
|
69
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument
|
70
|
+
end
|
71
|
+
|
72
|
+
puts opts
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Because we have multiple modes, we do some initial basic arg checking
|
77
|
+
# to see if they specified a mode. If not, we show the top-level help menu.
|
78
|
+
result = Slop.parse suppress_errors: true do |o|
|
79
|
+
o.string '-m', '--mode'
|
80
|
+
end
|
81
|
+
|
82
|
+
mode_classes = {
|
83
|
+
"inspect" => Moshy::Inspect,
|
84
|
+
"isplit" => Moshy::ISplit,
|
85
|
+
"pdupe" => Moshy::PDupe,
|
86
|
+
"ppulse" => Moshy::PPulse,
|
87
|
+
"prep" => Moshy::Prep,
|
88
|
+
"bake" => Moshy::Bake
|
89
|
+
}
|
90
|
+
|
91
|
+
if mode_classes.has_key? result[:m]
|
92
|
+
# We need to strip out the "m" otherwise our other arg parsers
|
93
|
+
# will choke on the extra parameter
|
94
|
+
ARGV.each_with_index do |o, i|
|
95
|
+
if o == "-m" || o == "--m"
|
96
|
+
ARGV.delete_at(i + 1)
|
97
|
+
ARGV.delete_at(i)
|
98
|
+
break
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
mode_classes[result[:m]].new.cli ARGV
|
103
|
+
else
|
104
|
+
Moshy.top_level_help
|
105
|
+
end
|
data/lib/moshy.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "aviglitch"
|
2
|
+
require "av"
|
3
|
+
require_relative "moshy/version"
|
4
|
+
require_relative "moshy/inspect"
|
5
|
+
require_relative "moshy/isplit"
|
6
|
+
require_relative "moshy/pdupe"
|
7
|
+
require_relative "moshy/bake"
|
8
|
+
require_relative "moshy/prep"
|
9
|
+
require_relative "moshy/ppulse"
|
10
|
+
|
11
|
+
module Moshy
|
12
|
+
# Empty skeleton - just used to include the others
|
13
|
+
end
|
data/lib/moshy/bake.rb
ADDED
@@ -0,0 +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
|
@@ -0,0 +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
|
data/lib/moshy/isplit.rb
ADDED
@@ -0,0 +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
|
data/lib/moshy/pdupe.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module Moshy
|
2
|
+
class PDupe
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.banner = "Usage: moshy -m pdupe -i file.avi -o file_out.avi -f <integer>\nmoshy -m pdupe --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, should be an .avi.'
|
9
|
+
opts.integer '-f', '--frame', 'Index of the frame that should be duplicated'
|
10
|
+
opts.separator 'Optional Parameters:'
|
11
|
+
opts.integer '-d', '--dupes', 'Number of times to multiply the frame (default: 30)'
|
12
|
+
opts.on '-h', '--help' do
|
13
|
+
puts opts
|
14
|
+
puts "\n"
|
15
|
+
puts \
|
16
|
+
"Duplicates a P-frame at a given frame a certain amount. To find
|
17
|
+
out which frames are P-frames, use software like avidemux to look at the
|
18
|
+
frame type. WARNING: This mode is a little glitchy. You may need to set
|
19
|
+
the interval 1 or 2 above or below the frame number you actually want to
|
20
|
+
duplicate. I'm not sure why this happens, but try it with a small
|
21
|
+
duplication amount first. NOTE: This can mode take a while to process
|
22
|
+
over 60-90 frame dupes.
|
23
|
+
|
24
|
+
You can specify the number of duplicates that you want with the -d parameter."
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
default = {
|
29
|
+
:dupes => 30
|
30
|
+
}
|
31
|
+
|
32
|
+
parser = Slop::Parser.new(opts)
|
33
|
+
slop_options = parser.parse(ARGV)
|
34
|
+
@options = default.merge(slop_options) { |key, oldval, newval|
|
35
|
+
if newval.nil?
|
36
|
+
oldval
|
37
|
+
else
|
38
|
+
newval
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
42
|
+
# Check mandatory params
|
43
|
+
mandatory = [:input, :output, :frame]
|
44
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
45
|
+
unless missing.empty?
|
46
|
+
puts "Missing options: #{missing.join(', ')}"
|
47
|
+
puts slop_options
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Opening file " + @options[:input] + "..."
|
52
|
+
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
53
|
+
puts "Opened!"
|
54
|
+
|
55
|
+
pdupe(a, @options[:output], @options[:frame], @options[:dupes])
|
56
|
+
end
|
57
|
+
|
58
|
+
def pdupe(clip, output, frame, duplicate_amount)
|
59
|
+
|
60
|
+
puts "Size: " + clip.frames.size_of('videoframe').to_s
|
61
|
+
|
62
|
+
frames = nil
|
63
|
+
video_frame_counter = 0
|
64
|
+
|
65
|
+
clip.frames.each_with_index do |f, i|
|
66
|
+
if f.is_videoframe?
|
67
|
+
video_frame_counter += 1
|
68
|
+
if video_frame_counter == frame
|
69
|
+
puts "On frame " + frame.to_s + ", duping " + duplicate_amount.to_s + " times"
|
70
|
+
clipped = clip.frames[0..(i + 5)]
|
71
|
+
dupe_clip = clip.frames[(i + 4), 1] * duplicate_amount
|
72
|
+
frames = clipped + dupe_clip
|
73
|
+
puts "Added dupe, grabbing rest..."
|
74
|
+
frames = frames + clip.frames[i..-1]
|
75
|
+
puts "Done. Output frame count: " + frames.size.to_s
|
76
|
+
break
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
o = AviGlitch.open frames
|
82
|
+
o.output output
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/moshy/ppulse.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
module Moshy
|
2
|
+
class PPulse
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.banner = "Usage: moshy -m ppulse -i file.avi -o file_out.avi [options]\nmoshy -m inspect --help for details"
|
6
|
+
opts.separator 'Required Parameters:'
|
7
|
+
opts.string '-i', '--input', 'Input file path - must be an .avi. Clip to split in split mode, first clip in stitch mode'
|
8
|
+
opts.string '-o', '--output', 'Output file path - will be appended with -#.avi for each frame in split mode'
|
9
|
+
opts.separator 'Optional Parameters:'
|
10
|
+
opts.string '-k', '--keep', 'Whether or not to keep standard frames. (default: true)', default: "true"
|
11
|
+
opts.integer '-c', '--count', 'How many frames to grab forward from each interval. (default: 1)', default: 1
|
12
|
+
opts.integer '-d', '--dupes', 'Number of times to multiply the frame (default: 30)', default: 30
|
13
|
+
opts.integer '-n', '--interval', 'Which nth frames should be duplicated (default: 30)', default: 30
|
14
|
+
opts.on '-h', '--help' do
|
15
|
+
puts opts
|
16
|
+
puts "\n"
|
17
|
+
puts \
|
18
|
+
"Takes c number of frames and every n frames and duplicates them a
|
19
|
+
given amount, resulting in a consistent P-duplication datamosh that's
|
20
|
+
good for creating rhythmic effects. This was originally created to
|
21
|
+
create mosh effects in sync with a beat for a music video.
|
22
|
+
|
23
|
+
You can specify what interval to get frames at with -n (--interval).
|
24
|
+
You can specify how many frames to get from the current interval with
|
25
|
+
-c (--count). You can specify how many times to duplicate a given
|
26
|
+
frame with -d (--dupes). You can then specify whether or not to keep
|
27
|
+
the original video's frames between the end of the duplication and
|
28
|
+
where the next interval occurs with -k (--keep). Keeping original
|
29
|
+
frames causes the original motion to continue after the P-frame dupe
|
30
|
+
effect, whereas dropping original frames causes the video to snap
|
31
|
+
into the motion of the frames at each interval. This is a more complex
|
32
|
+
effect so I recommend experimenting with it!"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
default = {
|
37
|
+
:dupes => 30,
|
38
|
+
:interval => 30,
|
39
|
+
:count => 1,
|
40
|
+
:keep => true
|
41
|
+
}
|
42
|
+
|
43
|
+
parser = Slop::Parser.new(opts)
|
44
|
+
slop_options = parser.parse(ARGV)
|
45
|
+
@options = default.merge(slop_options) { |key, oldval, newval|
|
46
|
+
if newval.nil?
|
47
|
+
oldval
|
48
|
+
else
|
49
|
+
newval
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
if @options[:keep] == "false"
|
54
|
+
@options[:keep] = false
|
55
|
+
else
|
56
|
+
@options[:keep] = true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check mandatory params
|
60
|
+
mandatory = [:input, :output]
|
61
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
62
|
+
unless missing.empty?
|
63
|
+
puts "Missing options: #{missing.join(', ')}"
|
64
|
+
puts slop_options
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
|
68
|
+
puts "Opening file " + @options[:input] + "..."
|
69
|
+
a = AviGlitch.open @options[:input] # Rewrite this line for your file.
|
70
|
+
puts "Opened!"
|
71
|
+
|
72
|
+
ppulse(a, @options[:output], @options[:interval], @options[:count], @options[:dupes], @options[:keep])
|
73
|
+
end
|
74
|
+
|
75
|
+
# Loops through a video file and grabs every `interval` frames then duplicates them
|
76
|
+
# `duplicate_amount` times
|
77
|
+
#
|
78
|
+
# `leave_originals` will copy standard frames and only p-frame the last one every `interval`
|
79
|
+
def ppulse(clip, output, interval = 30, count = 1, duplicate_amount = 30, leave_originals = true)
|
80
|
+
|
81
|
+
puts "Size: " + clip.frames.size_of('videoframe').to_s
|
82
|
+
|
83
|
+
frames = nil
|
84
|
+
|
85
|
+
video_frame_counter = 0
|
86
|
+
|
87
|
+
have_iframe = false
|
88
|
+
|
89
|
+
if leave_originals
|
90
|
+
first_index = 0
|
91
|
+
second_index = 0
|
92
|
+
clip.frames.each_with_index do |f, i|
|
93
|
+
if f.is_videoframe?
|
94
|
+
video_frame_counter += 1
|
95
|
+
if video_frame_counter % interval == 0
|
96
|
+
second_index = i
|
97
|
+
puts "first index: " + first_index.to_s
|
98
|
+
puts "second index: " + second_index.to_s
|
99
|
+
|
100
|
+
clipped = clip.frames[first_index..(i + 5)]
|
101
|
+
dupe_clip = clip.frames[i, count] * duplicate_amount
|
102
|
+
if frames.nil?
|
103
|
+
frames = clipped + dupe_clip
|
104
|
+
else
|
105
|
+
frames = frames + clipped + dupe_clip
|
106
|
+
end
|
107
|
+
puts "Current expected output frame count: " + frames.size.to_s
|
108
|
+
|
109
|
+
first_index = i + 5
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
# Harvest clip details
|
115
|
+
clip.frames.each_with_index do |f, i|
|
116
|
+
if f.is_videoframe?
|
117
|
+
if !have_iframe && f.is_keyframe?
|
118
|
+
puts "Added first iframe (necessary to avoid total corruption)"
|
119
|
+
# no idea why i need to get 5
|
120
|
+
frames = clip.frames[i, 1]
|
121
|
+
have_iframe = true
|
122
|
+
end
|
123
|
+
|
124
|
+
# +1 to offset the first iframe
|
125
|
+
if video_frame_counter % interval == 0 && f.is_deltaframe?
|
126
|
+
puts "Processing frame " + video_frame_counter.to_s + " at index " + i.to_s
|
127
|
+
# You might ask why we need to check if frames are nil when we already check
|
128
|
+
# whether or not we have an i frame and if the above is a keyframe - that's
|
129
|
+
# because datamoshers are crazy and might pass use clip with no leading iframe :)
|
130
|
+
if frames.nil?
|
131
|
+
puts "First frame, setting"
|
132
|
+
clipped = clip.frames[i, count]
|
133
|
+
frames = frames.concat( clipped * duplicate_amount )
|
134
|
+
puts "Frame size"
|
135
|
+
puts frames.size_of('videoframe')
|
136
|
+
else
|
137
|
+
puts "Current i: " + i.to_s
|
138
|
+
puts "Duping frame " + i.to_s + " " + duplicate_amount.to_s + " times"
|
139
|
+
frames = frames.concat( clip.frames[i, count] * duplicate_amount )
|
140
|
+
puts "Next frame, size: " + frames.size.to_s
|
141
|
+
end
|
142
|
+
end
|
143
|
+
video_frame_counter += 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
o = AviGlitch.open frames
|
149
|
+
o.output output
|
150
|
+
puts "Done! File processed to: " + output
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/moshy/prep.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Moshy
|
2
|
+
class Prep
|
3
|
+
def cli(args)
|
4
|
+
opts = Slop::Options.new
|
5
|
+
opts.banner = "Usage: moshy.rb -m prep -i <file> -o <output> [options]\n"
|
6
|
+
opts.separator 'Required Parameters:'
|
7
|
+
opts.string '-i', '--input', 'Input file path - can be anything that ffmpeg supports.'
|
8
|
+
opts.string '-o', '--output', 'File output path - should end in .avi.'
|
9
|
+
opts.separator 'Optional Parameters:'
|
10
|
+
opts.integer '-b', '--bitrate', 'Bitrate amount (kb/s). Defaults to 4196. Larger number means higher quality, but larger size.'
|
11
|
+
opts.on '-h', '--help' do
|
12
|
+
puts opts
|
13
|
+
puts "\n"
|
14
|
+
puts \
|
15
|
+
"Preps a video file for datamoshing with moshy by converting it into an
|
16
|
+
AVI with no B-Frames (they're not good for moshing), and placing as
|
17
|
+
few I-Frames as possible. Requires ffmpeg be installed locally. Check
|
18
|
+
the repository's README.md for more information on how to install ffmpeg.
|
19
|
+
|
20
|
+
This command is meant to be a simple one-liner that makes your datamoshing
|
21
|
+
workflow faster. Under the covers, it runs the following ffmpeg command:
|
22
|
+
|
23
|
+
ffmpeg -i <moshy input> -bf 0 -g 600 -b:v <moshy bitrate> -o <moshy output>
|
24
|
+
|
25
|
+
This takes in an input file (it should theoretically work with any video
|
26
|
+
file type that ffmpeg supports), makes sure that no B-Frames are rendered,
|
27
|
+
and sets the ideal I-frame interval to 600 (ffmpeg's max). This seems to
|
28
|
+
mean that an I-frame will only show up every 30 to 25 seconds
|
29
|
+
(600f / 30fps = 20s or 600f / 24fps = 25s), but I-Frames must be deposited
|
30
|
+
wherever there is a hard cut or transition in a video where a P-frame would
|
31
|
+
not be able to properly predict the motion of pixels."
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
default = {
|
36
|
+
:bitrate => 4196
|
37
|
+
}
|
38
|
+
|
39
|
+
parser = Slop::Parser.new(opts)
|
40
|
+
slop_options = parser.parse(ARGV)
|
41
|
+
@options = default.merge(slop_options) { |key, oldval, newval|
|
42
|
+
if newval.nil?
|
43
|
+
oldval
|
44
|
+
else
|
45
|
+
newval
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
# Check mandatory params
|
50
|
+
mandatory = [:input, :output]
|
51
|
+
missing = mandatory.select{ |param| @options[param].nil? }
|
52
|
+
unless missing.empty?
|
53
|
+
puts "Missing options: #{missing.join(', ')}"
|
54
|
+
puts slop_options
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
prep @options[:input], @options[:output], @options[:bitrate]
|
59
|
+
end
|
60
|
+
|
61
|
+
def prep(input, output, bitrate)
|
62
|
+
ffmpeg = Av::Commands::Ffmpeg.new
|
63
|
+
ffmpeg.add_source input
|
64
|
+
ffmpeg.add_destination output
|
65
|
+
|
66
|
+
# Ensures all frames come out as P-frames, B-frames don't
|
67
|
+
# dupe or mosh properly
|
68
|
+
ffmpeg.add_output_param ['bf', 0]
|
69
|
+
# Keyframe interval, sets as few I-frames as possible.
|
70
|
+
# ffmpeg will complain about anything over 600 and cap it.
|
71
|
+
ffmpeg.add_output_param ['g', 600]
|
72
|
+
# Bitrate
|
73
|
+
ffmpeg.add_output_param ['b:v', bitrate.to_s + 'k']
|
74
|
+
|
75
|
+
ffmpeg.run
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/moshy.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'moshy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "moshy"
|
8
|
+
spec.version = Moshy::VERSION
|
9
|
+
spec.summary = "datamoshing utility kit for common tasks with AVI files"
|
10
|
+
spec.description = spec.summary
|
11
|
+
spec.authors = ["wayspurrchen"]
|
12
|
+
spec.email = 'wayspurrchen@gmail.com'
|
13
|
+
spec.homepage = 'https://github.com/wayspurrchen/moshy'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "slop"
|
22
|
+
spec.add_runtime_dependency "aviglitch"
|
23
|
+
spec.add_runtime_dependency "av"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
end
|
metadata
CHANGED
@@ -1,34 +1,107 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moshy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- wayspurrchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
date: 2015-09-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: slop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aviglitch
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: av
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: datamoshing utility kit for common tasks with AVI files
|
26
84
|
email: wayspurrchen@gmail.com
|
27
|
-
executables:
|
85
|
+
executables:
|
86
|
+
- moshy
|
28
87
|
extensions: []
|
29
88
|
extra_rdoc_files: []
|
30
89
|
files:
|
31
|
-
-
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- bin/moshy
|
96
|
+
- lib/moshy.rb
|
97
|
+
- lib/moshy/bake.rb
|
98
|
+
- lib/moshy/inspect.rb
|
99
|
+
- lib/moshy/isplit.rb
|
100
|
+
- lib/moshy/pdupe.rb
|
101
|
+
- lib/moshy/ppulse.rb
|
102
|
+
- lib/moshy/prep.rb
|
103
|
+
- lib/moshy/version.rb
|
104
|
+
- moshy.gemspec
|
32
105
|
homepage: https://github.com/wayspurrchen/moshy
|
33
106
|
licenses:
|
34
107
|
- MIT
|
@@ -39,12 +112,12 @@ require_paths:
|
|
39
112
|
- lib
|
40
113
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
114
|
requirements:
|
42
|
-
- -
|
115
|
+
- - ">="
|
43
116
|
- !ruby/object:Gem::Version
|
44
117
|
version: '0'
|
45
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
119
|
requirements:
|
47
|
-
- -
|
120
|
+
- - ">="
|
48
121
|
- !ruby/object:Gem::Version
|
49
122
|
version: '0'
|
50
123
|
requirements: []
|
data/moshy.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require_relative 'lib/inspect'
|
2
|
-
require_relative 'lib/isplit'
|
3
|
-
require_relative 'lib/pdupe'
|
4
|
-
require_relative 'lib/bake'
|
5
|
-
require_relative 'lib/prep'
|
6
|
-
require_relative 'lib/ppulse'
|
7
|
-
require 'aviglitch'
|
8
|
-
require 'optparse'
|
9
|
-
require 'slop'
|
10
|
-
require 'av'
|
11
|
-
|
12
|
-
module Moshy
|
13
|
-
def self.top_level_help
|
14
|
-
$options = {
|
15
|
-
|
16
|
-
}
|
17
|
-
opts = OptionParser.new do |opts|
|
18
|
-
opts.banner = "
|
19
|
-
moshy, a Ruby utility for making it easier to datamosh AVI files. It has
|
20
|
-
multiple modes that can be run with the -m or --mode option.
|
21
|
-
|
22
|
-
MODES DETAILS
|
23
|
-
-------------
|
24
|
-
|
25
|
-
\"prep\"
|
26
|
-
------
|
27
|
-
Preps a video file for datamoshing with moshy by converting it
|
28
|
-
into an AVI with no B-Frames (they're not good for moshing), and placing as
|
29
|
-
few I-Frames as possible. Requires ffmpeg be installed locally.
|
30
|
-
|
31
|
-
\"isplit\"
|
32
|
-
--------
|
33
|
-
Extracts individual clips from an AVI where each clip is
|
34
|
-
separated by I-frames in the original AVI. Great for getting specific
|
35
|
-
clips out of a larger video and later doing I-frame moshing.
|
36
|
-
|
37
|
-
\"pdupe\"
|
38
|
-
-------
|
39
|
-
Duplicates a P-frame at a given frame a certain amount. To find
|
40
|
-
out which frames are P-frames, use software like avidemux to look at the
|
41
|
-
frame type. WARNING: This mode is a little glitchy. You may need to set
|
42
|
-
the interval 1 or 2 above or below the frame number you actually want to
|
43
|
-
duplicate. I'm not sure why this happens, but try it with a small
|
44
|
-
duplication amount first. NOTE: This can mode take a while to process
|
45
|
-
over 60-90 frame dupes.
|
46
|
-
|
47
|
-
\"ppulse\"
|
48
|
-
--------
|
49
|
-
Takes c number of frames and every n frames and duplicates them a
|
50
|
-
given amount, resulting in a consistent P-duplication datamosh that's
|
51
|
-
good for creating rhythmic effects. This was originally created to
|
52
|
-
create mosh effects in sync with a beat for a music video.
|
53
|
-
|
54
|
-
\"bake\"
|
55
|
-
------
|
56
|
-
\"Bakes\" your datamosh by creating a new video file from your
|
57
|
-
datamoshed .avi, causing the datamosh effects to be treated as the actual
|
58
|
-
content of the new video instead of an error. Requires ffmpeg to be
|
59
|
-
installed locally.
|
60
|
-
|
61
|
-
\"inspect\"
|
62
|
-
---------
|
63
|
-
Reads an .avi file and prints which video frames are keyframes
|
64
|
-
(I-Frames) and which frames are delta frames (P-frames or B-frames). moshy
|
65
|
-
cannot tell the difference between a P-frame or a B-frame, so you will want
|
66
|
-
to use avidemux or another program if you need to know.
|
67
|
-
|
68
|
-
Run moshy with mode -m <mode> --help to see options for individual modes.
|
69
|
-
"
|
70
|
-
end
|
71
|
-
|
72
|
-
begin
|
73
|
-
opts.parse
|
74
|
-
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument
|
75
|
-
end
|
76
|
-
|
77
|
-
puts opts
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Because we have multiple modes, we do some initial basic arg checking
|
82
|
-
# to see if they specified a mode. If not, we show the top-level help menu.
|
83
|
-
result = Slop.parse suppress_errors: true do |o|
|
84
|
-
o.string '-m', '--mode'
|
85
|
-
end
|
86
|
-
|
87
|
-
mode_classes = {
|
88
|
-
"inspect" => Moshy::Inspect,
|
89
|
-
"isplit" => Moshy::ISplit,
|
90
|
-
"pdupe" => Moshy::PDupe,
|
91
|
-
"ppulse" => Moshy::PPulse,
|
92
|
-
"prep" => Moshy::Prep,
|
93
|
-
"bake" => Moshy::Bake
|
94
|
-
}
|
95
|
-
|
96
|
-
if mode_classes.has_key? result[:m]
|
97
|
-
# We need to strip out the "m" otherwise our other arg parsers
|
98
|
-
# will choke on the extra parameter
|
99
|
-
ARGV.each_with_index do |o, i|
|
100
|
-
if o == "-m" || o == "--m"
|
101
|
-
ARGV.delete_at(i + 1)
|
102
|
-
ARGV.delete_at(i)
|
103
|
-
break
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
mode_classes[result[:m]].new ARGV
|
108
|
-
else
|
109
|
-
Moshy.top_level_help
|
110
|
-
end
|