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 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