rtp 0.1.3 → 0.1.4
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.
- data/History.rdoc +7 -0
- data/lib/rtp/receiver.rb +2 -2
- data/lib/rtp/sender.rb +37 -0
- data/lib/rtp/senders/socat.rb +283 -0
- data/lib/rtp/version.rb +1 -1
- data/spec/rtp/coverage/index.html +1 -1
- data/spec/rtp/receiver_spec.rb +90 -30
- metadata +4 -2
data/History.rdoc
CHANGED
data/lib/rtp/receiver.rb
CHANGED
@@ -184,7 +184,7 @@ module RTP
|
|
184
184
|
# @yield [Time] The timestamp from the packet as it was received on the
|
185
185
|
# socket.
|
186
186
|
# @return [Thread] The packet writer thread.
|
187
|
-
def start_packet_writer
|
187
|
+
def start_packet_writer(&block)
|
188
188
|
return @packet_writer if @packet_writer
|
189
189
|
|
190
190
|
# If a block is given for packet inspection, perhaps we should save
|
@@ -196,7 +196,7 @@ module RTP
|
|
196
196
|
|
197
197
|
data_to_write = @strip_headers ? packet.rtp_payload : packet
|
198
198
|
|
199
|
-
if
|
199
|
+
if block
|
200
200
|
yield data_to_write, timestamp
|
201
201
|
else
|
202
202
|
@capture_file.write(data_to_write)
|
data/lib/rtp/sender.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'senders/socat'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
|
5
|
+
module RTP
|
6
|
+
class Sender
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@stream_module = RTP::Senders::Socat
|
11
|
+
@sessions = {}
|
12
|
+
@pids = {}
|
13
|
+
@rtcp_threads = {}
|
14
|
+
@rtp_timestamp = 2612015746
|
15
|
+
@rtp_sequence = 21934
|
16
|
+
@rtp_map = []
|
17
|
+
@fmtp = []
|
18
|
+
@source_ip = []
|
19
|
+
@source_port = []
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sets the stream module to be used by the stream server.
|
23
|
+
#
|
24
|
+
# @param [Module] module_name Module name.
|
25
|
+
def stream_module=(module_name)
|
26
|
+
@stream_module = module_name
|
27
|
+
self.class.send(:include, module_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gets the current stream_module.
|
31
|
+
#
|
32
|
+
# @return [Module] Module name.
|
33
|
+
def stream_module
|
34
|
+
@stream_module
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'os'
|
2
|
+
require 'ipaddr'
|
3
|
+
require_relative '../packet'
|
4
|
+
require 'sys/proctable'
|
5
|
+
|
6
|
+
|
7
|
+
module RTP
|
8
|
+
module Senders
|
9
|
+
module Socat
|
10
|
+
RTCP_SOURCE = ["80c80006072dee6ad42c300f76c3b928377e99e5006c461ba92d8a3" +
|
11
|
+
"081ca0006072dee6a010e49583330444e2d41414a4248513600000000"]
|
12
|
+
MP4_RTP_MAP = "96 MP4V-ES/30000"
|
13
|
+
MP4_FMTP = "96 profile-level-id=5;config=000001b005000001b50900000100000" +
|
14
|
+
"0012000c888ba9860fa22c087828307"
|
15
|
+
H264_RTP_MAP = "96 H264/90000"
|
16
|
+
H264_FMTP = "96 packetization-mode=1;profile-level-id=428032;" +
|
17
|
+
"sprop-parameter-sets=Z0KAMtoAgAMEwAQAAjKAAAr8gYAAAYhMAABMS0IvfjAA" +
|
18
|
+
"ADEJgAAJiWhF78CA,aM48gA=="
|
19
|
+
SOCAT_OPTIONS = "rcvbuf=2500000,sndbuf=2500000,sndtimeo=0.00001,rcvtimeo=0.00001"
|
20
|
+
BLOCK_SIZE = 2000
|
21
|
+
BSD_OPTIONS = "setsockopt-int=0xffff:0x200:0x01"
|
22
|
+
|
23
|
+
# @return [Hash] Hash of session IDs and SOCAT commands.
|
24
|
+
attr_accessor :sessions
|
25
|
+
|
26
|
+
# @return [Hash] Hash of session IDs and pids.
|
27
|
+
attr_reader :pids
|
28
|
+
|
29
|
+
# @return [Hash] Hash of session IDs and RTCP threads.
|
30
|
+
attr_reader :rtcp_threads
|
31
|
+
|
32
|
+
# @return [Array<String>] IP address of the source camera.
|
33
|
+
attr_accessor :source_ip
|
34
|
+
|
35
|
+
# @return [Array<Fixnum>] Port where the source camera is streaming.
|
36
|
+
attr_accessor :source_port
|
37
|
+
|
38
|
+
# @return [String] IP address of the interface of the RTSP streamer.
|
39
|
+
attr_accessor :interface_ip
|
40
|
+
|
41
|
+
# @return [Fixnum] RTP timestamp of the source stream.
|
42
|
+
attr_accessor :rtp_timestamp
|
43
|
+
|
44
|
+
# @return [Fixnum] RTP sequence number of the source stream.
|
45
|
+
attr_accessor :rtp_sequence
|
46
|
+
|
47
|
+
# @return [String] RTCP source identifier.
|
48
|
+
attr_accessor :rtcp_source_identifier
|
49
|
+
|
50
|
+
# @return [Array<String>] Media type attributes.
|
51
|
+
attr_accessor :rtp_map
|
52
|
+
|
53
|
+
# @return [Array<String>] Media format attributes.
|
54
|
+
attr_accessor :fmtp
|
55
|
+
|
56
|
+
# Generates a RTCP source ID based on the friendly name.
|
57
|
+
# This ID is used in the RTCP communication with the client.
|
58
|
+
# The default +RTCP_SOURCE+ will be used if one is not provided.
|
59
|
+
#
|
60
|
+
# @param [String] friendly_name Name to be used in the RTCP source ID.
|
61
|
+
# @return [String] rtcp_source_id RTCP Source ID.
|
62
|
+
#def generate_rtcp_source_id friendly_name
|
63
|
+
# ["80c80006072dee6ad42c300f76c3b928377e99e5006c461ba92d8a3081ca0006072dee6a010e" +
|
64
|
+
# friendly_name.unpack("H*").first + "00000000"].pack("H*")
|
65
|
+
#end
|
66
|
+
|
67
|
+
# Creates a RTP streamer using socat.
|
68
|
+
#
|
69
|
+
# @param [String] sid Session ID.
|
70
|
+
# @param [String] transport_url Destination IP:port.
|
71
|
+
# @param [Fixnum] index Stream index.
|
72
|
+
# @return [Fixnum] The port the streamer will stream on.
|
73
|
+
def setup_streamer(sid, transport_url, index=1)
|
74
|
+
dest_ip, dest_port = transport_url.split ":"
|
75
|
+
@rtcp_source_identifier ||= RTCP_SOURCE.pack("H*")
|
76
|
+
|
77
|
+
@rtcp_threads[sid] = Thread.start do
|
78
|
+
s = UDPSocket.new
|
79
|
+
s.bind(@interface_ip, 0)
|
80
|
+
|
81
|
+
loop do
|
82
|
+
begin
|
83
|
+
_, sender = s.recvfrom(36)
|
84
|
+
s.send(@rtcp_source_identifier, 0, sender[3], sender[1])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
@cleaner ||= Thread.start { cleanup_defunct }
|
90
|
+
@processes ||= Sys::ProcTable.ps.map { |p| p.cmdline }
|
91
|
+
@sessions[sid] = build_socat(dest_ip, dest_port, local_port, index)
|
92
|
+
|
93
|
+
local_port
|
94
|
+
end
|
95
|
+
|
96
|
+
# Start streaming for the requested session.
|
97
|
+
#
|
98
|
+
# @param [String] sid Session ID.
|
99
|
+
def start_streaming sid
|
100
|
+
spawn_socat(sid, @sessions[sid])
|
101
|
+
end
|
102
|
+
|
103
|
+
# Stop streaming for the requested session.
|
104
|
+
#
|
105
|
+
# @param [String] session ID.
|
106
|
+
def stop_streaming sid
|
107
|
+
if sid.nil?
|
108
|
+
disconnect_all_streams
|
109
|
+
else
|
110
|
+
disconnect sid
|
111
|
+
@rtcp_threads[sid].kill unless rtcp_threads[sid].nil?
|
112
|
+
@rtcp_threads.delete sid
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the default stream description.
|
117
|
+
#
|
118
|
+
# @param[Boolean] multicast True if the description is for a multicast stream.
|
119
|
+
# @param [Fixnum] stream_index Index of the stream type.
|
120
|
+
=begin
|
121
|
+
def description(multicast=false, stream_index=1)
|
122
|
+
rtp_map = @rtp_map[stream_index - 1] || H264_RTP_MAP
|
123
|
+
fmtp = @fmtp[stream_index - 1] || H264_FMTP
|
124
|
+
|
125
|
+
<<EOF
|
126
|
+
v=0\r
|
127
|
+
o=- 1345481255966282 1 IN IP4 #{@interface_ip}\r
|
128
|
+
s=Session streamed by "Streaming Server"\r
|
129
|
+
i=stream1\r
|
130
|
+
t=0 0\r
|
131
|
+
a=tool:LIVE555 Streaming Media v2007.07.09\r
|
132
|
+
a=type:broadcast\r
|
133
|
+
a=control:*\r
|
134
|
+
a=range:npt=0-\r
|
135
|
+
a=x-qt-text-nam:Session streamed by "Streaming Server"\r
|
136
|
+
a=x-qt-text-inf:stream1\r
|
137
|
+
m=video 0 RTP/AVP 96\r
|
138
|
+
c=IN IP4 #{multicast ? "#{multicast_ip(stream_index)}/10" : "0.0.0.0"}\r
|
139
|
+
a=rtpmap:#{rtp_map}\r
|
140
|
+
a=fmtp:#{fmtp}\r
|
141
|
+
a=control:track1\r
|
142
|
+
EOF
|
143
|
+
end
|
144
|
+
=end
|
145
|
+
|
146
|
+
# Disconnects the stream matching the session ID.
|
147
|
+
#
|
148
|
+
# @param [String] sid Session ID.
|
149
|
+
def disconnect sid
|
150
|
+
pid = @pids[sid].to_i
|
151
|
+
@pids.delete(sid)
|
152
|
+
@sessions.delete(sid)
|
153
|
+
Process.kill(9, pid) if pid > 1000
|
154
|
+
rescue Errno::ESRCH
|
155
|
+
log "Tried to kill dead process: #{pid}"
|
156
|
+
end
|
157
|
+
|
158
|
+
# Parses the headers from an RTP stream.
|
159
|
+
#
|
160
|
+
# @param [String] src_ip Multicast IP address of RTP stream.
|
161
|
+
# @param [Fixnum] src_port Port of RTP stream.
|
162
|
+
# @return [Array<Fixnum>] Sequence number and timestamp
|
163
|
+
def parse_sequence_number(src_ip, src_port)
|
164
|
+
sock = UDPSocket.new
|
165
|
+
ip = IPAddr.new(src_ip).hton + IPAddr.new("0.0.0.0").hton
|
166
|
+
sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ip)
|
167
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
168
|
+
sock.bind(Socket::INADDR_ANY, src_port)
|
169
|
+
|
170
|
+
begin
|
171
|
+
data = sock.recv_nonblock(1500)
|
172
|
+
rescue Errno::EAGAIN
|
173
|
+
retry
|
174
|
+
end
|
175
|
+
|
176
|
+
sock.close
|
177
|
+
packet = RTP::Packet.read(data)
|
178
|
+
|
179
|
+
[packet["sequence_number"], packet["timestamp"]]
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
# Returns the multicast IP on which the streamer will stream.
|
185
|
+
#
|
186
|
+
# @param [Fixnum] index Stream index.
|
187
|
+
# @return [String] Multicast IP.
|
188
|
+
def multicast_ip index=1
|
189
|
+
@interface_ip ||= find_best_interface_ipaddr @source_ip[index-1]
|
190
|
+
multicast_ip = @interface_ip.split "."
|
191
|
+
multicast_ip[0] = "239"
|
192
|
+
|
193
|
+
multicast_ip.join "."
|
194
|
+
end
|
195
|
+
|
196
|
+
# Cleans up defunct child processes
|
197
|
+
def cleanup_defunct
|
198
|
+
loop do
|
199
|
+
begin
|
200
|
+
Process.wait 0
|
201
|
+
rescue Errno::ECHILD
|
202
|
+
sleep 10
|
203
|
+
retry
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Determine the interface address that best matches an IP address. This
|
209
|
+
# is most useful when talking to a remote computer and needing to
|
210
|
+
# determine the interface that is being used for the connection.
|
211
|
+
#
|
212
|
+
# @param [String] device_ip IP address of the remote device you want to
|
213
|
+
# talk to.
|
214
|
+
# @return [String] IP of the interface that would be used to talk to.
|
215
|
+
def find_best_interface_ipaddr device_ip
|
216
|
+
UDPSocket.open { |s| s.connect(device_ip, 1); s.addr.last }
|
217
|
+
end
|
218
|
+
|
219
|
+
# Disconnects all streams that are currently streaming.
|
220
|
+
def disconnect_all_streams
|
221
|
+
@pids.values.each { |pid| Process.kill(9, pid.to_i) if pid.to_i > 1000 }
|
222
|
+
@sessions.clear
|
223
|
+
@pids.clear
|
224
|
+
end
|
225
|
+
|
226
|
+
# Spawns an instance of Socat.
|
227
|
+
#
|
228
|
+
# @param [String] sid The session ID of the stream.
|
229
|
+
# @param [String] command The SOCAT command to be spawned.
|
230
|
+
def spawn_socat(sid, command)
|
231
|
+
@processes ||= Sys::ProcTable.ps.map { |p| p.cmdline }
|
232
|
+
|
233
|
+
if command.nil?
|
234
|
+
log("SOCAT command for #{sid} was nil", :warn)
|
235
|
+
return
|
236
|
+
end
|
237
|
+
|
238
|
+
if @processes.include?(command)
|
239
|
+
pid = get_pid(command)
|
240
|
+
log "Streamer already running with pid #{pid}" if pid.is_a? Fixnum
|
241
|
+
else
|
242
|
+
@sessions[sid] = command
|
243
|
+
|
244
|
+
Thread.start do
|
245
|
+
log "Running stream spawner: #{command}"
|
246
|
+
@processes << command
|
247
|
+
pid = spawn command
|
248
|
+
@pids[sid] = pid
|
249
|
+
Thread.start { sleep 20; spawn_socat(sid, command) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Builds a socat stream command based on the source and target
|
255
|
+
# IP and ports of the RTP stream.
|
256
|
+
#
|
257
|
+
# @param [String] target_ip IP address of the remote device you want to
|
258
|
+
# talk to.
|
259
|
+
# @param [Fixnum] target_port Port on the remote device you want to
|
260
|
+
# talk to.
|
261
|
+
# @return [String] IP of the interface that would be used to talk to.
|
262
|
+
def build_socat(target_ip, target_port, server_port, index=1)
|
263
|
+
bsd_options = BSD_OPTIONS if OS.mac?
|
264
|
+
bsd_options ||= ""
|
265
|
+
|
266
|
+
"socat -b #{BLOCK_SIZE} UDP-RECV:#{@source_port[index-1]},reuseaddr," +
|
267
|
+
"#{bsd_options}"+ SOCAT_OPTIONS + ",ip-add-membership=#{@source_ip[index-1]}:" +
|
268
|
+
"#{@interface_ip} UDP:#{target_ip}:#{target_port},sourceport=#{server_port}," +
|
269
|
+
SOCAT_OPTIONS
|
270
|
+
end
|
271
|
+
|
272
|
+
# Gets the pid for a SOCAT command.
|
273
|
+
#
|
274
|
+
# @param [String] cmd SOCAT command
|
275
|
+
# @return [Fixnum] PID of the process.
|
276
|
+
def get_pid cmd
|
277
|
+
Sys::ProcTable.ps.each do |p|
|
278
|
+
return p.pid.to_i if p.cmdline.include? cmd
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
data/lib/rtp/version.rb
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<img src="./assets/0.7.1/loading.gif" alt="loading"/>
|
15
15
|
</div>
|
16
16
|
<div id="wrapper" style="display:none;">
|
17
|
-
<div class="timestamp">Generated <abbr class="timeago" title="2012-
|
17
|
+
<div class="timestamp">Generated <abbr class="timeago" title="2012-12-06T10:53:51-08:00">2012-12-06T10:53:51-08:00</abbr></div>
|
18
18
|
<ul class="group_tabs"></ul>
|
19
19
|
|
20
20
|
<div id="content">
|
data/spec/rtp/receiver_spec.rb
CHANGED
@@ -150,16 +150,21 @@ describe RTP::Receiver do
|
|
150
150
|
describe "#start_packet_writer" do
|
151
151
|
context "packet writer running" do
|
152
152
|
let(:packet) { double "RTP::Packet" }
|
153
|
+
let(:msg) { "the data" }
|
154
|
+
let(:timestamp) { "12345" }
|
153
155
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
156
|
+
let(:packets) do
|
157
|
+
p = double("Queue")
|
158
|
+
p.should_receive(:pop).and_return [msg, timestamp]
|
159
|
+
|
160
|
+
p
|
159
161
|
end
|
160
162
|
|
161
|
-
|
162
|
-
Thread.
|
163
|
+
before do
|
164
|
+
Thread.should_receive(:start).and_yield
|
165
|
+
subject.should_receive(:loop).and_yield
|
166
|
+
RTP::Packet.should_receive(:read).with(msg).and_return packet
|
167
|
+
subject.instance_variable_set(:@packets, packets)
|
163
168
|
end
|
164
169
|
|
165
170
|
context "@strip_headers is false" do
|
@@ -183,6 +188,40 @@ describe RTP::Receiver do
|
|
183
188
|
subject.send(:start_packet_writer)
|
184
189
|
end
|
185
190
|
end
|
191
|
+
|
192
|
+
context "block is given" do
|
193
|
+
it "yields the data and its timestamp" do
|
194
|
+
expect { |block|
|
195
|
+
subject.send(:start_packet_writer, &block)
|
196
|
+
}.to yield_with_args packet, timestamp
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "no block given" do
|
201
|
+
let(:capture_file) do
|
202
|
+
c = double "@capture_file"
|
203
|
+
c.stub(:closed?)
|
204
|
+
|
205
|
+
c
|
206
|
+
end
|
207
|
+
|
208
|
+
before { RTP::Receiver.any_instance.instance_variable_set(:@capture_file, capture_file) }
|
209
|
+
|
210
|
+
it "writes to the capture file" do
|
211
|
+
subject.instance_variable_get(:@capture_file).should_receive(:write).
|
212
|
+
with(packet)
|
213
|
+
|
214
|
+
subject.send(:start_packet_writer)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "adds timestamps to @timestamps" do
|
218
|
+
subject.instance_variable_get(:@capture_file).stub(:write)
|
219
|
+
subject.instance_variable_get(:@packet_timestamps).
|
220
|
+
should_receive(:<<).with(timestamp)
|
221
|
+
|
222
|
+
subject.send(:start_packet_writer)
|
223
|
+
end
|
224
|
+
end
|
186
225
|
end
|
187
226
|
|
188
227
|
context "packet writer not running" do
|
@@ -194,6 +233,7 @@ describe RTP::Receiver do
|
|
194
233
|
|
195
234
|
specify { subject.send(:start_packet_writer).should == packet_writer }
|
196
235
|
end
|
236
|
+
|
197
237
|
end
|
198
238
|
|
199
239
|
describe "#init_socket" do
|
@@ -275,26 +315,10 @@ describe RTP::Receiver do
|
|
275
315
|
l
|
276
316
|
end
|
277
317
|
|
278
|
-
let(:data)
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
d
|
283
|
-
end
|
284
|
-
|
285
|
-
let(:timestamp) { double "timestamp" }
|
286
|
-
|
287
|
-
let(:message) do
|
288
|
-
m = double "msg"
|
289
|
-
m.stub(:first).and_return data
|
290
|
-
m.stub_chain(:last, :timestamp).and_return timestamp
|
291
|
-
|
292
|
-
m
|
293
|
-
end
|
294
|
-
|
295
|
-
let(:socket) do
|
296
|
-
double "Socket", recvmsg: message
|
297
|
-
end
|
318
|
+
let(:data) { double "socket data", size: 10 }
|
319
|
+
let(:socket_info) { double "socket info", timestamp: '12345' }
|
320
|
+
let(:message) { [data, socket_info] }
|
321
|
+
let(:socket) { double "Socket", recvmsg: message }
|
298
322
|
|
299
323
|
it "starts a new Thread and returns that" do
|
300
324
|
Thread.should_receive(:start).with(socket).and_return listener
|
@@ -312,8 +336,13 @@ describe RTP::Receiver do
|
|
312
336
|
Thread.unstub(:start)
|
313
337
|
end
|
314
338
|
|
315
|
-
it "
|
316
|
-
|
339
|
+
it "adds the socket data and timestamp to @packets" do
|
340
|
+
Thread.stub(:start).and_yield
|
341
|
+
subject.stub(:loop).and_yield
|
342
|
+
|
343
|
+
subject.instance_variable_get(:@packets).should_receive(:<<).
|
344
|
+
with [data, '12345']
|
345
|
+
subject.send(:start_listener, socket)
|
317
346
|
end
|
318
347
|
end
|
319
348
|
|
@@ -346,6 +375,37 @@ describe RTP::Receiver do
|
|
346
375
|
end
|
347
376
|
|
348
377
|
describe "#stop_packet_writer" do
|
349
|
-
|
378
|
+
let(:packet_writer) { double "@packet_writer" }
|
379
|
+
|
380
|
+
it "closes the @capture_file" do
|
381
|
+
subject.instance_variable_get(:@capture_file).should_receive(:close)
|
382
|
+
subject.send(:stop_packet_writer)
|
383
|
+
end
|
384
|
+
|
385
|
+
context "writing packets" do
|
386
|
+
before do
|
387
|
+
subject.should_receive(:writing_packets?).and_return true
|
388
|
+
subject.should_receive(:writing_packets?).and_return false
|
389
|
+
end
|
390
|
+
|
391
|
+
it "kills the @packet_writer and sets it to nil" do
|
392
|
+
subject.instance_variable_get(:@packet_writer).should_receive(:kill)
|
393
|
+
subject.send(:stop_packet_writer)
|
394
|
+
subject.instance_variable_get(:@packet_writer).should be_nil
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context "not writing packets" do
|
399
|
+
before do
|
400
|
+
subject.should_receive(:writing_packets?).and_return false
|
401
|
+
subject.should_receive(:writing_packets?).and_return false
|
402
|
+
end
|
403
|
+
|
404
|
+
it "sets @packet_writer it to nil" do
|
405
|
+
subject.instance_variable_get(:@packet_writer).should_not_receive(:kill)
|
406
|
+
subject.send(:stop_packet_writer)
|
407
|
+
subject.instance_variable_get(:@packet_writer).should be_nil
|
408
|
+
end
|
409
|
+
end
|
350
410
|
end
|
351
411
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rtp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-12-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bindata
|
@@ -152,6 +152,8 @@ files:
|
|
152
152
|
- lib/rtp/logger.rb
|
153
153
|
- lib/rtp/packet.rb
|
154
154
|
- lib/rtp/receiver.rb
|
155
|
+
- lib/rtp/sender.rb
|
156
|
+
- lib/rtp/senders/socat.rb
|
155
157
|
- lib/rtp/version.rb
|
156
158
|
- lib/rtp.rb
|
157
159
|
- spec/rtp/coverage/assets/0.7.1/application.css
|