vidibus-recording 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ cli
5
+ recordings
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in vidibus-rtmpdump.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ vidibus-recording (0.0.1)
5
+ delayed_job_mongoid
6
+ mongoid (~> 2.0.0.beta.20)
7
+ open4
8
+ robustthread
9
+ vidibus-uuid
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ specs:
14
+ activemodel (3.0.3)
15
+ activesupport (= 3.0.3)
16
+ builder (~> 2.1.2)
17
+ i18n (~> 0.4)
18
+ activesupport (3.0.3)
19
+ bson (1.2.0)
20
+ builder (2.1.2)
21
+ daemons (1.1.0)
22
+ delayed_job (2.1.3)
23
+ activesupport (~> 3.0)
24
+ daemons
25
+ delayed_job_mongoid (1.0.2)
26
+ delayed_job (~> 2.1.1)
27
+ mongoid (~> 2.0.0.rc)
28
+ diff-lcs (1.1.2)
29
+ i18n (0.5.0)
30
+ macaddr (1.0.0)
31
+ mongo (1.2.0)
32
+ bson (>= 1.2.0)
33
+ mongoid (2.0.0.rc.6)
34
+ activemodel (~> 3.0)
35
+ mongo (~> 1.2)
36
+ tzinfo (~> 0.3.22)
37
+ will_paginate (~> 3.0.pre)
38
+ open4 (1.0.1)
39
+ rake (0.8.7)
40
+ relevance-rcov (0.9.2.1)
41
+ robustthread (0.5.2)
42
+ rr (1.0.2)
43
+ rspec (2.0.1)
44
+ rspec-core (~> 2.0.1)
45
+ rspec-expectations (~> 2.0.1)
46
+ rspec-mocks (~> 2.0.1)
47
+ rspec-core (2.0.1)
48
+ rspec-expectations (2.0.1)
49
+ diff-lcs (>= 1.1.2)
50
+ rspec-mocks (2.0.1)
51
+ rspec-core (~> 2.0.1)
52
+ rspec-expectations (~> 2.0.1)
53
+ tzinfo (0.3.24)
54
+ uuid (2.3.1)
55
+ macaddr (~> 1.0)
56
+ vidibus-uuid (0.3.8)
57
+ mongoid (~> 2.0.0.beta.20)
58
+ uuid (~> 2.3.1)
59
+ will_paginate (3.0.pre2)
60
+
61
+ PLATFORMS
62
+ ruby
63
+
64
+ DEPENDENCIES
65
+ bundler (>= 1.0.0)
66
+ delayed_job_mongoid
67
+ mongoid (~> 2.0.0.beta.20)
68
+ open4
69
+ rake
70
+ relevance-rcov
71
+ robustthread
72
+ rr
73
+ rspec (~> 2.0.0.beta.20)
74
+ vidibus-recording!
75
+ vidibus-uuid
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Andre Pankratz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,10 @@
1
+ = Vidibus::Recording
2
+
3
+ Allows recording of RTMP video streams. Uses RTMPdump.
4
+ Further description goes here.
5
+
6
+
7
+
8
+ == Copyright
9
+
10
+ Copyright (c) 2011 Andre Pankratz. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,4 @@
1
+ class Recording
2
+ include Mongoid::Document
3
+ include Vidibus::Recording::Mongoid
4
+ end
@@ -0,0 +1,66 @@
1
+ module Vidibus::Recording::Backend
2
+ class Rtmpdump
3
+
4
+ PROTOCOLS = %[rtmp rtmpt rtmpe rtmpte rtmps rtmpts]
5
+
6
+ attr_accessor :stream, :file, :live, :metadata
7
+
8
+ def initialize(attributes)
9
+ self.stream = attributes[:stream] or raise ConfigurationError.new("No input stream given")
10
+ self.file = attributes[:file] or raise ConfigurationError.new("No output file defined")
11
+ self.live = attributes[:live]
12
+ end
13
+
14
+ # Command for starting the recording.
15
+ # Required options:
16
+ # :stream, :file
17
+ # Optional:
18
+ # :live
19
+ #
20
+ def command(options = {})
21
+ c = %(rtmpdump -r "#{stream}" -o #{file})
22
+ c << " --live" if live
23
+ c
24
+ end
25
+
26
+ # Extract metadata from stdout or stderr.
27
+ # Output delivered by rtmpdump looks like this:
28
+ #
29
+ # RTMPDump v2.2
30
+ # (c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL
31
+ # Connecting ...
32
+ # ERROR: rtmp server sent error
33
+ # Starting Live Stream
34
+ # Metadata:
35
+ # author
36
+ # copyright
37
+ # description
38
+ # keywords
39
+ # rating
40
+ # title
41
+ # presetname Custom
42
+ # creationdate Mon Jan 17 15:22:50 2011
43
+ # videodevice Osprey-210 Video Device 1
44
+ # framerate 25.00
45
+ # width 680.00
46
+ # height 394.00
47
+ # videocodecid avc1
48
+ # videodatarate 650.00
49
+ # avclevel 31.00
50
+ # avcprofile 66.00
51
+ # videokeyframe_frequency5.00
52
+ # audiodevice Osprey-210 Audio Device 1
53
+ # audiosamplerate 22050.00
54
+ # audiochannels 1.00
55
+ # audioinputvolume 75.00
56
+ # audiocodecid .mp3
57
+ # audiodatarate 48.00
58
+ #
59
+ def extract_metadata(std)
60
+ if metadata = std.match(/Metadata\:\n\s+(.+)\Z/m)
61
+ tuples = $1.scan(/([^\n\s\d]+)\s*([^\n]+)\n/m)
62
+ self.metadata = Hash[tuples]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ module Vidibus::Recording
2
+ module Backend
3
+ class ConfigurationError < StandardError; end
4
+ class ProtocolError < ConfigurationError; end
5
+
6
+ BACKENDS = %[rtmpdump]
7
+
8
+ # Returns an instance of a backend processor
9
+ # that is able to record the given stream.
10
+ def self.load(attributes)
11
+ stream = attributes[:stream] or raise ConfigurationError.new("No input stream given")
12
+ protocol = stream.match(/^[^:]+/).to_s
13
+ raise ProtocolError.new(%(No protocol could be derived stream "#{stream}")) if protocol == ""
14
+
15
+ for backend in BACKENDS
16
+ require "recording/backend/#{backend}"
17
+ backend_class = "Vidibus::Recording::Backend::#{backend.classify}".constantize
18
+ if backend_class::PROTOCOLS.include?(protocol)
19
+ return backend_class.new(attributes)
20
+ end
21
+ end
22
+ raise ProtocolError.new(%(No recording backend available for "#{protocol}" protocol.))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Vidibus::Recording
2
+ module Helpers
3
+
4
+ # Recursively fixes classes of value strings
5
+ def fix_value_classes!(value)
6
+ c = value.class
7
+
8
+ # Get nested items in Hash
9
+ if c == Hash
10
+ value.each do |v|
11
+ value[v[0]] = fix_value_classes!(v[1])
12
+ end
13
+
14
+ # Get nested items in Array
15
+ elsif c == Array
16
+ value.each_with_index do |v,i|
17
+ value[i] = fix_value_classes!(v)
18
+ end
19
+
20
+ # Fix classes of values
21
+ else
22
+ if value.match /^\d+[\.,]\d+$/
23
+ value = value.to_f
24
+ elsif value.match /^\d+$/
25
+ value = value.to_i
26
+ elsif value.match /^true$/
27
+ value = true
28
+ elsif value.match /^false$/
29
+ value = false
30
+ end
31
+ end
32
+ value
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,103 @@
1
+ module Vidibus::Recording
2
+ class Job
3
+ include Open4
4
+
5
+ class ProcessError < StandardError; end
6
+
7
+ attr_accessor :recording, :pid
8
+
9
+ def initialize(recording)
10
+ self.recording = recording
11
+ self.pid = recording.pid
12
+ end
13
+
14
+ def start
15
+ start_logger
16
+ self.pid = fork do
17
+ start_thread
18
+ end
19
+ Process.detach(pid)
20
+ pid
21
+ end
22
+
23
+ def stop
24
+ if pid and running?
25
+ Process.kill("SIGTERM", pid)
26
+ sleep 2
27
+ raise ProcessError.new("Recording job is still running!") if running?
28
+ end
29
+ end
30
+
31
+ def running?
32
+ pid and self.class.running?(pid)
33
+ end
34
+
35
+ def self.running?(pid)
36
+ begin
37
+ Process.kill(0, pid)
38
+ return true
39
+ rescue Errno::ESRCH
40
+ return false
41
+ rescue Errno::EPERM
42
+ raise ProcessError.new("No permission to check #{pid}")
43
+ rescue
44
+ raise ProcessError.new("Unable to determine status for #{pid}: #{$!}")
45
+ end
46
+ end
47
+
48
+ protected
49
+
50
+ def start_thread
51
+ stdin = ""
52
+ stdout = ""
53
+ stderr = ""
54
+ last_stderr = ""
55
+ last_stdout = ""
56
+ task = background(recording.backend.command, 0=>stdin, 1=>stdout, 2=>stderr)
57
+
58
+ waiter = Thread.new {y(task.pid => task.exitstatus)} # t.exitstatus is a blocking call!
59
+ timeout = 5
60
+
61
+ while(status = task.status)
62
+ unless stderr == last_stderr
63
+ metadata = extract_metadata(stderr)
64
+ last_stderr = stderr
65
+ end
66
+
67
+ unless stdout == last_stdout
68
+ metadata = extract_metadata(stdout)
69
+ last_stdout = stdout
70
+ end
71
+
72
+ unless metadata
73
+ timeout -= 1
74
+ if timeout == 0
75
+ recording.fail("No Metadata has been received. This stream does not work.")
76
+ return
77
+ end
78
+ end
79
+
80
+ sleep 2
81
+ end
82
+
83
+ waiter.join
84
+ end
85
+
86
+ def extract_metadata(std)
87
+ metadata = recording.backend.extract_metadata(std)
88
+ if metadata
89
+ File.open(recording.yml_file, "w") do |file|
90
+ file.write(metadata.to_yaml)
91
+ end
92
+ end
93
+ metadata
94
+ end
95
+
96
+ def start_logger
97
+ RobustThread.logger = Logger.new(recording.log_file)
98
+ RobustThread.exception_handler do |exception|
99
+ RobustThread.log exception
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,195 @@
1
+ module Vidibus::Recording
2
+ module Mongoid
3
+ extend ActiveSupport::Concern
4
+
5
+ class ProcessError < StandardError; end
6
+ class StreamError < StandardError; end
7
+
8
+ included do
9
+ include Vidibus::Recording::Helpers
10
+ include Vidibus::Uuid::Mongoid
11
+
12
+ field :name
13
+ field :stream
14
+ field :live, :type => Boolean
15
+ field :pid, :type => Integer
16
+ field :info, :type => Hash
17
+ field :size, :type => Integer
18
+ field :duration, :type => Integer
19
+ field :log
20
+ field :error
21
+ field :scheduled_at, :type => DateTime
22
+ field :started_at, :type => DateTime
23
+ field :stopped_at, :type => DateTime
24
+ field :failed_at, :type => DateTime
25
+
26
+ validates :name, :presence => true
27
+ validates :stream, :format => {:with => /^rtmp.*?:\/\/.+$/}
28
+
29
+ before_destroy :cleanup
30
+ end
31
+
32
+ # Starts a recording job now, unless it has been done already.
33
+ # Provide a Time object to schedule start.
34
+ def start(time = :now)
35
+ return false if done? or started?
36
+ if time == :now
37
+ job.start
38
+ update_attributes(:pid => job.pid, :started_at => Time.now)
39
+ job.pid
40
+ else
41
+ schedule(time)
42
+ end
43
+ end
44
+
45
+ # Resets data and stars anew.
46
+ def restart(time = :now)
47
+ stop
48
+ reset
49
+ start(time)
50
+ end
51
+
52
+ # Stops the recording job and starts postprocessing.
53
+ def stop
54
+ return false if !started_at? or done?
55
+ job.stop
56
+ self.pid = nil
57
+ self.stopped_at = Time.now
58
+ postprocess
59
+ end
60
+
61
+ # Receives an error from recording job and stores it.
62
+ # The job gets stopped and postprocessing is started.
63
+ def fail(msg)
64
+ return if done?
65
+ job.stop
66
+ self.pid = nil
67
+ self.error = msg
68
+ self.failed_at = Time.now
69
+ postprocess
70
+ end
71
+
72
+ # Removes all acquired data
73
+ def reset
74
+ remove_files
75
+ blank = {}
76
+ [:started_at, :stopped_at, :failed_at, :info, :log, :error, :size, :duration].map {|a| blank[a] = nil }
77
+ update_attributes(blank)
78
+ end
79
+
80
+ # Returns an instance of the recording job.
81
+ def job
82
+ @job ||= Vidibus::Recording::Job.new(self)
83
+ end
84
+
85
+ # Returns an instance of a fitting recording backend.
86
+ def backend
87
+ @backend ||= Vidibus::Recording::Backend.load(:stream => stream, :file => file, :live => live)
88
+ end
89
+
90
+ # Returns true if recording has either been stopped or failed.
91
+ def done?
92
+ stopped_at or failed?
93
+ end
94
+
95
+ # Returns true if recording has failed.
96
+ def failed?
97
+ !!failed_at
98
+ end
99
+
100
+ # Returns true if if job has been started.
101
+ def started?
102
+ !!started_at
103
+ end
104
+
105
+ # Returns true if recording job is still running.
106
+ def running?
107
+ started? and job.running?
108
+ end
109
+
110
+ # Return folder to store recordings in.
111
+ def folder
112
+ @folder ||= begin
113
+ f = ["recordings"]
114
+ f.unshift(Rails.root) if defined?(Rails)
115
+ path = File.join(f)
116
+ FileUtils.mkdir_p(path) unless File.exist?(path)
117
+ path
118
+ end
119
+ end
120
+
121
+ # Returns the file name of this recording.
122
+ def file
123
+ @file ||= "#{folder}/#{uuid}.rec"
124
+ end
125
+
126
+ # Returns the log file name for this recording.
127
+ def log_file
128
+ @log_file ||= file.gsub(/\.[^\.]+$/, ".log")
129
+ end
130
+
131
+ # Returns the YAML file name for this recording.
132
+ def yml_file
133
+ @info_file ||= file.gsub(/\.[^\.]+$/, ".yml")
134
+ end
135
+
136
+ protected
137
+
138
+ def schedule(time)
139
+ self.delay(:run_at => time).start
140
+ end
141
+
142
+ def postprocess
143
+ process_log_file
144
+ process_yml_file
145
+ set_size
146
+ set_duration
147
+ save!
148
+ end
149
+
150
+ def process_log_file
151
+ if str = read_file(log_file)
152
+ str.gsub!(/\A[^\n]+\n/, "") # remove first line
153
+ unless str == ""
154
+ self.log = str
155
+ end
156
+ end
157
+ end
158
+
159
+ def process_yml_file
160
+ if str = read_file(yml_file)
161
+ if values = YAML::load(str)
162
+ fix_value_classes!(values)
163
+ self.info = values
164
+ end
165
+ end
166
+ end
167
+
168
+ def set_size
169
+ self.size = File.exists?(file) ? File.size(file) : nil
170
+ end
171
+
172
+ def set_duration
173
+ self.duration = failed? ? 0 : Time.now - started_at
174
+ end
175
+
176
+ def read_file(file)
177
+ if File.exists?(file)
178
+ str = File.read(file)
179
+ File.delete(file)
180
+ str
181
+ end
182
+ end
183
+
184
+ def cleanup
185
+ job.stop
186
+ remove_files
187
+ end
188
+
189
+ def remove_files
190
+ [file, log_file, yml_file].each do |f|
191
+ File.delete(f) if File.exists?(f)
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,5 @@
1
+ module Vidibus
2
+ module Recording
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require "recording/job"
2
+ require "recording/backend"
3
+ require "recording/helpers"
4
+ require "recording/mongoid"
@@ -0,0 +1,17 @@
1
+ require "open4"
2
+ require "yaml"
3
+ require "robustthread"
4
+ require "delayed_job_mongoid"
5
+ require "active_support/core_ext"
6
+ require "vidibus-uuid"
7
+
8
+ module Vidibus
9
+ module Recording
10
+ if defined?(Rails)
11
+ class Engine < ::Rails::Engine; end
12
+ end
13
+ end
14
+ end
15
+
16
+ $:.unshift(File.join(File.dirname(__FILE__), "vidibus"))
17
+ require "recording"
@@ -0,0 +1,38 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require "rubygems"
5
+ require "rspec"
6
+ require "rr"
7
+ require "mongoid"
8
+
9
+ require "vidibus-recording"
10
+ require "app/models/recording"
11
+
12
+ Mongoid.configure do |config|
13
+ name = "vidibus-recording_test"
14
+ host = "localhost"
15
+ config.master = Mongo::Connection.new.db(name)
16
+ config.logger = nil
17
+ end
18
+
19
+ RSpec.configure do |config|
20
+ config.mock_with :rr
21
+ config.before(:each) do
22
+ Mongoid.master.collections.select {|c| c.name !~ /system/}.each(&:drop)
23
+ end
24
+ end
25
+
26
+ # Helper for stubbing time. Define String to be set as Time.now.
27
+ # Usage:
28
+ # stub_time('01.01.2010 14:00')
29
+ # stub_time(2.days.ago)
30
+ #
31
+ def stub_time(string = nil)
32
+ string ||= Time.now.to_s(:db)
33
+ now = Time.parse(string.to_s)
34
+ stub(Time).now { now }
35
+ now
36
+ end
37
+
38
+ #I18n.load_path += Dir[File.join('config', 'locales', '**', '*.{rb,yml}')]
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe "Vidibus::Recording::Backend" do
4
+
5
+ describe ".load" do
6
+ it "should return an error unless a stream attribute is given" do
7
+ expect {
8
+ Vidibus::Recording::Backend.load({})
9
+ }.to raise_error(Vidibus::Recording::Backend::ConfigurationError)
10
+ end
11
+
12
+ it "should return a backend instance for given stream protocol" do
13
+ Vidibus::Recording::Backend.load(:stream => "rtmp://something", :file => "test").
14
+ should be_an_instance_of(Vidibus::Recording::Backend::Rtmpdump)
15
+ end
16
+
17
+ it "should return an error unless a stream attribute with consumable protocol is given" do
18
+ expect {
19
+ Vidibus::Recording::Backend.load({:stream => "mms://something", :file => "test"})
20
+ }.to raise_error(Vidibus::Recording::Backend::ProtocolError)
21
+ end
22
+
23
+ it "should return an error unless a stream attribute with a protocol is given" do
24
+ expect {
25
+ Vidibus::Recording::Backend.load({:stream => "something", :file => "test"})
26
+ }.to raise_error(Vidibus::Recording::Backend::ProtocolError)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,111 @@
1
+ require "spec_helper"
2
+
3
+ describe "Vidibus::Recording::Mongoid" do
4
+
5
+ let(:this) do
6
+ Recording.new(:name => "N-TV Live", :stream => "rtmp://fms.rtl.de/ntvlive/livestream/channel1", :live => true)
7
+ end
8
+
9
+ def cleanup(recording)
10
+ Process.kill("SIGTERM", recording.pid) if recording.pid
11
+ delete_safely(recording.file)
12
+ delete_safely(recording.log_file)
13
+ delete_safely(recording.yml_file)
14
+ end
15
+
16
+ def delete_safely(file)
17
+ return unless file.match(/.{32}\..{3}/)
18
+ File.delete(file) if File.exists?(file)
19
+ end
20
+
21
+ describe "validation" do
22
+ it "should pass with valid attributes" do
23
+ this.should be_valid
24
+ end
25
+
26
+ it "should fail without a stream" do
27
+ this.stream = nil
28
+ this.should be_invalid
29
+ end
30
+
31
+ it "should fail without a valid stream address" do
32
+ this.stream = "something"
33
+ this.should be_invalid
34
+ end
35
+
36
+ it "should fail without a valid rtmp stream address" do
37
+ this.stream = "rtmp://something"
38
+ this.should be_valid
39
+ end
40
+
41
+ it "should fail without a name" do
42
+ this.name = nil
43
+ this.should be_invalid
44
+ end
45
+ end
46
+
47
+ describe "job" do
48
+ it "should return a job instance" do
49
+ this.job.should be_an_instance_of(Vidibus::Recording::Job)
50
+ end
51
+ end
52
+
53
+ describe "backend" do
54
+ it "should return a backend instance for given stream protocol" do
55
+ this.backend.should be_an_instance_of(Vidibus::Recording::Backend::Rtmpdump)
56
+ end
57
+ end
58
+
59
+ describe "start" do
60
+ before {this.save}
61
+
62
+ it "should return a process id" do
63
+ this.start.should be_a(Fixnum)
64
+ end
65
+
66
+ context "without params" do
67
+ it "should start a recording job now" do
68
+ pid = this.start
69
+ Vidibus::Recording::Job.running?(pid).should be_true
70
+ end
71
+ end
72
+
73
+ context "with a given Time" do
74
+ it "should schedule a recording job" do
75
+ stub_time("2011-01-12 00:00")
76
+ run_at = 10.minutes.since
77
+ this.start(run_at)
78
+ Delayed::Backend::Mongoid::Job.count.should eql(1)
79
+ Delayed::Backend::Mongoid::Job.first.run_at.should eql(run_at)
80
+ end
81
+ end
82
+
83
+ after {cleanup(this)}
84
+ end
85
+
86
+ describe "stop" do
87
+ before {this.save}
88
+
89
+ it "should return false unless recording has been started" do
90
+ this.stop.should be_false
91
+ end
92
+
93
+ it "should return false if recording is done" do
94
+ this.stopped_at = Time.now
95
+ this.stop.should be_false
96
+ end
97
+
98
+ context "with started job" do
99
+ before {this.start}
100
+
101
+ it "should stop the recording job" do
102
+ pid = this.pid
103
+ this.stop
104
+ sleep 1
105
+ Vidibus::Recording::Job.running?(pid).should be_false
106
+ end
107
+
108
+ after {cleanup(this)}
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/vidibus/recording/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "vidibus-recording"
6
+ s.rubyforge_project = "vidibus-recording"
7
+ s.version = Vidibus::Recording::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = "Andre Pankratz"
10
+ s.email = "andre@vidibus.com"
11
+ s.homepage = "http://rubygems.org/gems/vidibus-rtmpdump"
12
+ s.summary = "Video stream recording tools"
13
+ s.description = "Allows recording of RTMP video streams. Uses RTMPdump."
14
+
15
+ s.required_rubygems_version = ">= 1.3.6"
16
+
17
+ s.add_dependency "mongoid", "~> 2.0.0.beta.20"
18
+ s.add_dependency "open4"
19
+ s.add_dependency "robustthread"
20
+ s.add_dependency "delayed_job_mongoid"
21
+
22
+ s.add_dependency "vidibus-uuid"
23
+
24
+ s.add_development_dependency "bundler", ">= 1.0.0"
25
+ s.add_development_dependency "rake"
26
+ s.add_development_dependency "rspec", "~> 2.0.0.beta.20"
27
+ s.add_development_dependency "rr"
28
+ s.add_development_dependency "relevance-rcov"
29
+
30
+ s.files = `git ls-files`.split("\n")
31
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
32
+ s.require_path = 'lib'
33
+ end
metadata ADDED
@@ -0,0 +1,236 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vidibus-recording
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Andre Pankratz
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-23 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: mongoid
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 62196427
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 0
34
+ - beta
35
+ - 20
36
+ version: 2.0.0.beta.20
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: open4
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: robustthread
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ type: :runtime
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: delayed_job_mongoid
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :runtime
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: vidibus-uuid
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ type: :runtime
94
+ version_requirements: *id005
95
+ - !ruby/object:Gem::Dependency
96
+ name: bundler
97
+ prerelease: false
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 23
104
+ segments:
105
+ - 1
106
+ - 0
107
+ - 0
108
+ version: 1.0.0
109
+ type: :development
110
+ version_requirements: *id006
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ prerelease: false
114
+ requirement: &id007 !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ type: :development
124
+ version_requirements: *id007
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ prerelease: false
128
+ requirement: &id008 !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ hash: 62196427
134
+ segments:
135
+ - 2
136
+ - 0
137
+ - 0
138
+ - beta
139
+ - 20
140
+ version: 2.0.0.beta.20
141
+ type: :development
142
+ version_requirements: *id008
143
+ - !ruby/object:Gem::Dependency
144
+ name: rr
145
+ prerelease: false
146
+ requirement: &id009 !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ hash: 3
152
+ segments:
153
+ - 0
154
+ version: "0"
155
+ type: :development
156
+ version_requirements: *id009
157
+ - !ruby/object:Gem::Dependency
158
+ name: relevance-rcov
159
+ prerelease: false
160
+ requirement: &id010 !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ hash: 3
166
+ segments:
167
+ - 0
168
+ version: "0"
169
+ type: :development
170
+ version_requirements: *id010
171
+ description: Allows recording of RTMP video streams. Uses RTMPdump.
172
+ email: andre@vidibus.com
173
+ executables: []
174
+
175
+ extensions: []
176
+
177
+ extra_rdoc_files: []
178
+
179
+ files:
180
+ - .gitignore
181
+ - Gemfile
182
+ - Gemfile.lock
183
+ - LICENSE
184
+ - README.rdoc
185
+ - Rakefile
186
+ - app/models/recording.rb
187
+ - lib/vidibus-recording.rb
188
+ - lib/vidibus/recording.rb
189
+ - lib/vidibus/recording/backend.rb
190
+ - lib/vidibus/recording/backend/rtmpdump.rb
191
+ - lib/vidibus/recording/helpers.rb
192
+ - lib/vidibus/recording/job.rb
193
+ - lib/vidibus/recording/mongoid.rb
194
+ - lib/vidibus/recording/version.rb
195
+ - spec/spec_helper.rb
196
+ - spec/vidibus/recording/backend_spec.rb
197
+ - spec/vidibus/recording/mongoid_spec.rb
198
+ - vidibus-recording.gemspec
199
+ has_rdoc: true
200
+ homepage: http://rubygems.org/gems/vidibus-rtmpdump
201
+ licenses: []
202
+
203
+ post_install_message:
204
+ rdoc_options: []
205
+
206
+ require_paths:
207
+ - lib
208
+ required_ruby_version: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ hash: 3
214
+ segments:
215
+ - 0
216
+ version: "0"
217
+ required_rubygems_version: !ruby/object:Gem::Requirement
218
+ none: false
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ hash: 23
223
+ segments:
224
+ - 1
225
+ - 3
226
+ - 6
227
+ version: 1.3.6
228
+ requirements: []
229
+
230
+ rubyforge_project: vidibus-recording
231
+ rubygems_version: 1.3.7
232
+ signing_key:
233
+ specification_version: 3
234
+ summary: Video stream recording tools
235
+ test_files: []
236
+