Module: RTSP::SocatStreaming

Includes:
Global
Included in:
StreamServer
Defined in:
lib/rtsp/socat_streaming.rb

Constant Summary collapse

RTCP_SOURCE =
["80c80006072dee6ad42c300f76c3b928377e99e5006c461ba92d8a3" +
"081ca0006072dee6a010e49583330444e2d41414a4248513600000000"]
MP4_RTP_MAP =
"96 MP4V-ES/30000"
MP4_FMTP =
"96 profile-level-id=5;config=000001b005000001b50900000100000" +
"0012000c888ba9860fa22c087828307"
H264_RTP_MAP =
"96 H264/90000"
H264_FMTP =
"96 packetization-mode=1;profile-level-id=428032;" +
"sprop-parameter-sets=Z0KAMtoAgAMEwAQAAjKAAAr8gYAAAYhMAABMS0IvfjAA" +
"ADEJgAAJiWhF78CA,aM48gA=="
SOCAT_OPTIONS =
"rcvbuf=2500000,sndbuf=2500000,sndtimeo=0.00001,rcvtimeo=0.00001"
BLOCK_SIZE =
2000
BSD_OPTIONS =
"setsockopt-int=0xffff:0x200:0x01"

Constants included from Global

Global::DEFAULT_RTSP_PORT, Global::DEFAULT_VERSION

Instance Attribute Summary collapse

Attributes included from Global

#log, #log_level, #logger, #raise_errors

Instance Method Summary collapse

Methods included from Global

#log?, #raise_errors?, #reset_config!, #rtsp_version

Instance Attribute Details

#fmtpArray<String>

Returns Media format attributes.

Returns:

  • (Array<String>)

    Media format attributes.


52
53
54
# File 'lib/rtsp/socat_streaming.rb', line 52

def fmtp
  @fmtp
end

#interface_ipString

Returns IP address of the interface of the RTSP streamer.

Returns:

  • (String)

    IP address of the interface of the RTSP streamer.


37
38
39
# File 'lib/rtsp/socat_streaming.rb', line 37

def interface_ip
  @interface_ip
end

#pidsHash (readonly)

Returns Hash of session IDs and pids.

Returns:

  • (Hash)

    Hash of session IDs and pids.


25
26
27
# File 'lib/rtsp/socat_streaming.rb', line 25

def pids
  @pids
end

#rtcp_source_identifierString

Returns RTCP source identifier.

Returns:

  • (String)

    RTCP source identifier.


46
47
48
# File 'lib/rtsp/socat_streaming.rb', line 46

def rtcp_source_identifier
  @rtcp_source_identifier
end

#rtcp_threadsHash (readonly)

Returns Hash of session IDs and RTCP threads.

Returns:

  • (Hash)

    Hash of session IDs and RTCP threads.


28
29
30
# File 'lib/rtsp/socat_streaming.rb', line 28

def rtcp_threads
  @rtcp_threads
end

#rtp_mapArray<String>

Returns Media type attributes.

Returns:

  • (Array<String>)

    Media type attributes.


49
50
51
# File 'lib/rtsp/socat_streaming.rb', line 49

def rtp_map
  @rtp_map
end

#rtp_sequenceFixnum

Returns RTP sequence number of the source stream.

Returns:

  • (Fixnum)

    RTP sequence number of the source stream.


43
44
45
# File 'lib/rtsp/socat_streaming.rb', line 43

def rtp_sequence
  @rtp_sequence
end

#rtp_timestampFixnum

Returns RTP timestamp of the source stream.

Returns:

  • (Fixnum)

    RTP timestamp of the source stream.


40
41
42
# File 'lib/rtsp/socat_streaming.rb', line 40

def rtp_timestamp
  @rtp_timestamp
end

#sessionsHash

Returns Hash of session IDs and SOCAT commands.

Returns:

  • (Hash)

    Hash of session IDs and SOCAT commands.


22
23
24
# File 'lib/rtsp/socat_streaming.rb', line 22

def sessions
  @sessions
end

#source_ipArray<String>

Returns IP address of the source camera.

Returns:

  • (Array<String>)

    IP address of the source camera.


31
32
33
# File 'lib/rtsp/socat_streaming.rb', line 31

def source_ip
  @source_ip
end

#source_portArray<Fixnum>

Returns Port where the source camera is streaming.

Returns:

  • (Array<Fixnum>)

    Port where the source camera is streaming.


34
35
36
# File 'lib/rtsp/socat_streaming.rb', line 34

def source_port
  @source_port
end

Instance Method Details

#description(multicast = false, stream_index = 1) ⇒ Object

Returns the default stream description.

@param multicast True if the description is for a multicast stream.

Parameters:

  • stream_index (Fixnum) (defaults to: 1)

    Index of the stream type.


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/rtsp/socat_streaming.rb', line 144

def description(multicast=false, stream_index=1)
  rtp_map = @rtp_map[stream_index - 1] || H264_RTP_MAP
  fmtp = @fmtp[stream_index - 1] || H264_FMTP

  "v=0\\r\no=- 1345481255966282 1 IN IP4 \#{@interface_ip}\\r\ns=Session streamed by \"Streaming Server\"\\r\ni=stream1\#{multicast ? 'm' : ''}\\r\nt=0 0\\r\na=tool:LIVE555 Streaming Media v2007.07.09\\r\na=type:broadcast\\r\na=control:*\\r\na=range:npt=0-\\r\na=x-qt-text-nam:Session streamed by \"Streaming Server\"\\r\na=x-qt-text-inf:stream\#{stream_index}\#{multicast ? 'm' : ''}\\r\nm=video \#{multicast ? @source_port[stream_index - 1] : 0} RTP/AVP 96\\r\nc=IN IP4 \#{multicast ? \"\#{multicast_ip(stream_index)}/10\" : \"0.0.0.0\"}\\r\na=rtpmap:\#{rtp_map}\\r\na=fmtp:\#{fmtp}\\r\na=control:track1\\r\n"
