distorted 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +661 -0
  3. data/README.md +4 -139
  4. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Less_Perfect_DOS_VGA.png +0 -0
  5. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/More_Perfect_DOS_VGA.png +0 -0
  6. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Perfect_DOS_VGA.png +0 -0
  7. data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/less_more_perfect_dos_vga_437.html +52 -0
  8. data/font/1252/LICENSE/PerfectDOSVGA437/font-comment.php@file=perfect_dos_vga_437.html +5 -0
  9. data/font/1252/LessPerfectDOSVGA.ttf +0 -0
  10. data/font/1252/MorePerfectDOSVGA.ttf +0 -0
  11. data/font/1252/Perfect DOS VGA 437 Win.ttf +0 -0
  12. data/font/437/Perfect DOS VGA 437.ttf +0 -0
  13. data/font/437/dos437.txt +72 -0
  14. data/font/65001/Anonymous Pro B.ttf +0 -0
  15. data/font/65001/Anonymous Pro BI.ttf +0 -0
  16. data/font/65001/Anonymous Pro I.ttf +0 -0
  17. data/font/65001/Anonymous Pro.ttf +0 -0
  18. data/font/65001/LICENSE/AnonymousPro/FONTLOG.txt +45 -0
  19. data/font/65001/LICENSE/AnonymousPro/OFL-FAQ.txt +235 -0
  20. data/font/65001/LICENSE/AnonymousPro/OFL.txt +94 -0
  21. data/font/65001/LICENSE/AnonymousPro/README.txt +55 -0
  22. data/font/850/ProFont-Bold-01/LICENSE +22 -0
  23. data/font/850/ProFont-Bold-01/readme.txt +28 -0
  24. data/font/850/ProFontWindows-Bold.ttf +0 -0
  25. data/font/850/ProFontWindows.ttf +0 -0
  26. data/font/850/Profont/LICENSE +22 -0
  27. data/font/850/Profont/readme.txt +31 -0
  28. data/font/932/LICENSE/README-ttf.txt +213 -0
  29. data/font/932/mona.ttf +0 -0
  30. data/lib/distorted/error_code.rb +8 -0
  31. data/lib/distorted/font.rb +192 -0
  32. data/lib/distorted/image.rb +121 -0
  33. data/lib/distorted/modular_technology/pango.rb +75 -0
  34. data/lib/distorted/monkey_business/hash.rb +33 -0
  35. data/lib/distorted/monkey_business/mnemoniq.rb +8 -0
  36. data/lib/distorted/monkey_business/set.rb +15 -0
  37. data/lib/distorted/monkey_business/string.rb +6 -0
  38. data/lib/distorted/pdf.rb +110 -0
  39. data/lib/distorted/svg.rb +21 -0
  40. data/lib/distorted/text.rb +241 -0
  41. data/lib/distorted/version.rb +20 -0
  42. data/lib/distorted/video.rb +193 -0
  43. data/test/distorted_test.rb +11 -0
  44. data/test/test_helper.rb +4 -0
  45. metadata +49 -5
