moshy 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10b05cab4000093c123ded361c51188a2a35294a
4
- data.tar.gz: 05b77cee5a180f285e48c97627626ba6190e1229
3
+ metadata.gz: b0c17c458da7a8ea731db28eaf6a919469107b9d
4
+ data.tar.gz: fa5b4eea8ca1538790eaf989e77e40b058fcf01c
5
5
  SHA512:
6
- metadata.gz: ae34d58ed8350a14a71422a49283d99917a8f08e7b1698c867f73f859f94644720169d1f115bc2b8cd4e459587381fed7cc59b7fa7c80ec30a43397b578909d1
7
- data.tar.gz: ff48178601ce668c27770ba535c431528c036a0ed9e20c9dcd865acdb49ada0915847c96ed8791819dda485c3dfaf16ea7d45e400cd61ce006a0cecca06f8c98
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in moshy.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Moshy
2
+ VERSION = "1.0.0"
3
+ 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: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Way Spurr-Chen
7
+ - wayspurrchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2010-09-12 00:00:00.000000000 Z
12
- dependencies: []
13
- description: |-
14
- moshy is a datamoshing utility kit for AVI files, based heavily on [aviglitch](https://github.com/ucnv/aviglitch).
15
- It's designed to make common datamoshing tasks easier from a command line interface
16
- without having to open avidemux or other GUI tools. It lets you do stuff like:
17
-
18
- - Convert video files into AVI video files with minimal I-Frames and no B-frames for ultimate moshability
19
- - Create P-Frame duplication effects quickly
20
- - Split a long video file into multiple clips based on its I-Frames
21
- - "Bake" your datamoshed video, encoding the corruption as actual video content for uploading to video services or moshing even further!
22
- - Identifying keyframe and deltaframe indexes in any AVI file
23
- - ...and more!
24
-
25
- See https://github.com/wayspurrchen/moshy for detailed documentation.
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
- - moshy.rb
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