carrierwave-video 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in carrierwave-video.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,58 @@
1
+ = CarrierWave Video Converter
2
+
3
+ This gem adds a video converter using ffmpeg and the streamio-ffmpeg rubygem.
4
+ Note: for watermarking to work, there a fork of the streamio-ffmpeg gem that must be used.
5
+
6
+ = Installation
7
+
8
+ gem install carrierwave-video
9
+
10
+ Using bundler:
11
+
12
+ gem 'carrierwave-video'
13
+
14
+ = Usage
15
+
16
+ class VideoUploader < CarrierWave::Uploader::Base
17
+ include CarrierWave::VideoConverter
18
+
19
+ process encode_video: [:mp4, after_transcode: :set_success ]
20
+ end
21
+
22
+ class Video
23
+ mount_uploader :file, VideoUploader
24
+
25
+ def set_success
26
+ self.success = true
27
+ end
28
+ end
29
+
30
+ = Possible Options
31
+
32
+ Pass in options to process:
33
+
34
+ process encode_video: [:mp4, resolution: "200x200"]
35
+
36
+ Resolution passed to ffmpeg:
37
+ resolution: "640x360"
38
+
39
+ Watermark:
40
+ watermark: {
41
+ path: File.join(Rails.root, "directory", "file.png"),
42
+ position: :bottom_right, # also: :top_right, :bottom_left, :bottom_right
43
+ pixels_from_edge: 10
44
+ }
45
+
46
+ Callbacks:
47
+ callbacks: {
48
+ before_transcode: :method
49
+ after_transcode: :method
50
+ ensure: :method
51
+ }
52
+
53
+ Logging:
54
+ logger: :method #returns object that behaves like Logger
55
+
56
+ = Upcoming
57
+
58
+ * screengrabs
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "carrierwave-video/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "carrierwave-video"
7
+ s.version = Carrierwave::Video::VERSION
8
+ s.authors = ["rheaton"]
9
+ s.email = ["rachelmheaton@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Carrierwave extension that uses ffmpeg to transcode videos.}
12
+ s.description = %q{Transcodes to html5-friendly videos.}
13
+
14
+ s.rubyforge_project = "carrierwave-video"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+
24
+ s.add_runtime_dependency 'streamio-ffmpeg'#, :git => 'git://github.com/stakach/streamio-ffmpeg.git'
25
+ s.add_runtime_dependency 'carrierwave'
26
+ s.requirements << 'ruby, version 1.9 or greater'
27
+ s.requirements << 'ffmpeg, version 0.10 or greater with libx256, libfaac, libtheora, libvorbid, libvpx enabled'
28
+ end
@@ -0,0 +1,5 @@
1
+ module Carrierwave
2
+ module Video
3
+ VERSION = "0.0.1.pre"
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ require 'streamio-ffmpeg'
2
+ require 'carrierwave'
3
+ require 'carrierwave/video/ffmpeg_options'
4
+
5
+ module CarrierWave
6
+ module Video
7
+ extend ActiveSupport::Concern
8
+ module ClassMethods
9
+ def encode_video(target_format, options={})
10
+ process encode_video: [target_format, options]
11
+ end
12
+ end
13
+
14
+ def encode_video(format, opts={})
15
+ # move upload to local cache
16
+ cache_stored_file! if !cached?
17
+
18
+ options = CarrierWave::Video::FfmpegOptions.new(format, opts)
19
+ tmp_path = File.join( File.dirname(current_path), "tmpfile.#{format}" )
20
+
21
+
22
+ with_callbacks(opts) do
23
+ file = ::FFMPEG::Movie.new(current_path)
24
+ file.transcode(tmp_path, options.format_options, options.encoder_options)
25
+ File.rename tmp_path, current_path
26
+ end
27
+ end
28
+
29
+ private
30
+ def with_callbacks(opts, &block)
31
+ callbacks = opts[:callbacks] || {}
32
+ logger_opt = opts[:logger]
33
+ begin
34
+ send_callback(callbacks[:before_transcode])
35
+ setup_logger(logger_opt)
36
+ block.call
37
+ send_callback(callbacks[:after_transcode])
38
+ ensure
39
+ reset_logger
40
+ send_callback(callbacks[:ensure])
41
+ end
42
+ end
43
+
44
+ def send_callback(callback)
45
+ model.send(callback) if callback.present?
46
+ end
47
+
48
+ def setup_logger(opt)
49
+ return unless opt.present?
50
+ @ffmpeg_logger = ::FFMPEG.logger
51
+ ::FFMPEG.logger = model.send(opt)
52
+ end
53
+
54
+ def reset_logger
55
+ return unless @ffmpeg_logger
56
+ ::FFMPEG.logger = @ffmpeg_logger
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,69 @@
1
+ module CarrierWave
2
+ module Video
3
+ class FfmpegOptions
4
+ attr_reader :watermark_path, :watermark_position, :watermark_pixels,
5
+ :format, :resolution
6
+
7
+ def initialize(format, options)
8
+ @format = format.to_s
9
+ @watermark = options[:watermark].present?
10
+ @resolution = options[:resolution] || "640x360"
11
+ if watermark?
12
+ @watermark_path = options[:watermark][:path]
13
+ @watermark_position = options[:watermark][:position].to_s || :bottom_right
14
+ @watermark_pixels = options[:watermark][:pixels_from_edge] || 10
15
+ end
16
+ end
17
+
18
+ def encoder_options
19
+ {preserve_aspect_ratio: :width}
20
+ end
21
+
22
+ def format_options
23
+ format_options = case format
24
+ when "mp4"
25
+ {
26
+ video_codec: 'libx264',
27
+ audio_codec: 'libfaac',
28
+ custom: "-b 1500k -vpre slow -vpre baseline -g 30 #{watermark_params}"
29
+ }
30
+ when "webm"
31
+ {
32
+ video_codec: 'libvpx',
33
+ audio_codec: 'libvorbis',
34
+ custom: "-b 1500k -ab 160000 -f webm -g 30 #{watermark_params}"
35
+ }
36
+ when "ogv"
37
+ {
38
+ video_codec: 'libtheora',
39
+ audio_codec: 'libvorbis',
40
+ custom: "-b 1500k -ab 160000 -g 30 #{watermark_params}"
41
+ }
42
+ else
43
+ {}
44
+ end
45
+ { resolution: resolution }.merge(format_options)
46
+ end
47
+
48
+ def watermark?
49
+ @watermark
50
+ end
51
+
52
+ def watermark_params
53
+ return "" unless watermark?
54
+ positioning = case watermark_position
55
+ when 'bottom_left'
56
+ "#{watermark_pixels}:main_h-overlay_h-#{watermark_pixels}"
57
+ when 'bottom_right'
58
+ "main_w-overlay_w-#{watermark_pixels}:main_h-overlay_h-#{watermark_pixels}"
59
+ when 'top_left'
60
+ "#{watermark_pixels}:#{watermark_pixels}"
61
+ when 'top_right'
62
+ "main_w-overlay_w-#{watermark_pixels}:#{watermark_pixels}"
63
+ end
64
+
65
+ "-vf \"movie=#{watermark_path} [logo]; [in][logo] overlay=#{positioning} [out]\""
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,163 @@
1
+ require 'spec_helper'
2
+
3
+ describe CarrierWave::Video do
4
+ class Video; end
5
+
6
+ class TestVideoUploader
7
+ include CarrierWave::Video
8
+ def cached?; end
9
+ def cache_stored_file!; end
10
+ def model
11
+ @video ||= Video.new
12
+ end
13
+ end
14
+
15
+ let(:converter) { TestVideoUploader.new }
16
+
17
+ describe ".encode_video" do
18
+ it "processes the model" do
19
+ TestVideoUploader.should_receive(:process).with(encode_video: ["format", :opts])
20
+ TestVideoUploader.encode_video("format", :opts)
21
+ end
22
+
23
+ it "does not require options" do
24
+ TestVideoUploader.should_receive(:process).with(encode_video: ["format", {}])
25
+ TestVideoUploader.encode_video("format")
26
+ end
27
+ end
28
+
29
+ describe "#encode_video" do
30
+ let(:format) { 'webm' }
31
+ let(:movie) { mock }
32
+
33
+ before do
34
+ converter.stub(:current_path).and_return('video/path/file.mov')
35
+
36
+ FFMPEG::Movie.should_receive(:new).and_return(movie)
37
+ end
38
+
39
+ context "no options set" do
40
+ before { File.should_receive(:rename) }
41
+ it "is calls transcode with correct format options" do
42
+ movie.should_receive(:transcode) do |path, opts, codec_opts|
43
+ codec_opts.should == {preserve_aspect_ratio: :width}
44
+
45
+ opts[:video_codec].should == 'libvpx'
46
+ opts[:audio_codec].should == 'libvorbis'
47
+ opts[:custom].should == '-b 1500k -ab 160000 -f webm -g 30 '
48
+
49
+ path.should == "video/path/tmpfile.#{format}"
50
+ end
51
+
52
+ converter.encode_video(format)
53
+ end
54
+ end
55
+
56
+ context "callbacks set" do
57
+ before { movie.should_receive(:transcode) }
58
+
59
+ context "no exceptions raised" do
60
+ before { File.should_receive(:rename) }
61
+
62
+ it "calls before_transcode, after_transcode, and ensure" do
63
+ converter.model.should_receive(:method1).ordered
64
+ converter.model.should_receive(:method2).ordered
65
+ converter.model.should_receive(:method3).ordered
66
+
67
+ converter.encode_video(format, callbacks: {
68
+ before_transcode: :method1,
69
+ after_transcode: :method2,
70
+ ensure: :method3
71
+ })
72
+ end
73
+ end
74
+
75
+ context "exception raised" do
76
+ let(:e) { StandardError.new("test error") }
77
+ before { File.should_receive(:rename).and_raise(e) }
78
+
79
+ it "calls before_transcode and ensure" do
80
+ converter.model.should_receive(:method1).ordered
81
+ converter.model.should_not_receive(:method2)
82
+ converter.model.should_receive(:method3).ordered
83
+
84
+ lambda do
85
+ converter.encode_video(format, callbacks: {
86
+ before_transcode: :method1,
87
+ after_transcode: :method2,
88
+ ensure: :method3
89
+ })
90
+ end.should raise_exception(e)
91
+ end
92
+ end
93
+ end
94
+
95
+ context "logger set" do
96
+ let(:logger) { mock }
97
+ before do
98
+ converter.model.stub(:logger).and_return(logger)
99
+ movie.should_receive(:transcode)
100
+ end
101
+
102
+ context "with no exceptions" do
103
+ before { File.should_receive(:rename) }
104
+
105
+ it "sets FFMPEG logger to logger and resets" do
106
+ old_logger = ::FFMPEG.logger
107
+ ::FFMPEG.should_receive(:logger=).with(logger).ordered
108
+ ::FFMPEG.should_receive(:logger=).with(old_logger).ordered
109
+ converter.encode_video(format, logger: :logger)
110
+ end
111
+ end
112
+
113
+ context "with exceptions" do
114
+ let(:e) { StandardError.new("test error") }
115
+ before { File.should_receive(:rename).and_raise(e) }
116
+
117
+ it "logs exception" do
118
+ lambda do
119
+ converter.encode_video(format, logger: :logger)
120
+ end.should raise_exception(e)
121
+ end
122
+ end
123
+ end
124
+
125
+ context "watermark set" do
126
+ before { File.should_receive(:rename) }
127
+
128
+ it "appends watermark params to custom params for ffmpeg" do
129
+ movie.should_receive(:transcode) do |path, opts, codec_opts|
130
+ codec_opts.should == {preserve_aspect_ratio: :width}
131
+
132
+ opts[:video_codec].should == 'libvpx'
133
+ opts[:audio_codec].should == 'libvorbis'
134
+ opts[:custom].should == "-b 1500k -ab 160000 -f webm -g 30 -vf \"movie=path/to/file.png [logo]; [in][logo] overlay=5:main_h-overlay_h-5 [out]\""
135
+
136
+ path.should == "video/path/tmpfile.#{format}"
137
+ end
138
+
139
+ converter.encode_video(format, watermark: {
140
+ path: 'path/to/file.png',
141
+ position: :bottom_left,
142
+ pixels_from_edge: 5
143
+ })
144
+ end
145
+
146
+ it "only requires path watermark parameter" do
147
+ movie.should_receive(:transcode) do |path, opts, codec_opts|
148
+ codec_opts.should == {preserve_aspect_ratio: :width}
149
+
150
+ opts[:video_codec].should == 'libvpx'
151
+ opts[:audio_codec].should == 'libvorbis'
152
+ opts[:custom].should == "-b 1500k -ab 160000 -f webm -g 30 -vf \"movie=path/to/file.png [logo]; [in][logo] overlay= [out]\""
153
+
154
+ path.should == "video/path/tmpfile.#{format}"
155
+ end
156
+
157
+ converter.encode_video(format, watermark: {
158
+ path: 'path/to/file.png'
159
+ })
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,6 @@
1
+ require 'streamio-ffmpeg'
2
+ require 'carrierwave/video'
3
+
4
+ RSpec.configure do |config|
5
+ config.mock_with :rspec
6
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: carrierwave-video
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - rheaton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70335404267560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70335404267560
25
+ - !ruby/object:Gem::Dependency
26
+ name: streamio-ffmpeg
27
+ requirement: &70335404266960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70335404266960
36
+ - !ruby/object:Gem::Dependency
37
+ name: carrierwave
38
+ requirement: &70335404266520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70335404266520
47
+ description: Transcodes to html5-friendly videos.
48
+ email:
49
+ - rachelmheaton@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - README.rdoc
57
+ - Rakefile
58
+ - carrierwave-video.gemspec
59
+ - lib/carrierwave-video/version.rb
60
+ - lib/carrierwave/video.rb
61
+ - lib/carrierwave/video/ffmpeg_options.rb
62
+ - spec/lib/carrierwave_video_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: ''
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>'
80
+ - !ruby/object:Gem::Version
81
+ version: 1.3.1
82
+ requirements:
83
+ - ruby, version 1.9 or greater
84
+ - ffmpeg, version 0.10 or greater with libx256, libfaac, libtheora, libvorbid, libvpx
85
+ enabled
86
+ rubyforge_project: carrierwave-video
87
+ rubygems_version: 1.8.17
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Carrierwave extension that uses ffmpeg to transcode videos.
91
+ test_files:
92
+ - spec/lib/carrierwave_video_spec.rb
93
+ - spec/spec_helper.rb