@@ -0,0 +1,6 @@
1
+ class String
2
+ # https://stackoverflow.com/a/22586646
3
+ def map
4
+ size.times.with_object('') {|i,s| s << yield(self[i])}
5
+ end
6
+ end
@@ -0,0 +1,110 @@
1
+ require 'set'
2
+
3
+ require 'hexapdf'
4
+ require 'mime/types'
5
+
6
+
7
+ module Cooltrainer
8
+ module DistorteD
9
+ class PDF
10
+
11
+ MEDIA_TYPE = 'application'.freeze
12
+ SUB_TYPE = 'pdf'.freeze
13
+
14
+ MIME_TYPES = MIME::Types["#{MEDIA_TYPE}/#{SUB_TYPE}"].to_set
15
+
16
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#Attributes
17
+ # https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
18
+ PDF_OPEN_PARAMS = Array[
19
+ # Keep the PDF Open Params in the order they are defined
20
+ # in the Adobe documentation, since it says they should
21
+ # be specified in the URL in that same order.
22
+ # Ruby's Set doesn't guarantee order, so use a plain Array here.
23
+ :nameddest,
24
+ :page,
25
+ :comment,
26
+ :collab,
27
+ :zoom,
28
+ :view,
29
+ :viewrect,
30
+ :pagemode,
31
+ :scrollbar,
32
+ :search,
33
+ :toolbar,
34
+ :statusbar,
35
+ :messages,
36
+ :navpanes,
37
+ :highlight,
38
+ :fdf,
39
+ ]
40
+ ATTRS = Set[
41
+ :alt,
42
+ :caption,
43
+ :height, #<object> viewer container height.
44
+ :width, # <object> viewer container width.
45
+ ].merge(PDF_OPEN_PARAMS)
46
+
47
+ # "You cannot use the reserved characters =, #, and &.
48
+ # There is no way to escape these special characters."
49
+ RESERVED_CHARACTERS_FRAGMENT = '[^=#&]+'.freeze
50
+
51
+ FLOAT_INT_FRAGMENT = '[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)'.freeze
52
+ ZERO_TO_ONE_HUNDRED = /^(([1-9]\d?|1\d{1})([.,]\d{0,1})?|100([.,]0{1})?)$/
53
+ BOOLEAN_SET = Set[0, 1, false, true, '0'.freeze, '1'.freeze, 'false'.freeze, 'true'.freeze]
54
+
55
+ ATTRS_DEFAULT = {
56
+ :height => '100%'.freeze,
57
+ :width => '100%'.freeze,
58
+ # BEGIN PDF Open Parameters
59
+ :page => 1,
60
+ :view => :Fit,
61
+ :pagemode => :none,
62
+ :scrollbar => 1,
63
+ :toolbar => 1,
64
+ :statusbar => 1,
65
+ :messages => 0,
66
+ :navpanes => 1,
67
+ # END PDF Open Parameters
68
+ }
69
+
70
+ # Adobe's PDF Open Parameters documentation sez:
71
+ # "Individual parameters, together with their values (separated by & or #),
72
+ # can be no greater then 32 characters in length."
73
+ # …but then goes on to show some examples (like `comment`)
74
+ # that are clearly longer than 32 characters.
75
+ # Dunno. I'll err on the side of giving you a footgun.
76
+ ATTRS_VALUES = {
77
+ :nameddest => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
78
+ :page => /\d/,
79
+ :comment => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
80
+ :collab => /^(DAVFDF|FSFDF|DB)@#{RESERVED_CHARACTERS_FRAGMENT}$/,
81
+ :zoom => /^#{FLOAT_INT_FRAGMENT}(,#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT})?$/,
82
+ :view => /^Fit(H|V|B|BH|BV(,#{FLOAT_INT_FRAGMENT})?)?$/,
83
+ :viewrect => /^#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT}$/,
84
+ :pagemode => Set[:none, :thumbs, :bookmarks],
85
+ :scrollbar => BOOLEAN_SET,
86
+ :search => /^#{RESERVED_CHARACTERS_FRAGMENT}(,\s#{RESERVED_CHARACTERS_FRAGMENT})*$/,
87
+ :toolbar => BOOLEAN_SET,
88
+ :statusbar => BOOLEAN_SET,
89
+ :messages => BOOLEAN_SET,
90
+ :navpanes => BOOLEAN_SET,
91
+ :fdf => /^#{RESERVED_CHARACTERS_FRAGMENT}$/,
92
+ }
93
+
94
+
95
+ def self.optimize(src, dest)
96
+ HexaPDF::Document.open(src) do |doc|
97
+ doc.task(
98
+ :optimize,
99
+ compact: true,
100
+ object_streams: :generate,
101
+ xref_streams: :generate,
102
+ compress_pages: false,
103
+ )
104
+ doc.write(dest)
105
+ end
106
+ end
107
+
108
+ end # PDF
109
+ end # DistorteD
110
+ end # Cooltrainer
@@ -0,0 +1,21 @@
1
+ require 'set'
2
+
3
+ require 'mime/types'
4
+ require 'svg_optimizer'
5
+
6
+ module Cooltrainer
7
+ module DistorteD
8
+ class SVG < Image
9
+
10
+ SUB_TYPE = 'svg'.freeze
11
+
12
+ MIME_TYPES = MIME::Types[/^#{self::MEDIA_TYPE}\/#{self::SUB_TYPE}/, :complete => true].to_set
13
+
14
+ def self.optimize(src, dest)
15
+ # TODO: Make optimizations/plugins configurable
16
+ SvgOptimizer.optimize_file(src, dest, SvgOptimizer::DEFAULT_PLUGINS)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,241 @@
1
+ require 'set'
2
+
3
+ require 'ttfunk' # Font metadata extraction
4
+ require 'charlock_holmes' # Text file charset detection
5
+
6
+ require 'distorted/monkey_business/string' # String#map
7
+ require 'distorted/modular_technology/pango'
8
+
9
+ require 'distorted/image'
10
+
11
+ require 'mime/types'
12
+
13
+ # No need to do all the fancy library versioning in a subclass.
14
+ require 'vips'
15
+
16
+
17
+ module Cooltrainer
18
+ module DistorteD
19
+ class Text < Image
20
+
21
+ include Cooltrainer::DistorteD::Tech::Pango;
22
+
23
+
24
+ MEDIA_TYPE = 'text'.freeze
25
+
26
+ MIME_TYPES = MIME::Types[/^#{self::MEDIA_TYPE}\/(plain|x-nfo)/, :complete => true].to_set
27
+
28
+ ATTRS = Set[
29
+ :alt,
30
+ :crop,
31
+ :font,
32
+ :encoding,
33
+ :spacing,
34
+ :dpi,
35
+ ]
36
+ ATTRS_VALUES = {
37
+ :spacing => Set[:monospace, :proportional],
38
+ }
39
+ ATTRS_DEFAULT = {
40
+ :crop => :none,
41
+ :dpi => 144,
42
+ }
43
+
44
+ # Track supported fonts by codepage.
45
+ # Avoid renaming these from the original archives / websites.
46
+ # Try not to go nuts here bloating the size of our Gem for a
47
+ # very niche feature, but I want to ensure good coverage too.
48
+ #
49
+ # Treat codepage 8859 documents as codepage 1252 to avoid breaking smart-
50
+ # quotes and other printable chars in 1252 that are control chars in 8859.
51
+ # https://encoding.spec.whatwg.org/#names-and-labels
52
+ #
53
+ # Numeric key for UTF-8 is codepage 65001 like Win32:
54
+ # https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers
55
+ FONT_FILENAME = {
56
+ :anonpro => 'Anonymous Pro.ttf'.freeze,
57
+ :anonpro_b => 'Anonymous Pro B.ttf'.freeze,
58
+ :anonpro_bi => 'Anonymous Pro BI.ttf'.freeze,
59
+ :anonpro_i => 'Anonymous Pro I.ttf'.freeze,
60
+ :lessperfectdosvga => 'LessPerfectDOSVGA.ttf'.freeze,
61
+ :moreperfectdisvga => 'MorePerfectDOSVGA.ttf'.freeze,
62
+ :perfectdosvgawin => 'Perfect DOS VGA 437 Win.ttf'.freeze,
63
+ :mona => 'mona.ttf'.freeze,
64
+ :perfectdosvga => 'Perfect DOS VGA 437.ttf'.freeze,
65
+ :profont => 'ProFontWindows.ttf'.freeze,
66
+ :profont_b => 'ProFontWindows-Bold.ttf'.freeze,
67
+ }
68
+ # Certain fonts are more suitable for certain codepages,
69
+ # so track each codepage's available fonts…
70
+ CODEPAGE_FONT = {
71
+ 65001 => [
72
+ :anonpro,
73
+ :anonpro_b,
74
+ :anonpro_bi,
75
+ :anonpro_i,
76
+ ],
77
+ 1252 => [
78
+ :lessperfectdosvga,
79
+ :moreperfectdosvga,
80
+ :perfectdosvgawin,
81
+ ],
82
+ 932 => [
83
+ :mona,
84
+ ],
85
+ 850 => [
86
+ :profont,
87
+ :profont_b,
88
+ ],
89
+ 437 => [
90
+ :perfectdosvga,
91
+ ],
92
+ }
93
+ # …as well as the inverse, the numeric codepage for each font:
94
+ FONT_CODEPAGE = CODEPAGE_FONT.reduce(Hash.new([])) { |memo, (key, values)|
95
+ values.each { |value| memo[value] = key }
96
+ memo
97
+ }
98
+
99
+
100
+ # Using a numeric key for things for simplicity.
101
+ # TODO: Replace this with Ruby's built-in Encoding class after I have
102
+ # a better idea what I want to do.
103
+ def codepage
104
+ case @encoding
105
+ when 'UTF-8'.freeze then 65001
106
+ when 'Shift_JIS'.freeze then 932
107
+ when 'IBM437'.freeze then 437
108
+ else 1252
109
+ end
110
+ end
111
+
112
+ # Return a Pango Markup escaped version of the document.
113
+ def to_pango
114
+ # https://developer.gnome.org/glib/stable/glib-Simple-XML-Subset-Parser.html#g-markup-escape-text
115
+ escaped = @contents.map{ |c|
116
+ g_markup_escape_char(c)
117
+ }
118
+ if spacing == :monospace
119
+ "<tt>" << escaped << "</tt>"
120
+ else
121
+ escaped
122
+ end
123
+ end
124
+
125
+ def initialize(src, encoding: nil, font: nil, spacing: nil, dpi: ATTRS_DEFAULT[:dpi])
126
+ @src = src
127
+ @liquid_spacing = spacing
128
+
129
+ # VIPS makes us provide the text content as a single variable,
130
+ # so we may as well just one-shot File.read() it into memory.
131
+ # https://kunststube.net/encoding/
132
+ contents = File.read(@src)
133
+
134
+ # It's not easy or even possible in some cases to tell the "true" codepage
135
+ # we should use for any given text document, but using character detection
136
+ # is worth a shot if the user gave us nothing.
137
+ detected = CharlockHolmes::EncodingDetector.detect(contents)
138
+ @encoding = (encoding || detected[:encoding] || 'UTF-8'.freeze).to_s
139
+ @contents = CharlockHolmes::Converter.convert(contents, @encoding, 'UTF-8'.freeze)
140
+
141
+ # Set the shorthand symbol key for our chosen font.
142
+ @font = font&.to_sym || self.singleton_class.const_get(:CODEPAGE_FONT)[codepage].first
143
+
144
+ # Load font metadata directly from the file so we don't have to
145
+ # duplicate it here to feed to Vips/Pango.
146
+ #
147
+ # irb(main)> font_meta.name.font_name
148
+ # => ["Perfect DOS VGA 437", "\x00P\x00e\x00r\x00f\x00e\x00c\x00t\x00 \x00D\x00O\x00S\x00 \x00V\x00G\x00A\x00 \x004\x003\x007"]
149
+ # irb(main)> font_meta.name.font_family
150
+ # => ["Perfect DOS VGA 437", "\x00P\x00e\x00r\x00f\x00e\x00c\x00t\x00 \x00D\x00O\x00S\x00 \x00V\x00G\x00A\x00 \x004\x003\x007"]
151
+ # irb(main)> font_meta.name.font_subfamily
152
+ # => ["Regular", "\x00R\x00e\x00g\x00u\x00l\x00a\x00r"]
153
+ # irb(main)> font_meta.name.postscript_name
154
+ # => "PerfectDOSVGA437"
155
+ # irb(main)> font_meta.line_gap
156
+ # => 0
157
+ @font_meta = TTFunk::File.open(font_path)
158
+
159
+ # https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
160
+ @image = Vips::Image.text(
161
+ # This string must be well-escaped Pango Markup:
162
+ # https://developer.gnome.org/pango/stable/pango-Markup.html
163
+ # However the official function for escaping text is
164
+ # not implemented in Ruby GLib, so we have to do it ourselves.
165
+ to_pango,
166
+ **{
167
+ # String absolute path to TTF
168
+ :fontfile => font_path,
169
+ # It's not enough to just specify the TTF path;
170
+ # we must also specify a font family, subfamily, and size.
171
+ :font => "#{font_name} 16",
172
+ # Space between lines (in Points).
173
+ :spacing => @font_meta.line_gap,
174
+ :justify => true, # Requires libvips 8.8
175
+ :dpi => dpi.to_i,
176
+ },
177
+ )
178
+ end
179
+
180
+ protected
181
+
182
+ # Return the String absolute path to the TTF file
183
+ def font_path
184
+ File.join(
185
+ File.dirname(__FILE__), # distorted
186
+ '..'.freeze, # lib
187
+ '..'.freeze, # DistorteD-Ruby
188
+ 'font'.freeze,
189
+ font_codepage,
190
+ font_filename,
191
+ )
192
+ end
193
+
194
+ # Returns the numeric representation of the codepage
195
+ # covered by our font.
196
+ def font_codepage
197
+ self.singleton_class.const_get(:FONT_CODEPAGE)&.dig(@font).to_s
198
+ end
199
+
200
+ # Returns the basename (with file extension) of our font.
201
+ def font_filename
202
+ self.singleton_class.const_get(:FONT_FILENAME)&.dig(@font)
203
+ end
204
+
205
+ # Returns a boolean for whether or not this font is monospaced.
206
+ # true == monospace
207
+ # false == proportional
208
+ def spacing
209
+ # Monospace fonts will (read: should) have the same width
210
+ # for every glyph, so we can tell a monospace font by
211
+ # checking if a deduplicated widths table has size == 1:
212
+ # irb(main)> font.horizontal_metrics.widths.count
213
+ # => 256
214
+ # irb(main)> font.horizontal_metrics.widths.uniq.compact.length
215
+ # => 1
216
+ @font_meta.horizontal_metrics.widths.uniq.compact.length == 1 ? :monospace : :proportional
217
+ end
218
+
219
+ # Returns the Family and Subfamily as one string suitable for libvips
220
+ def font_name
221
+ "#{@font_meta.name.font_family.first.encode('UTF-8')} #{@font_meta.name.font_subfamily.first.encode('UTF-8')}"
222
+ end
223
+
224
+ # Returns the Pango-Markup-encoded UTF-8 String version + revision of the font
225
+ def font_version
226
+ g_markup_escape_text(@font_meta.name&.version&.first&.encode('UTF-8').to_s)
227
+ end
228
+
229
+ # Returns the Pango-Markup-encoded UTF-8 String font file description
230
+ def font_description
231
+ g_markup_escape_text(@font_meta.name&.description&.first&.encode('UTF-8').to_s)
232
+ end
233
+
234
+ # Returns the Pango-Markup-encoded UTF-8 String copyright information of the font
235
+ def font_copyright
236
+ g_markup_escape_text(@font_meta.name&.copyright&.first&.encode('UTF-8').to_s)
237
+ end
238
+
239
+ end # Text
240
+ end # DistorteD
241
+ end # Cooltrainer
@@ -0,0 +1,20 @@
1
+ #
2
+ # `.........-` `:/:::://.`
3
+ # `+/``+ssss:``-/:` `:o+y. `::::-----``
4
+ # -+- :hmNNmdhs- `o++` `-:--/ -/... ./shhyoy. +mmmmds+:--...`
5
+ # .//``odmNmmd+++` :+/h:-------------:+. ./...-..........---/--:.------// -.--------//o//o+/+- :hhdmmmmmh/`-/-`
6
+ # `:+- :ymNmmyy:...-:` /: ./////////////- ::/:/- `++++++++` -y /:.`.////+- /////- `/+++++++. :- .::+sydmNm+ `/+.
7
+ # -+:` /syyyo:--:+oso` /: ---:::::::syys. -yoy+s` :mmmmmmmd- .h` ``:osshhyyy` /oshhs` -:::::::. :: `-..-/dmo` `/yo:
8
+ # `/o:--------:+syyy++``+s+++++++++/``:h+os``oshdos``odhhhddyy-`.h.`.ssyhmmdmoh/``o+mmho``:++++++++++y+``----::``-oyss.
9
+ # +sssssssyyyyyhdmmsyo-+/----------.`.yooy-`-hodss/``---------.`.h-`.sohdhsymhsy.`:+yNmy/`.-----------:++ooooooo+sss+`
10
+ # `+dmmmdddddmmNNddhsyosooooooooooooooysoysssyymssssssssssssssssshyssyoh:` .mhsyssssodddyooooooooooooooysoyyyyyyyyy/
11
+ # /mNmdddhmdmmdy+-+mdhhhhhhhhhhhhhhhmhhyhhhmdmmdhhhhhhhhhhhhhhhdhhhsm+ ydddhhhhd-hdmdddddddddddddddyhdhdmmNms.
12
+ # .---.......` -yhNmmhddhhhdhmhmhddmNNmyyy+dmNdydhyyyhddhmmmmNmhh. -hyhNNNh: .mNmdmdddhhhdddmmyssssssso:
13
+ # `/oooooooooooooo:`-+o+/. `+oo+o++++ooo+oooooo- `-+oo/` :oooooooooooooo/
14
+ #
15
+
16
+ module Cooltrainer
17
+ module DistorteD
18
+ VERSION = '0.5.7'.freeze
19
+ end
20
+ end
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env ruby
2
+ # Tell the user to install the shared library if it's missing.
3
+ begin
4
+ require 'gst'
5
+ rescue LoadError => le
6
+ raise unless le.message =~ /libgst/
7
+
8
+ # Multiple OS help
9
+ help = <<~INSTALL
10
+
11
+ Please install the GStreamer library for your system.
12
+ INSTALL
13
+
14
+ # Re-raise with install message
15
+ raise $!, "#{help}\n#{$!}", $!.backtrace
16
+ end
17
+
18
+ require 'set'
19
+
20
+ require 'mime/types'
21
+
22
+ module Cooltrainer
23
+ module DistorteD
24
+ class Video
25
+
26
+ MEDIA_TYPE = 'video'.freeze
27
+ MIME_TYPES = MIME::Types[/^#{MEDIA_TYPE}/, :complete => true].to_set
28
+
29
+ # Attributes for our <video>.
30
+ # Automatically enabled as attrs for DD Liquid Tag.
31
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#Attributes
32
+ ATTRS = Set[:caption]
33
+
34
+ # Defaults for HTML Element attributes.
35
+ # Not every attr has to be listed here.
36
+ # Many need no default and just won't render.
37
+ ATTRS_DEFAULT = {}
38
+ ATTRS_VALUES = {}
39
+
40
+ attr_accessor :dest
41
+
42
+
43
+ def initialize(src, dest, basename)
44
+ @src = src
45
+ @dest = dest
46
+ @basename = basename
47
+ end
48
+
49
+ def rotate(angle=nil)
50
+ false
51
+ end
52
+
53
+ def clean
54
+ false
55
+ end
56
+
57
+ def generate
58
+ self.generate_hls
59
+ begin
60
+ self.generate_dash
61
+ rescue Gst::ParseError::NoSuchElement
62
+ # This is going away once the new dashsink2 lands in Gst so :effort:
63
+ end
64
+ end
65
+
66
+ def generate_dash
67
+ orig_dest = @dest
68
+ orig_path = @src
69
+
70
+ FileUtils.mkdir_p(File.dirname(orig_dest))
71
+
72
+ hls_dest = File.join(File.dirname(orig_dest), @basename + '.dash')
73
+ FileUtils.mkdir_p(hls_dest)
74
+ Jekyll.logger.debug(@tag_name, "Re-muxing #{orig_path} to #{hls_dest}.")
75
+
76
+ #FileUtils.rm(orig_dest) if File.exist?(orig_dest)
77
+ if not File.file?(orig_dest)
78
+ FileUtils.cp(orig_path, orig_dest)
79
+ end
80
+
81
+ # https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description
82
+ # TODO: Convert this from parse_launch() pipeline notation to Element objects
83
+ # TODO: Get source video duration/resolution/etc and use it to compute a
84
+ # value for `target-duration`.
85
+ # TODO: Also support urldecodebin for remote media.
86
+ pipeline, error = Gst.parse_launch("filesrc name=src ! decodebin name=demux ! videoconvert ! vaapih264enc ! queue2 ! h264parse ! queue2 ! mux.video dashsink name=mux max-files=0 playlist-length=0 target-duration=2 demux. ! audioconvert ! voaacenc ! queue2 ! mux.audio")
87
+
88
+ if pipeline.nil?
89
+ Jekyll.logger.error(@tag_name, "Parse error: #{error.message}")
90
+ return false
91
+ end
92
+
93
+ filesrc = pipeline.get_by_name('src')
94
+ filesrc.location = orig_path
95
+
96
+ hls_playlist = "#{hls_dest}/#{@basename}.m3u8"
97
+ hls = pipeline.get_by_name('mux')
98
+ hls.location = "#{hls_dest}/#{@basename}%05d.ts"
99
+ hls.playlist_location = hls_playlist
100
+
101
+ # TODO: config option for absolute vs relative segment URIs in the playlist.
102
+ #hls.playlist_root = @url
103
+
104
+ # TODO: dashsink support once there is a stable GStreamer release including it:
105
+ # https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/merge_requests/704
106
+
107
+ pipeline.play
108
+
109
+ # Play until End Of Stream
110
+ event_loop(pipeline)
111
+
112
+ pipeline.stop
113
+
114
+ end
115
+
116
+ def generate_hls
117
+ orig_dest = @dest
118
+ orig_path = @src
119
+
120
+ FileUtils.mkdir_p(File.dirname(orig_dest))
121
+
122
+ hls_dest = File.join(File.dirname(orig_dest), @basename + '.hls')
123
+ FileUtils.mkdir_p(hls_dest)
124
+ Jekyll.logger.debug(@tag_name, "Re-muxing #{orig_path} to #{hls_dest}.")
125
+
126
+ #FileUtils.rm(orig_dest) if File.exist?(orig_dest)
127
+ if not File.file?(orig_dest)
128
+ FileUtils.cp(orig_path, orig_dest)
129
+ end
130
+
131
+ # https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description
132
+ # TODO: Convert this from parse_launch() pipeline notation to Element objects
133
+ # TODO: Get source video duration/resolution/etc and use it to compute a
134
+ # value for `target-duration`.
135
+ # TODO: Also support urldecodebin for remote media.
136
+ pipeline, error = Gst.parse_launch("filesrc name=src ! decodebin name=demux ! videoconvert ! vaapih264enc ! queue2 ! h264parse ! queue2 ! mux.video hlssink2 name=mux max-files=0 playlist-length=0 target-duration=2 demux. ! audioconvert ! voaacenc ! queue2 ! mux.audio")
137
+
138
+ if pipeline.nil?
139
+ Jekyll.logger.error(@tag_name, "Parse error: #{error.message}")
140
+ return false
141
+ end
142
+
143
+ filesrc = pipeline.get_by_name('src')
144
+ filesrc.location = orig_path
145
+
146
+ hls_playlist = "#{hls_dest}/#{@basename}.m3u8"
147
+ hls = pipeline.get_by_name('mux')
148
+ hls.location = "#{hls_dest}/#{@basename}%05d.ts"
149
+ hls.playlist_location = hls_playlist
150
+
151
+ # TODO: config option for absolute vs relative segment URIs in the playlist.
152
+ #hls.playlist_root = @url
153
+
154
+ # TODO: dashsink support once there is a stable GStreamer release including it:
155
+ # https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/merge_requests/704
156
+
157
+ pipeline.play
158
+
159
+ # Play until End Of Stream
160
+ event_loop(pipeline)
161
+
162
+ pipeline.stop
163
+
164
+ # HACK HACK HACK: Replace X-ALLOW-CACHE line in playlist with YES.
165
+ # This property does not seem to be exposed to the outside of hlssink:
166
+ # https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/ext/hls/gsthlssink.c
167
+ text = File.read(hls_playlist)
168
+ File.write(hls_playlist, text.gsub(/^#EXT-X-ALLOW-CACHE:NO$/, '#EXT-X-ALLOW-CACHE:YES'))
169
+ end
170
+
171
+ def event_loop(pipeline)
172
+ running = true
173
+ bus = pipeline.bus
174
+
175
+ while running
176
+ message = bus.poll(Gst::MessageType::ANY, -1)
177
+
178
+ case message.type
179
+ when Gst::MessageType::EOS
180
+ running = false
181
+ when Gst::MessageType::WARNING
182
+ warning, _debug = message.parse_warning
183
+ Jekyll.logger.warning(@tag_name, warning)
184
+ when Gst::MessageType::ERROR
185
+ error, _debug = message.parse_error
186
+ Jekyll.logger.error(@tag_name, error)
187
+ running = false
188
+ end
189
+ end
190
+ end
191
+ end # Image
192
+ end # DistorteD
193
+ end # Cooltrainer