PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://mobicents.googlecode.com/
Java | 916 lines | 459 code | 158 blank | 299 comment | 53 complexity | 991e24781dd6875aefe0a1e5ffdea845 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.impl.rtp;
  19. import java.io.IOException;
  20. import java.net.DatagramSocket;
  21. import java.net.InetAddress;
  22. import java.net.InetSocketAddress;
  23. import java.net.PortUnreachableException;
  24. import java.net.SocketException;
  25. import java.nio.ByteBuffer;
  26. import java.nio.channels.ClosedChannelException;
  27. import java.nio.channels.DatagramChannel;
  28. import java.nio.channels.SelectionKey;
  29. import java.nio.channels.Selector;
  30. import java.util.Collection;
  31. import org.mobicents.media.Format;
  32. import org.mobicents.media.Server;
  33. import org.mobicents.media.server.NtpTimeStampUtil;
  34. import org.mobicents.media.server.impl.clock.LocalTask;
  35. import org.mobicents.media.server.impl.clock.Scheduler;
  36. import org.mobicents.media.server.impl.rtcp.RtcpPacket;
  37. import org.mobicents.media.server.impl.rtcp.RtcpReceptionReport;
  38. import org.mobicents.media.server.impl.rtcp.RtcpReceptionReportItem;
  39. import org.mobicents.media.server.impl.rtcp.RtcpSdes;
  40. import org.mobicents.media.server.impl.rtcp.RtcpSdesChunk;
  41. import org.mobicents.media.server.impl.rtcp.RtcpSdesItem;
  42. import org.mobicents.media.server.impl.rtcp.RtcpSenderReport;
  43. import org.mobicents.media.server.spi.MediaType;
  44. import org.mobicents.media.server.spi.ResourceUnavailableException;
  45. import org.mobicents.media.server.spi.clock.Task;
  46. import org.mobicents.media.server.spi.dsp.Codec;
  47. import org.mobicents.media.server.spi.rtp.AVProfile;
  48. import org.mobicents.media.server.spi.rtp.RtpSocket;
  49. /**
  50. * @author amit bhayani
  51. * @author Oleg Kulikov
  52. */
  53. public class RtpSocketImpl implements RtpSocket {
  54. private static int GEN = 1;
  55. private String id;
  56. // After every 5000 ms RTCP report will be sent
  57. public static final int RTCP_MIN_TIME = 5000;
  58. //If there is no RTP or RTCP received till RTCP_CUT_OFF_TIME, notify
  59. //listener to take appropriate action
  60. public static final int RTCP_CUT_OFF_TIME = 2 * RTCP_MIN_TIME;
  61. /** Media type */
  62. protected MediaType mediaType;
  63. //RTP local address and port pair
  64. private DatagramSocket rtpSocket;
  65. private DatagramChannel rtpChannel;
  66. //RTCP local address and port pair
  67. private DatagramSocket rtcpSocket;
  68. private DatagramChannel rtcpChannel;
  69. private String localAddress;
  70. //remote address and port pair
  71. protected InetSocketAddress rtpRemoteAddress = null;
  72. protected InetSocketAddress rtcpRemoteAddress = null;
  73. //jitter in milliseconds. default value is 60ms
  74. private int jitter = 60;
  75. //media streams
  76. private ReceiveStream receiveStream;
  77. private SendStream sendStream;
  78. //factory instance
  79. private RtpFactory factory = null;
  80. //listener instance
  81. private RtpSocketListener listener;
  82. //the default format map between rtp payload and format
  83. private AVProfile avProfile = new AVProfile();
  84. //collection of codecs
  85. protected Collection<Codec> codecs;
  86. //RTP clock instance
  87. private RtpClock clock;
  88. //the format and rtp payload number negotiatiated for conversation
  89. //this values are valid after negotiation and till this socket will be released
  90. private Format format;
  91. private int payloadId;
  92. private SelectionKey rtpSelection;
  93. private SelectionKey rtcpSelection;
  94. protected boolean registered = false;
  95. private ByteBuffer sendBuffer = ByteBuffer.allocateDirect(8192);
  96. //statistics
  97. private int rtpPacketsSent;
  98. private int rtpPacketsReceived;
  99. private int rtcpPacketsSent;
  100. private int rtcpPacketsReceived;
  101. private long rtcpOctetSent;
  102. private long rtcpOctetReceived;
  103. private long lastRtcpPackReceivedTimeStamp;
  104. private long lastRtcpSenderRprtReceivedTimeStamp;
  105. /**
  106. * Expected packets calculated on every RTCP transmission timeout and used as previous expected packets on next
  107. * transmission timeout for calculation of Fraction
  108. */
  109. private int intervalExpectedRtpPackets;
  110. /**
  111. * Actual received packets calculated on every transmission timeout and used as previous actual received packets on
  112. * next transmission timeout for calculation of Fraction
  113. */
  114. private int intervalReceivedRtpPackets;
  115. private RtcpSenderReport lastRtcpSenderReport = null;
  116. private volatile boolean isClosed = false;
  117. /**
  118. * The prefferedPort is assigned by RtpFactory.
  119. * RtpSocket will reuse the prefferedPort to bind the Socket everytime
  120. * till BindException is thrown.
  121. *
  122. * Once BindException, a new Port is obtained from RtpFactory and it becomes
  123. * as prefferedPort for all future binds
  124. */
  125. private int rtpPreferredPort;
  126. private int rtcpPreferredPort;
  127. /**
  128. * The reference to last RTP Packet sent. Used for creation of RTCP SR
  129. */
  130. private RtpPacket lastRtpPackSent;
  131. private Scheduler scheduler = Server.scheduler;
  132. private byte[] rtcpPacketRawData = new byte[8192];
  133. private RtcpSendStream rtcpSendStream = null;
  134. private RtpPacketHandler rtpPacketHandler = null;
  135. private RtcpPacketHandler rtcpPacketHandler = null;
  136. private LocalTask rtcpSenderWorker;
  137. private String cname;
  138. private boolean silenceSuppression;
  139. /**
  140. * Creates a new instance of RtpSocket
  141. *
  142. * @param timer
  143. * used to synchronize receiver stream.
  144. * @param rtpMap
  145. * RTP payloads list.
  146. */
  147. public RtpSocketImpl(RtpFactory factory, Collection<Codec> codecs, MediaType media, boolean silenceSuppression) throws IOException, ResourceUnavailableException {
  148. this.id = this.genID();
  149. this.mediaType = media;
  150. this.clock = factory.getClock(media);
  151. this.factory = factory;
  152. //assigns default preconfgured value for silence suppression
  153. this.silenceSuppression = silenceSuppression;
  154. this.codecs = codecs;
  155. this.avProfile = factory.getAVProfile().clone();
  156. jitter = factory.getJitter();
  157. sendStream = new SendStream(this, this.avProfile);
  158. receiveStream = new ReceiveStream(this, factory.getJitter(), this.avProfile);
  159. this.localAddress = factory.getBindAddress();
  160. this.cname = "MMS-" + id + "-" + mediaType.getName() + "@" + localAddress;
  161. this.rtcpSendStream = new RtcpSendStream(this);
  162. this.rtpPacketHandler = new RtpPacketHandler(this);
  163. this.rtcpPacketHandler = new RtcpPacketHandler(this);
  164. }
  165. /**
  166. * Generates unique identifier for this connection.
  167. *
  168. * @return hex view of the unique integer.
  169. */
  170. private String genID() {
  171. GEN++;
  172. if (GEN == Integer.MAX_VALUE) {
  173. GEN = 1;
  174. }
  175. return Integer.toHexString(GEN);
  176. }
  177. /**
  178. * Opens channel.
  179. *
  180. * @throws java.io.IOException
  181. */
  182. private void openRtpChannel() throws IOException {
  183. rtpChannel = DatagramChannel.open();
  184. rtpChannel.configureBlocking(false);
  185. rtpSocket = rtpChannel.socket();
  186. }
  187. private void openRtcpChannel() throws IOException {
  188. rtcpChannel = DatagramChannel.open();
  189. rtcpChannel.configureBlocking(false);
  190. rtcpSocket = rtcpChannel.socket();
  191. }
  192. private void closeRtpSocket() {
  193. try {
  194. if (rtpChannel != null) {
  195. rtpChannel.close();
  196. }
  197. if (rtpSocket != null) {
  198. rtpSocket.disconnect();
  199. rtpSocket.close();
  200. }
  201. } catch (IOException e) {
  202. }
  203. }
  204. private void closeRtcpSocket() {
  205. try {
  206. if (rtcpChannel != null) {
  207. rtcpChannel.close();
  208. }
  209. if (rtcpSocket != null) {
  210. rtcpSocket.disconnect();
  211. rtcpSocket.close();
  212. }
  213. } catch (IOException e) {
  214. }
  215. }
  216. /**
  217. * Binds Datagram to the address sprecified.
  218. *
  219. * @throws java.io.IOException
  220. * @throws org.mobicents.media.server.spi.ResourceUnavailableException
  221. */
  222. public void bind() throws IOException, ResourceUnavailableException {
  223. //disbale closed flag if was set
  224. this.isClosed = false;
  225. boolean portCycled = false;
  226. this.rtpPreferredPort = factory.getNextEvenPort();
  227. this.rtcpPreferredPort = factory.getNextOddPort();
  228. int initialPort = this.rtpPreferredPort;
  229. //opening RTP channel
  230. openRtpChannel();
  231. //binding socket to the first available port.
  232. while (!rtpSocket.isBound()) {
  233. try {
  234. //it is expected that "preffered port" is what we need
  235. InetSocketAddress address = new InetSocketAddress(factory.getBindAddress(), this.rtpPreferredPort);
  236. rtpSocket.bind(address);
  237. } catch (SocketException e) {
  238. //port is in use? let's try another
  239. this.rtpPreferredPort = this.factory.getNextEvenPort();
  240. if (this.rtpPreferredPort <= initialPort) {
  241. portCycled = true;
  242. }
  243. if (portCycled && this.rtpPreferredPort > initialPort) {
  244. //At this point one full cycle of ports are scanned and there are no free ports. Better to exit
  245. break;
  246. }
  247. continue;
  248. }
  249. }//while loop
  250. //RTP socket still not bound? throw exception
  251. if (!rtpSocket.isBound()) {
  252. rtpChannel.close();
  253. throw new ResourceUnavailableException();
  254. }
  255. initialPort = this.rtcpPreferredPort;
  256. portCycled = false;
  257. //Opening RTCP channel
  258. openRtcpChannel();
  259. while (!rtcpSocket.isBound()) {
  260. try {
  261. InetSocketAddress address = new InetSocketAddress(localAddress, this.rtcpPreferredPort);
  262. rtcpSocket.bind(address);
  263. } catch (SocketException e) {
  264. //Port is not free. let's try another
  265. this.rtcpPreferredPort = this.factory.getNextOddPort();
  266. if (this.rtcpPreferredPort <= initialPort) {
  267. portCycled = true;
  268. }
  269. if (portCycled && this.rtcpPreferredPort > initialPort) {
  270. //At this point one full cycle of ports are scanned and there are no free ports. Better to exit
  271. break;
  272. }
  273. }
  274. }
  275. //RTCP socket still not bound? throw exception
  276. if (!rtcpSocket.isBound()) {
  277. rtcpChannel.close();
  278. this.closeRtpSocket();
  279. throw new ResourceUnavailableException();
  280. }
  281. }
  282. /**
  283. * Registers this socket usign specified selector.
  284. *
  285. * @param selector the selector for registration.
  286. * @throws java.nio.channels.ClosedChannelException
  287. */
  288. public void register(Selector selector) throws ClosedChannelException {
  289. rtpSelection = rtpChannel.register(selector, SelectionKey.OP_READ, this.rtpPacketHandler);
  290. rtcpSelection = rtcpChannel.register(selector, SelectionKey.OP_READ, this.rtcpPacketHandler);
  291. registered = true;
  292. }
  293. /**
  294. * Gets the currently used format.
  295. *
  296. * @return the format instance.
  297. */
  298. public Format getFormat() {
  299. return format;
  300. }
  301. /**
  302. * Gets the list of used codecs.
  303. *
  304. * @return list of codecs.
  305. */
  306. public Collection<Codec> getCodecs() {
  307. return codecs;
  308. }
  309. /**
  310. * Specifies format and payload id which will be used by this socket for transmission
  311. *
  312. * This methods should be used by other components which are responsible for SDP negotiation.
  313. * The socket itself can not negotiate SDP.
  314. *
  315. *
  316. * @param payloadId rtp payload number
  317. * @param format the format object.
  318. */
  319. public void setFormat(int payloadId, Format format) {
  320. //checking input parameters
  321. if (payloadId < 0) {
  322. throw new IllegalArgumentException("Illegal payload number");
  323. }
  324. if (format == null) {
  325. throw new IllegalArgumentException("Format can not be null");
  326. }
  327. // No any checks with formatConfig!
  328. // format conf is used as default configuration for creating local session
  329. // description when remote session description is not known yet.
  330. // just apply values as is
  331. this.payloadId = payloadId;
  332. this.format = format;
  333. //initialize streams
  334. sendStream.setFormat(payloadId, format);
  335. receiveStream.setFormat(payloadId, format);
  336. }
  337. /**
  338. * Assigns RFC2833 DTMF playload number.
  339. *
  340. * @param dtmf the DTMF payload number.
  341. */
  342. public void setDtmfPayload(int dtmf) {
  343. receiveStream.setDtmf(dtmf);
  344. sendStream.setDtmf(dtmf);
  345. }
  346. /**
  347. * Modifies silence suppression flag.
  348. *
  349. * @param value the new value for flag.
  350. */
  351. public void setSilenceSuppression(boolean value) {
  352. this.silenceSuppression = value;
  353. }
  354. /**
  355. * Indicates the status of silence suppression algorithm.
  356. *
  357. * @return true if silence suppression is enabled.
  358. */
  359. public boolean isSilenceSuppressed() {
  360. return this.silenceSuppression;
  361. }
  362. /**
  363. * Gets address to which this socked is bound.
  364. *
  365. * @return either local address to which this socket is bound or public
  366. * address in case of NAT translation.
  367. */
  368. public String getLocalAddress() {
  369. return localAddress;
  370. }
  371. /**
  372. * Returns port number to which this socked is bound.
  373. *
  374. * @return port number or -1 if socket not bound.
  375. */
  376. public int getLocalPort() {
  377. return this.rtpPreferredPort;
  378. }
  379. /**
  380. * Gets the jitter for time of packet arrival
  381. *
  382. * @return the value of jitter in milliseconds.
  383. */
  384. public int getJitter() {
  385. return this.jitter;
  386. }
  387. /**
  388. * Assign new value of packet time arrival jitter.
  389. *
  390. * @param jitter
  391. * the value of jitter in milliseconds.
  392. */
  393. public void setJitter(int jitter) {
  394. this.jitter = jitter;
  395. }
  396. /**
  397. * Assign RTP clock implementation.
  398. *
  399. * @param clock the RTP clock instance;
  400. */
  401. public void setClock(RtpClock clock) {
  402. this.clock = clock;
  403. }
  404. /**
  405. * Gets current RTP clock instance.
  406. *
  407. * @return the RTP clock instance.
  408. */
  409. public RtpClock getClock() {
  410. return clock;
  411. }
  412. /**
  413. * Gets receiver stream.
  414. *
  415. * @return receiver stream instance.
  416. */
  417. public ReceiveStream getReceiveStream() {
  418. return receiveStream;
  419. }
  420. /**
  421. * Gets currently used audio/video profile
  422. * @return
  423. */
  424. public AVProfile getAVProfile() {
  425. return this.avProfile;
  426. }
  427. /**
  428. * Gets the number of received packets
  429. *
  430. * @return the number of packets received
  431. */
  432. public int getPacketsReceived() {
  433. return rtpPacketsReceived;
  434. }
  435. /**
  436. * Gets the number of sent packets
  437. *
  438. * @return the number of sent packets.
  439. */
  440. public int getPacketsSent() {
  441. return rtpPacketsSent;
  442. }
  443. /**
  444. * Closes this socket and resets its streams;
  445. * This method is called by RtpSocket user.
  446. *
  447. */
  448. public void release() {
  449. //channel close action should be synchronized with read and write
  450. //but we want to avid usage of any locks so at this stage we are
  451. //setting flag that socket is closed only!
  452. //
  453. //Receiver will check this flag on selected channel and perform actual close procedure.
  454. this.isClosed = true;
  455. if (rtcpSenderWorker != null) {
  456. this.rtcpSenderWorker.cancel();
  457. }
  458. }
  459. public void close() {
  460. if (rtpSelection != null) {
  461. rtpSelection.cancel();
  462. }
  463. //reset streams
  464. receiveStream.reset();
  465. sendStream.reset();
  466. //disable format and payload
  467. this.format = null;
  468. this.payloadId = -1;
  469. //clean stats
  470. rtpPacketsSent = 0;
  471. rtpPacketsReceived = 0;
  472. this.closeRtpSocket();
  473. if (rtcpSelection != null) {
  474. rtcpSelection.cancel();
  475. }
  476. this.lastRtcpPackReceivedTimeStamp = 0;
  477. this.rtcpOctetReceived = 0;
  478. this.rtcpPacketsReceived = 0;
  479. this.rtcpOctetSent = 0;
  480. this.rtcpOctetReceived = 0;
  481. this.intervalExpectedRtpPackets = 0;
  482. this.intervalReceivedRtpPackets = 0;
  483. this.lastRtpPackSent = null;
  484. this.rtcpSendStream.reset();
  485. this.closeRtcpSocket();
  486. }
  487. public boolean isClosed() {
  488. return this.isClosed;
  489. }
  490. /**
  491. * Gets the currently assigned listener.
  492. *
  493. * @return the listener instance.
  494. */
  495. public RtpSocketListener getListener() {
  496. return listener;
  497. }
  498. /**
  499. * Assigns listener which will receive notifications.
  500. *
  501. * @param listener the listener instance.
  502. */
  503. public void setListener(RtpSocketListener listener) {
  504. this.listener = listener;
  505. }
  506. /**
  507. * Assigns remote end.
  508. *
  509. * @param address
  510. * the address of the remote party.
  511. * @param port
  512. * the port number of the remote party.
  513. */
  514. public void setPeer(InetAddress address, int port) throws IOException {
  515. rtpRemoteAddress = new InetSocketAddress(address, port);
  516. rtpChannel.connect(rtpRemoteAddress);
  517. rtcpRemoteAddress = new InetSocketAddress(address, port + 1);
  518. rtcpChannel.connect(rtcpRemoteAddress);
  519. factory.registerQueue.offer(this);
  520. // Add RtcpSendStream to scheduler to send RTCP Report every 5 secs
  521. this.rtcpSenderWorker = scheduler.execute(rtcpSendStream);
  522. }
  523. /**
  524. * (Non Java-doc).
  525. *
  526. * @see org.mobicents.media.server.impl.rtp.RtpSocket#startSendStream(PushBufferDataSource);
  527. */
  528. public SendStream getSendStream() {
  529. return sendStream;
  530. }
  531. private void send(RtcpPacket rtcpPacket) {
  532. // TODO optimize the RTCPPacket to use the ByteBuffer directly
  533. int length = rtcpPacket.encode(rtcpPacketRawData, 0);
  534. this.rtcpPacketsSent += rtcpPacket.getNoOfPackets();
  535. this.rtcpOctetSent += length;
  536. // converting packet to binary array and sent to the remote address.
  537. sendBuffer.clear();
  538. sendBuffer.rewind();
  539. sendBuffer.put(rtcpPacketRawData, 0, length);
  540. sendBuffer.flip();
  541. try {
  542. rtcpChannel.write(sendBuffer);
  543. } catch (IOException e) {
  544. this.notify(e);
  545. }
  546. }
  547. /**
  548. * Sends media data to remote peer.
  549. *
  550. * This method uses blocking sending to make sure that data is out in time.
  551. *
  552. * @param RtpPacket -
  553. * the packet which contains media data and rtp header
  554. * @throws java.io.IOException
  555. */
  556. public void send(RtpPacket packet) throws IOException {
  557. //check state flag before sending
  558. if (!this.isClosed) {
  559. //Reference to last RTP Packet Sent and time when it was sent
  560. //this.lastRtpPackSentTimeStamp = scheduler.getTimestamp();
  561. this.lastRtpPackSent = packet;
  562. byte[] p = packet.toByteArray();
  563. send(p);
  564. }
  565. }
  566. /**
  567. * Sends rtp packet to the remote peer.
  568. *
  569. * @param packet the rtp packet as binary arrary
  570. * @throws java.io.IOException
  571. */
  572. public void send(byte[] packet) throws IOException {
  573. //coverting packet to binary array and sent to the remote address.
  574. sendBuffer.clear();
  575. sendBuffer.rewind();
  576. sendBuffer.put(packet);
  577. sendBuffer.flip();
  578. try {
  579. rtpChannel.write(sendBuffer);
  580. } catch (PortUnreachableException e) {
  581. } catch (IOException e) {
  582. this.notify(e);
  583. }
  584. rtpPacketsSent++;
  585. }
  586. /**
  587. * This method is called when rtp socket receives new rtp frame.
  588. *
  589. * @param rtpPacket
  590. */
  591. public void receiveRtp(RtpPacket rtpPacket) {
  592. receiveStream.process(rtpPacket);
  593. rtpPacketsReceived++;
  594. }
  595. public void receiveRtcp(RtcpPacket rtcpPacket) {
  596. RtcpReceptionReportItem[] rtcpReceptionReports = null;
  597. // Keep reference to timeStamp when last RTCP was received
  598. this.lastRtcpPackReceivedTimeStamp = scheduler.getTimestamp();
  599. this.rtcpOctetReceived += rtcpPacket.getPacketSize();
  600. this.rtcpPacketsReceived += rtcpPacket.getNoOfPackets();
  601. if (rtcpPacket.getRtcpSenderReport() != null) {
  602. this.lastRtcpSenderReport = rtcpPacket.getRtcpSenderReport();
  603. this.lastRtcpSenderRprtReceivedTimeStamp = this.lastRtcpPackReceivedTimeStamp;
  604. rtcpReceptionReports = this.lastRtcpSenderReport.getRtcpReceptionReports();
  605. }
  606. //if(rtcpReceptionReports != null){
  607. //TODO : sender may modify its transmissions based on the feedback. Define events to be fired for Listener
  608. //}
  609. }
  610. /**
  611. * Notifies the listener that something goes wrong.
  612. *
  613. * @param e the exception.
  614. */
  615. public void notify(Exception e) {
  616. if (listener != null) {
  617. listener.error(e);
  618. }
  619. }
  620. public boolean rtcpExpired() {
  621. long now = scheduler.getTimestamp();
  622. if (now - this.lastRtcpPackReceivedTimeStamp > RTCP_CUT_OFF_TIME) {
  623. return true;
  624. }
  625. return false;
  626. }
  627. /**
  628. * Statistical method.
  629. *
  630. * @return the number of bytes received.
  631. */
  632. public long getBytesReceived() {
  633. return receiveStream.byteCount;
  634. }
  635. /**
  636. * Statistical method.
  637. *
  638. * @return the number of bytes sent.
  639. */
  640. public long getBytesSent() {
  641. return sendStream.byteCount;
  642. }
  643. public MediaType getMediaType() {
  644. return mediaType;
  645. }
  646. class RtcpSendStream implements Task {
  647. private RtpSocketImpl rtpSocketImpl = null;
  648. private boolean initial = true;
  649. RtcpSendStream(RtpSocketImpl rtpSocketImpl) {
  650. this.rtpSocketImpl = rtpSocketImpl;
  651. }
  652. void reset() {
  653. this.initial = true;
  654. }
  655. public void cancel() {
  656. // TODO Auto-generated method stub
  657. }
  658. public boolean isActive() {
  659. return !isClosed;
  660. }
  661. public int perform() {
  662. // If for first time we will send the report after 2.5 seconds. After this every 5 seconds
  663. if (initial) {
  664. this.initial = false;
  665. return (RTCP_MIN_TIME / 2);
  666. }
  667. RtcpSenderReport rtcpSenderReport = null;
  668. RtcpReceptionReport rtcpReceptionReport = null;
  669. RtcpSdes rtcpSdes = null;
  670. // RtcpAppDefined rtcpAppDefined = null;
  671. long now = Server.scheduler.getTimestamp();
  672. // rtpSocketImpl.setLastRtcpPackSentTimeStamp(now);
  673. JitterBuffer jitBuff = receiveStream.getJitterBuffer();
  674. int expectedPackets = jitBuff.getExpectedPacketCount();
  675. int receivedPackets = rtpSocketImpl.getPacketsReceived();
  676. RtpPacket rtpPacket = lastRtpPackSent;
  677. if (rtpPacket != null) {
  678. long[] ntpTs = NtpTimeStampUtil.toNtpTime(now);
  679. rtcpSenderReport = new RtcpSenderReport(false, rtpPacket.getSyncSource(), ntpTs[0], ntpTs[1], rtpPacket.getTimestamp(), rtpSocketImpl.getPacketsSent(), rtpSocketImpl.getBytesSent());
  680. rtcpSdes = new RtcpSdes(false);
  681. RtcpSdesItem rtcpSdesItem = new RtcpSdesItem(RtcpSdesItem.RTCP_SDES_CNAME, cname);
  682. RtcpSdesChunk rtcpSdesChunk = new RtcpSdesChunk(rtpPacket.getSyncSource());
  683. rtcpSdesChunk.addRtcpSdesItem(rtcpSdesItem);
  684. rtcpSdes.addRtcpSdesChunk(rtcpSdesChunk);
  685. }
  686. // TODO If no RTP sent for time > 2T raise RTCPSendTimeout event. May be indicator that silence started? But
  687. // do we need it? If needed and implemented we call rtcpSenderTimeout on listener
  688. //if (listener != null) {
  689. // listener.rtcpSenderTimeout(this.rtpSocketImpl);
  690. //}
  691. rtpPacket = jitBuff.getLastRtpPacketRecd();
  692. if (rtpPacket != null) {
  693. int lost = expectedPackets - receivedPackets;
  694. int expected_interval = (expectedPackets - intervalExpectedRtpPackets);
  695. int received_interval = (receivedPackets - intervalReceivedRtpPackets);
  696. int lost_interval = expected_interval - received_interval;
  697. int fraction = 0;
  698. if (expected_interval == 0 || lost_interval <= 0) {
  699. fraction = 0;
  700. } else {
  701. fraction = (lost_interval << 8) / expected_interval;
  702. }
  703. // The middle 32 bits out of 64 in the NTP timestamp (as explained in
  704. // Section 4) received as part of the most recent RTCP sender report
  705. // (SR) packet from source SSRC_n. If no SR has been received yet,
  706. // the field is set to zero.
  707. long lsr = 0;
  708. // The delay, expressed in units of 1/65536 seconds, between
  709. // receiving the last SR packet from source SSRC_n and sending this
  710. // reception report block.
  711. long dsr = 0;
  712. if (lastRtcpSenderReport != null) {
  713. lsr |= lastRtcpSenderReport.getNtpSec() & 0x0000FFFF;
  714. lsr <<= 16;
  715. lsr |= ((lastRtcpSenderReport.getNtpFrac() & 0xFFFF0000) >> 16);
  716. dsr = ((now - lastRtcpSenderRprtReceivedTimeStamp) * 65536) / 1000;
  717. }
  718. RtcpReceptionReportItem rtcpReceptionReportItem = new RtcpReceptionReportItem(
  719. rtpPacket.getSyncSource(), fraction, lost, jitBuff.getSeqNoCycles(), rtpPacket.getSeqNumber(),
  720. (int) receiveStream.getInterArrivalJitter(), lsr, dsr);
  721. if (rtcpSenderReport == null) {
  722. rtcpReceptionReport = new RtcpReceptionReport(false,
  723. receiveStream.getSsrc());
  724. rtcpReceptionReport.addRtcpReceptionReportItem(rtcpReceptionReportItem);
  725. rtcpSdes = new RtcpSdes(false);
  726. RtcpSdesItem rtcpSdesItem = new RtcpSdesItem(
  727. RtcpSdesItem.RTCP_SDES_CNAME, cname);
  728. RtcpSdesChunk rtcpSdesChunk = new RtcpSdesChunk(receiveStream.getSsrc());
  729. rtcpSdesChunk.addRtcpSdesItem(rtcpSdesItem);
  730. rtcpSdes.addRtcpSdesChunk(rtcpSdesChunk);
  731. } else {
  732. rtcpSenderReport.addRtcpReceptionReportItem(rtcpReceptionReportItem);
  733. }
  734. }
  735. // If no RTP and RTCP received for 2T fire RTCPReceiveTimeout event.
  736. // 1. This is useful when Connection is RECV_ONLY and far end is crashed.
  737. // 2. If Connection is SEND_RECV and far end crashed, the RTPSocket will anyway throw error and this
  738. // Connection will be deleted
  739. // 3. If Connection is SEND_RECV and silence is ON on MMS side and far end crashed, even then this algo will
  740. // work. But if far end doesn't
  741. // have RTCP implemented, this algo will wrongly indicate ReceiverTimeout. This is punishment for not
  742. // implementing RTCP ;)
  743. if ((now - jitBuff.getLastRtpPackReceivedTimeStamp()) > RtpSocketImpl.RTCP_CUT_OFF_TIME && (now - lastRtcpPackReceivedTimeStamp) > RtpSocketImpl.RTCP_CUT_OFF_TIME) {
  744. if (listener != null) {
  745. listener.rtcpReceiverTimeout(this.rtpSocketImpl);
  746. }
  747. }
  748. // set the reference to expected and received packets here, so they can be referenced on next transmission
  749. // timeout for calculation of Fraction
  750. intervalExpectedRtpPackets = expectedPackets;
  751. intervalReceivedRtpPackets = receivedPackets;
  752. RtcpPacket rtcpPacket = new RtcpPacket(rtcpSenderReport,
  753. rtcpReceptionReport, rtcpSdes, null, null);
  754. send(rtcpPacket);
  755. return RTCP_MIN_TIME;
  756. }
  757. }
  758. }