PageRenderTime 28ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/servers/media/core/server-impl/src/main/java/org/mobicents/media/server/RtpConnectionImpl.java

http://mobicents.googlecode.com/
Java | 675 lines | 427 code | 100 blank | 148 comment | 97 complexity | 02416d1e2aac89257af6af070fd7e1bf MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright XXXX, Red Hat Middleware LLC, and individual contributors as indicated
  4. * by the @authors tag. All rights reserved.
  5. * See the copyright.txt in the distribution for a full listing
  6. * of individual contributors.
  7. * This copyrighted material is made available to anyone wishing to use,
  8. * modify, copy, or redistribute it subject to the terms and conditions
  9. * of the GNU General Public License, v. 2.0.
  10. * This program is distributed in the hope that it will be useful, but WITHOUT A
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12. * PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. * You should have received a copy of the GNU General Public License,
  14. * v. 2.0 along with this distribution; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  16. * MA 02110-1301, USA.
  17. */
  18. package org.mobicents.media.server;
  19. import java.io.IOException;
  20. import java.net.InetAddress;
  21. import java.net.InetSocketAddress;
  22. import java.net.UnknownHostException;
  23. import java.util.Collection;
  24. import java.util.HashMap;
  25. import org.mobicents.media.Format;
  26. import org.mobicents.media.server.impl.rtp.ReceiveStream;
  27. import org.mobicents.media.server.impl.rtp.RtpSocketImpl;
  28. import org.mobicents.media.server.impl.rtp.RtpSocketListener;
  29. import org.mobicents.media.server.impl.rtp.sdp.MediaDescriptor;
  30. import org.mobicents.media.server.impl.rtp.sdp.SessionDescriptor;
  31. import org.mobicents.media.server.resource.Channel;
  32. import org.mobicents.media.server.spi.Connection;
  33. import org.mobicents.media.server.spi.ConnectionListener;
  34. import org.mobicents.media.server.spi.ConnectionState;
  35. import org.mobicents.media.server.spi.Endpoint;
  36. import org.mobicents.media.server.spi.MediaType;
  37. import org.mobicents.media.server.spi.ResourceUnavailableException;
  38. import org.mobicents.media.server.spi.dsp.Codec;
  39. import org.mobicents.media.server.spi.rtp.AVProfile;
  40. import org.mobicents.media.server.spi.rtp.RtpManager;
  41. import org.mobicents.media.server.spi.rtp.RtpSocket;
  42. /**
  43. *
  44. * @author kulikov
  45. * @author amit bhayani
  46. */
  47. public class RtpConnectionImpl extends ConnectionImpl implements RtpSocketListener {
  48. //session descriptors
  49. private String localDescriptor;
  50. private String remoteDescriptor;
  51. //media profile
  52. private int[][] payloads = new int[2][15];
  53. //keeps formats for each media type: formats[MediaType.getCode()][index]
  54. private Format[][] formats = new Format[2][15];
  55. //number of media types
  56. private int channelCount;
  57. //numbers of format per channel
  58. private int[] formatCount = new int[2];
  59. private RtpSocket[] sockets = new RtpSocket[2];
  60. private String bindAddress;
  61. private int prefferedPayload;
  62. private Format prefferedFormat;
  63. public RtpConnectionImpl(Endpoint endpoint, ConnectionFactory connectionFactory, RtpManager rtpFactory) throws ResourceUnavailableException {
  64. super(endpoint, connectionFactory);
  65. //Get media available media types
  66. Collection<MediaType> mediaTypes = endpoint.getMediaTypes();
  67. //Get rtp factory
  68. RtpManager factory = rtpFactory;
  69. bindAddress = factory.getBindAddress();
  70. for (MediaType mediaType : mediaTypes) {
  71. int i = mediaType.getCode();
  72. try {
  73. sockets[i] = factory.getRTPSocket(mediaType);
  74. ((RtpSocketImpl)sockets[i]).setListener(this);
  75. } catch (IOException e) {
  76. throw new ResourceUnavailableException(e);
  77. }
  78. }
  79. }
  80. @Override
  81. protected void join(MediaType mediaType){
  82. super.join(mediaType);
  83. int i = mediaType.getCode();
  84. //RX formats with possible encodings
  85. Format[] rxFormats = new Format[15];
  86. int rxCount = 0;
  87. if (rxChannels[i] != null) {
  88. rxCount = getExtendedRxFormats(rxChannels[i].getInputFormats(), sockets[i].getCodecs(), rxFormats);
  89. }
  90. //TX formats with possible encodings
  91. Format[] txFormats = new Format[15];
  92. int txCount = 0;
  93. if (txChannels[i] != null) {
  94. txCount = getExtendedTxFormats(txChannels[i].getOutputFormats(), sockets[i].getCodecs(), txFormats);
  95. }
  96. HashMap<Integer, Format> profile = getProfile(sockets[i].getAVProfile(), mediaType);
  97. formatCount[i] = intersection(rxFormats, rxCount, txFormats, txCount, profile,
  98. payloads[i], formats[i]);
  99. }
  100. @Override
  101. protected void join() {
  102. Collection<MediaType> mediaTypes = ((BaseEndpointImpl)getEndpoint()).getMediaTypes();
  103. for (MediaType mediaType : mediaTypes) {
  104. this.join(mediaType);
  105. }
  106. }
  107. @Override
  108. protected void bind(MediaType mediaType) throws ResourceUnavailableException {
  109. if((this.stream & mediaType.getMask()) != 0x0){
  110. try {
  111. sockets[mediaType.getCode()].bind();
  112. } catch (IOException e) {
  113. throw new ResourceUnavailableException(e);
  114. }
  115. super.bind(mediaType);
  116. }
  117. //if connection is just bound then we need to update local descriptor
  118. localDescriptor = null;
  119. }
  120. //This is just helper method if Controller doesn't care of MediaType.
  121. protected void bind() throws ResourceUnavailableException {
  122. Collection<MediaType> mediaTypes = ((BaseEndpointImpl)getEndpoint()).getMediaTypes();
  123. for (MediaType mediaType : mediaTypes) {
  124. this.bind(mediaType);
  125. }
  126. }
  127. private HashMap<Integer, Format> getProfile(AVProfile avProfile, MediaType mediaType) {
  128. HashMap<Integer, Format> profile = new HashMap();
  129. switch (mediaType.getCode()) {
  130. case 0 :
  131. profile.putAll(avProfile.getAudioFormats());
  132. break;
  133. case 1 :
  134. profile.putAll(avProfile.getVideoFormats());
  135. break;
  136. }
  137. return profile;
  138. }
  139. private boolean formatPresent(Format[] srcFormat, Format formatToCheck, int count){
  140. for(int i = 0; i < count; i++){
  141. Format f = srcFormat[i];
  142. if(f.matches(formatToCheck)){
  143. return true;
  144. }
  145. }
  146. return false;
  147. }
  148. private int getExtendedRxFormats(Format[] formats, Collection<Codec> codecs, Format[] extended) {
  149. int count = 0;
  150. for (Format fmt : formats) {
  151. if(formatPresent(extended, fmt, count)){
  152. continue;
  153. }
  154. extended[count++] = fmt;
  155. if (fmt == Format.ANY) {
  156. break;
  157. }
  158. for (Codec c : codecs) {
  159. if (c.getSupportedOutputFormat().matches(fmt)) {
  160. boolean present = formatPresent(extended, c.getSupportedInputFormat(), count);
  161. if (!present) {
  162. extended[count++] = c.getSupportedInputFormat();
  163. }
  164. }
  165. }
  166. }
  167. return count;
  168. }
  169. private int getExtendedTxFormats(Format[] formats, Collection<Codec> codecs, Format[] extended) {
  170. int count = 0;
  171. for (Format fmt : formats) {
  172. if(formatPresent(extended, fmt, count)){
  173. continue;
  174. }
  175. extended[count++] = fmt;
  176. if (fmt == Format.ANY) {
  177. break;
  178. }
  179. for (Codec c : codecs) {
  180. if (c.getSupportedInputFormat().matches(fmt)) {
  181. boolean present = formatPresent(extended, c.getSupportedOutputFormat(), count);
  182. if (!present) {
  183. extended[count++] = c.getSupportedOutputFormat();
  184. }
  185. }
  186. }
  187. }
  188. return count;
  189. }
  190. private int intersection(Format[] rxFormats, int rxCount,
  191. Format[] txFormats, int txCount,
  192. HashMap<Integer, Format> profile,
  193. int[] payloads,
  194. Format[] formats) {
  195. Collection<Integer> keys = profile.keySet();
  196. int count = 0;
  197. for (Integer key: keys) {
  198. //get format from profile
  199. Format f = profile.get(key);
  200. //if RFC2833 event is presented in profile then
  201. //add it because it will be transformed into actual media format;
  202. if (f.matches(AVProfile.DTMF)) {
  203. payloads[count] = key;
  204. formats[count++] = f;
  205. continue;
  206. }
  207. //checking format in rx array
  208. boolean found = rxCount == 0;
  209. for (int i = 0; i < rxCount; i++) {
  210. if (f.matches(rxFormats[i])) {
  211. found = true;
  212. }
  213. }
  214. //if not found check next format from profile
  215. if (!found) {
  216. continue;
  217. }
  218. found = txCount == 0;
  219. for (int i = 0; i < txCount; i++) {
  220. if (f.matches(txFormats[i])) {
  221. found = true;
  222. }
  223. }
  224. //if not found check next format from profile
  225. if (!found) {
  226. continue;
  227. }
  228. payloads[count] = key;
  229. formats[count++] = f;
  230. }
  231. return count;
  232. }
  233. private String createLocalDescriptor() {
  234. SessionDescriptor sdp = null;
  235. String userName = "-";
  236. String sessionID = Long.toString(System.currentTimeMillis() & 0xffffff);
  237. String sessionVersion = sessionID;
  238. String networkType = "IN";
  239. String addressType = "IP4";
  240. sdp = new SessionDescriptor();
  241. sdp.createOrigin(userName, sessionID, sessionVersion, networkType, addressType, bindAddress);
  242. sdp.setSession("Mobicents Media Server");
  243. sdp.createConnection(networkType, addressType, bindAddress);
  244. // encode formats
  245. Collection<MediaType> mediaTypes = ((BaseEndpointImpl)getEndpoint()).getMediaTypes();
  246. for (MediaType mediaType : mediaTypes) {
  247. int i = mediaType.getCode();
  248. if (formatCount[i] > 0) {
  249. RtpSocket rtpSocket = sockets[i];
  250. int port = rtpSocket.getLocalPort();
  251. MediaDescriptor md = sdp.addMedia(MediaType.getMediaType(i), port);
  252. for (int k = 0; k < formatCount[i]; k++) {
  253. md.addFormat(payloads[i][k], formats[i][k]);
  254. }
  255. }
  256. }
  257. localDescriptor = sdp.toString();
  258. return localDescriptor;
  259. }
  260. public String getLocalDescriptor() {
  261. if (getState() == ConnectionState.NULL || getState() == ConnectionState.CLOSED) {
  262. throw new IllegalStateException("State is " + getState());
  263. }
  264. if (this.localDescriptor == null) {
  265. createLocalDescriptor();
  266. }
  267. return this.localDescriptor;
  268. }
  269. public String getRemoteDescriptor() {
  270. return this.remoteDescriptor;
  271. }
  272. private InetAddress getAddress(String address) throws UnknownHostException {
  273. return InetAddress.getByName(address);
  274. }
  275. public void setRemoteDescriptor(String descriptor) throws IOException, ResourceUnavailableException {
  276. //JSR-309 hack
  277. descriptor = descriptor.replaceAll("16.16.93.241", "127.0.0.1");
  278. this.remoteDescriptor = descriptor;
  279. if (getState() != ConnectionState.HALF_OPEN && getState() != ConnectionState.OPEN) {
  280. throw new IllegalStateException("State is " + getState());
  281. }
  282. //parse session descriptor
  283. SessionDescriptor sdp = new SessionDescriptor(descriptor);
  284. InetAddress address = getAddress(sdp.getOrigin().getAddress());
  285. //analyze media descriptions
  286. Collection<MediaType> mediaTypes = ((BaseEndpointImpl)getEndpoint()).getMediaTypes();
  287. //negotiate formats
  288. for (MediaType mediaType : mediaTypes) {
  289. subset(sdp.getMediaDescriptor(mediaType.getCode()), mediaType.getCode());
  290. }
  291. //check format negotiation result
  292. boolean negotiated = check(sdp);
  293. if (!negotiated) {
  294. throw new IOException("Codecs are not negotiated");
  295. }
  296. //select preffered for each media type
  297. int count = sdp.getMediaTypeCount();
  298. for (int i = 0; i < count; i++) {
  299. MediaDescriptor md = sdp.getMediaDescriptor(i);
  300. MediaType mediaType = md.getMediaType();
  301. //We want to setPeer to only Socket which are bound
  302. if((this.stream & mediaType.getMask()) != 0x0){
  303. int k = mediaType.getCode();
  304. //asign prefferd formats
  305. selectPreffered(md);
  306. //join sockets and channels
  307. sockets[k].setPeer(address, md.getPort());
  308. Channel rxChannel = rxChannels[k];
  309. Channel txChannel = txChannels[k];
  310. if (rxChannel != null) {
  311. rxChannel.connect(sockets[k].getReceiveStream());
  312. }
  313. if (txChannel != null) {
  314. txChannel.connect(sockets[k].getSendStream());
  315. }
  316. //start transmission
  317. sockets[k].getReceiveStream().start();
  318. sockets[k].getSendStream().start();
  319. setMode(mode[k], mediaType);
  320. }
  321. }
  322. this.localDescriptor = null;
  323. setState(ConnectionState.OPEN);
  324. }
  325. /**
  326. * Checks the result of format negotiation.
  327. *
  328. * @return true if all media types are negotiated
  329. */
  330. private boolean check(SessionDescriptor sdp) {
  331. //codecs are negotiated if for all offered media descriptors
  332. //the number of formats > 0;
  333. int count = sdp.getMediaTypeCount();
  334. boolean passed = true;
  335. for (int i = 0; i < count; i++) {
  336. MediaDescriptor md = sdp.getMediaDescriptor(i);
  337. passed = formatCount[md.getMediaType().getCode()] != 0;
  338. if (!passed) {
  339. break;
  340. }
  341. }
  342. return passed;
  343. }
  344. /**
  345. * Calculates intersection between local formats and offered
  346. *
  347. * @param md the media descriptor offered.
  348. * @param mediaType the media type of the offer
  349. */
  350. private void subset(MediaDescriptor md, int mediaType) {
  351. //if media descriptor is null then we set format count to zero
  352. if (md == null) {
  353. formatCount[mediaType] = 0;
  354. return;
  355. }
  356. //detrmine subset.
  357. //the goal is to find only one common media format and rfc2833 event
  358. //payload numbers for subset
  359. int[] pt = new int[15];
  360. //formats for subset
  361. Format[] subset = new Format[15];
  362. //the number of formats presented in offer
  363. int fcount = md.getFormatCount();
  364. //index for subset arrays
  365. int l = 0;
  366. //flag raised when media formats is found
  367. boolean mediaFound = false;
  368. //flag raised when rfc2833 event descriptor is found
  369. boolean rfc2833Found = false;
  370. //checking offered format in loop
  371. for (int i = 0; i < fcount; i++) {
  372. //the next format for analysis.
  373. Format f = md.getFormat(i);
  374. //looking at rfc2833 flag. if format already is found
  375. //then we skip rfc2833 formats negotiation
  376. //if not found yet then we compare formats
  377. //if it matches then search same format in the list of supported
  378. //if it does not match then check mediaFound flag and if media format is not
  379. //negotiated yet run loop for searching.
  380. if (!rfc2833Found && f.matches(AVProfile.DTMF)) {
  381. for (int k = 0; k < formatCount[mediaType]; k++) {
  382. if (formats[mediaType][k].matches(f)) {
  383. pt[l] = md.getPyaloadType(i);
  384. subset[l++] = f;
  385. rfc2833Found = true;
  386. }
  387. }
  388. } else if (!mediaFound) {
  389. for (int k = 0; k < formatCount[mediaType]; k++) {
  390. if (formats[mediaType][k].matches(f)) {
  391. pt[l] = md.getPyaloadType(i);
  392. subset[l++] = f;
  393. mediaFound = true;
  394. }
  395. }
  396. }
  397. //if all formats negotiated break loop
  398. if (mediaFound && rfc2833Found) {
  399. break;
  400. }
  401. }
  402. //update supported formats with subset
  403. this.payloads[mediaType] = pt;
  404. this.formats[mediaType] = subset;
  405. this.formatCount[mediaType] = l;
  406. }
  407. /**
  408. * This methods selects preffered format and assigns it to the relative socket
  409. *
  410. * @param md media descriptor.
  411. */
  412. private void selectPreffered(MediaDescriptor md) {
  413. //it takes first non rfc2833 format and assigns it to socket
  414. int k = md.getMediaType().getCode();
  415. //formats contains one or two formats.
  416. //if two formats then one is RFC2833
  417. if (formats[k][0].matches(AVProfile.DTMF)) {
  418. sockets[k].setFormat(payloads[k][1], formats[k][1]);
  419. sockets[k].setDtmfPayload(payloads[k][0]);
  420. } else {
  421. sockets[k].setFormat(payloads[k][0], formats[k][0]);
  422. sockets[k].setDtmfPayload(payloads[k][1]);
  423. }
  424. // for (int i = 0; i < formatCount[k]; i++) {
  425. // if (!formats[k][i].matches(AVProfile.DTMF)) {
  426. // sockets[k].setFormat(payloads[k][i], formats[k][i]);
  427. // break;
  428. // }
  429. // }
  430. }
  431. public void setOtherParty(Connection other) throws IOException {
  432. throw new UnsupportedOperationException("Not supported yet.");
  433. }
  434. public void setOtherParty(Connection other, MediaType mediaType) throws IOException {
  435. throw new UnsupportedOperationException("Not supported yet.");
  436. }
  437. @Override
  438. public void close(MediaType mediaType){
  439. //We want to disconnect only if already joined
  440. if((this.stream & mediaType.getMask()) != 0x0){
  441. int i = mediaType.getCode();
  442. //Reset the FormatCount
  443. this.formatCount[i] = 0;
  444. if (sockets[i] != null) {
  445. //terminate transmission
  446. sockets[i].getReceiveStream().stop();
  447. sockets[i].getSendStream().stop();
  448. //disconnect rx
  449. if (rxChannels[i] != null && sockets[i].getReceiveStream().isConnected()) {
  450. rxChannels[i].disconnect(sockets[i].getReceiveStream());
  451. }
  452. //disconnect tx
  453. if (txChannels[i] != null && sockets[i].getSendStream().isConnected()) {
  454. txChannels[i].disconnect(sockets[i].getSendStream());
  455. }
  456. //disconecting remote side and unbinding from local address
  457. sockets[i].release();
  458. super.close(mediaType);
  459. }
  460. }
  461. }
  462. @Override
  463. public void close() {
  464. Collection<MediaType> mediaTypes = ((BaseEndpointImpl)getEndpoint()).getMediaTypes();
  465. for (MediaType mediaType : mediaTypes) {
  466. this.close(mediaType);
  467. }
  468. }
  469. public void error(Exception e) {
  470. //getEndpoint().deleteConnection(this.getId());
  471. for (Object ocl : this.connectionListeners.keySet()) {
  472. ConnectionListener cl = (ConnectionListener) ocl;
  473. cl.onError(this, e);
  474. }
  475. }
  476. /**
  477. * (Non Java-doc).
  478. *
  479. * @see org.mobicents.media.server.spi.Connection#getPacketsReceived(org.mobicents.media.server.spi.MediaType)
  480. */
  481. public long getPacketsReceived(MediaType media) {
  482. return sockets[media.getCode()].getReceiveStream().getPacketsTransmitted();
  483. }
  484. /**
  485. * (Non Java-doc).
  486. *
  487. * @see org.mobicents.media.server.spi.Connection#getBytesReceived(org.mobicents.media.server.spi.MediaType)
  488. */
  489. public long getBytesReceived(MediaType media) {
  490. return sockets[media.getCode()].getReceiveStream().getBytesTransmitted();
  491. }
  492. /**
  493. * (Non Java-doc).
  494. *
  495. * @see org.mobicents.media.server.spi.Connection#getBytesReceived()
  496. */
  497. public long getBytesReceived() {
  498. int res = 0;
  499. for (int i = 0; i < sockets.length; i++) {
  500. if (sockets[i] != null) {
  501. res += sockets[i].getBytesReceived();
  502. }
  503. }
  504. return res;
  505. }
  506. /**
  507. * (Non Java-doc).
  508. *
  509. * @see org.mobicents.media.server.spi.Connection#getPacketsTransmitted(org.mobicents.media.server.spi.MediaType)
  510. */
  511. public long getPacketsTransmitted(MediaType media) {
  512. return sockets[media.getCode()].getSendStream().getPacketsReceived();
  513. }
  514. /**
  515. * (Non Java-doc).
  516. *
  517. * @see org.mobicents.media.server.spi.Connection#getBytesTransmitted(org.mobicents.media.server.spi.MediaType)
  518. */
  519. public long getBytesTransmitted(MediaType media) {
  520. return sockets[media.getCode()].getSendStream().getBytesReceived();
  521. }
  522. /**
  523. * (Non Java-doc).
  524. *
  525. * @see org.mobicents.media.server.spi.Connection#getBytesTransmitted()
  526. */
  527. public long getBytesTransmitted() {
  528. int res = 0;
  529. for (int i = 0; i < sockets.length; i++) {
  530. if (sockets[i] != null) {
  531. res += sockets[i].getBytesReceived();
  532. }
  533. }
  534. return res;
  535. }
  536. /**
  537. * (Non Java-doc).
  538. *
  539. * @see org.mobicents.media.server.spi.Connection#getJitter(org.mobicents.media.server.spi.MediaType)
  540. */
  541. public double getJitter(MediaType media) {
  542. return sockets[media.getCode()].getJitter();
  543. }
  544. /**
  545. * (Non Java-doc).
  546. *
  547. * @see org.mobicents.media.server.spi.Connection#getJitter()
  548. */
  549. public double getJitter() {
  550. double res = 0;
  551. int count = 0;
  552. for (int i = 0; i < sockets.length; i++) {
  553. if (sockets[i] != null) {
  554. res += ((ReceiveStream)sockets[i].getReceiveStream()).getInterArrivalJitter();
  555. count++;
  556. }
  557. }
  558. return res/count;
  559. }
  560. public void setOtherParty(String media, InetSocketAddress address) throws IOException {
  561. //rtpSockets.get(media).setPeer(address.getAddress(), address.getPort());
  562. }
  563. public void rtcpReceiverTimeout(RtpSocket rtpSocket) {
  564. //TODO If the Mode of Connection is SEND_RECV or RECV_ONLY and if Receiver Timeout occur's probably
  565. //the far end died and notify the Connection Listener to take appropriate action
  566. }
  567. public void rtcpSenderTimeout(RtpSocket rtpSocket) {
  568. // TODO If the Mode of Connection is SEND_RECV or SEND_ONLY and no if Sender Timeout occur's
  569. // may be the silence started.
  570. }
  571. @Override
  572. public String toString() {
  573. return "RTP Connection [" + getEndpoint().getLocalName() + ", idx=" + getIndex() + "], state=" + getState();
  574. }
  575. }