end

#disconnect(sid) ⇒ Object

Disconnects the stream matching the session ID.

Parameters:

  • sid (String)

    Session ID.


171
172
173
174
175
176
177
178
# File 'lib/rtsp/socat_streaming.rb', line 171

def disconnect sid
  pid = @pids[sid].to_i
  @pids.delete(sid)
  @sessions.delete(sid)
  Process.kill(9, pid) if pid > 1000
rescue Errno::ESRCH
  log "Tried to kill dead process: #{pid}"
end

#generate_rtcp_source_id(friendly_name) ⇒ String

Generates a RTCP source ID based on the friendly name. This ID is used in the RTCP communication with the client. The default RTCP_SOURCE will be used if one is not provided.

Parameters:

  • friendly_name (String)

    Name to be used in the RTCP source ID.

Returns:

  • (String)

    rtcp_source_id RTCP Source ID.


60
61
62
63
# File 'lib/rtsp/socat_streaming.rb', line 60

def generate_rtcp_source_id friendly_name
  ["80c80006072dee6ad42c300f76c3b928377e99e5006c461ba92d8a3081ca0006072dee6a010e" +
    friendly_name.unpack("H*").first + "00000000"].pack("H*")
end

#parse_sequence_number(src_ip, src_port) ⇒ Array<Fixnum>

Parses the headers from an RTP stream.

Parameters:

  • src_ip (String)

    Multicast IP address of RTP stream.

  • src_port (Fixnum)

    Port of RTP stream.

Returns:

  • (Array<Fixnum>)

    Sequence number and timestamp


185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/rtsp/socat_streaming.rb', line 185

def parse_sequence_number(src_ip, src_port)
  sock = UDPSocket.new
  ip = IPAddr.new(src_ip).hton + IPAddr.new("0.0.0.0").hton
  sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ip)
  sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
  sock.bind(Socket::INADDR_ANY, src_port)

  begin
    data = sock.recv_nonblock(1500)
  rescue Errno::EAGAIN
    retry
  end

  sock.close
  packet = RTP::Packet.read(data)

  [packet["sequence_number"], packet["timestamp"]]
end

#setup_streamer(sid, request, index = 1, multicast = false) ⇒ Fixnum

Creates a RTP streamer using socat.

Parameters:

  • sid (String)

    Session ID.

  • reuuest (RTSP::Requesti)

    RTSP request

  • index (Fixnum) (defaults to: 1)

    Stream index.

Returns:

  • (Fixnum)

    The port the streamer will stream on.


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rtsp/socat_streaming.rb', line 71

def setup_streamer(sid, request, index=1, multicast=false)
  transport_url = request.transport_url
  dest_ip, dest_port = transport_url.split ":" unless multicast

  @rtcp_source_identifier ||= RTCP_SOURCE.pack("H*")
  local_port = multicast ? @source_port[index - 1] : free_port(true)
 
  unless multicast
    @rtcp_threads[sid] = Thread.start do
      s = UDPSocket.new
      s.bind(@interface_ip, local_port+1)
 
      loop do
        begin
          _, sender = s.recvfrom(36)
          s.send(@rtcp_source_identifier, 0, sender[3], sender[1])
        end
      end
    end
  end

  @cleaner ||= Thread.start { cleanup_defunct }
  @processes ||= Sys::ProcTable.ps.map { |p| p.cmdline }

  if multicast
    if request.transport.include?('cleint_port')
      /client_port=(?<p1>\d*)-(?<p2>\d*)/ =~ request.transport
      dest_port = p1
      if dest_port == "64000"
        @sessions[sid] = :multicast
      else
        puts "Request malformed client-port not found #{request.transport}" if dest_port.nil?
        raise "Request malformed client-port not found #{request.transport}" if dest_port.nil?
        @sessions[sid] = build_socat(@source_ip[index - 1], dest_port.to_i, local_port, index)
      end
    else
      @sessions[sid] = :multicast
    end
  else
    @sessions[sid] = build_socat(dest_ip, dest_port, local_port, index)
  end

  local_port
end

#start_streaming(sid) ⇒ Object

Start streaming for the requested session.

Parameters:

  • session (String)

    ID.


123
124
125
# File 'lib/rtsp/socat_streaming.rb', line 123

def start_streaming sid
  spawn_socat(sid, @sessions[sid]) unless @sessions[sid] == :multicast
end

#stop_streaming(sid) ⇒ Object

Stop streaming for the requested session.

Parameters:

  • session (String)

    ID.


130
131
132
133
134
135
136
137
138
# File 'lib/rtsp/socat_streaming.rb', line 130

def stop_streaming sid
  if sid.nil?
    disconnect_all_streams
  else
    disconnect sid
    @rtcp_threads[sid].kill unless rtcp_threads[sid].nil?
    @rtcp_threads.delete sid
  end
end