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

/servers/media/component/src/main/java/org/mobicents/media/server/impl/AbstractSource.java

http://mobicents.googlecode.com/
Java | 614 lines | 276 code | 97 blank | 241 comment | 30 complexity | 939ddca43a4dedf02c7ecb0d377b9668 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 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.media.server.impl;
  23. import java.util.concurrent.ConcurrentLinkedQueue;
  24. import org.mobicents.media.MediaSource;
  25. import org.mobicents.media.server.scheduler.Scheduler;
  26. import org.mobicents.media.server.scheduler.Task;
  27. import org.mobicents.media.server.spi.FormatNotSupportedException;
  28. import org.mobicents.media.server.spi.dsp.Codec;
  29. import org.mobicents.media.server.spi.dsp.Processor;
  30. import org.mobicents.media.server.spi.format.Format;
  31. import org.mobicents.media.server.spi.format.Formats;
  32. import org.mobicents.media.server.spi.io.Pipe;
  33. import org.mobicents.media.server.spi.memory.Frame;
  34. /**
  35. * The base implementation of the Media source.
  36. *
  37. * <code>AbstractSource</code> and <code>AbstractSink</code> are implement general wirring contruct. All media
  38. * components have to extend one of these classes.
  39. *
  40. * @author Oleg Kulikov
  41. */
  42. public abstract class AbstractSource extends BaseComponent implements MediaSource {
  43. //transmission statisctics
  44. private volatile long txPackets;
  45. private volatile long txBytes;
  46. //shows if component is started or not.
  47. private volatile boolean started;
  48. //stream synchronization flag
  49. private volatile boolean isSynchronized;
  50. //local media time
  51. private volatile long timestamp = 0;
  52. //initial media time
  53. private long initialOffset;
  54. //frame sequence number
  55. private long sn = 1;
  56. //duration of media stream in nanoseconds
  57. protected long duration = -1;
  58. //intial delay for media processing
  59. private long initialDelay = 0;
  60. //scheduler instance
  61. private Scheduler scheduler;
  62. //media generator
  63. private final Worker worker;
  64. //DSP processing task
  65. private final Transcoder transcoder;
  66. //media transmission pipe
  67. protected PipeImpl pipe;
  68. //digital signaling processor
  69. private Processor dsp;
  70. //active transmission formats
  71. private final Formats formats = new Formats();
  72. //supported formats
  73. private final Formats supportedFormats = new Formats();
  74. //temporary buffer
  75. private ConcurrentLinkedQueue<Frame> buffer = new ConcurrentLinkedQueue();
  76. private Stats stats = new Stats();
  77. /**
  78. * Creates new instance of source with specified name.
  79. *
  80. * @param name
  81. * the name of the source to be created.
  82. */
  83. public AbstractSource(String name, Scheduler scheduler) {
  84. super(name);
  85. this.scheduler = scheduler;
  86. this.worker = new Worker(scheduler);
  87. this.transcoder = new Transcoder(scheduler);
  88. this.initFormats();
  89. }
  90. /**
  91. * (Non Java-doc.)
  92. *
  93. * @see org.mobicents.media.server.impl.AbstractSource#setInitialDelay(long)
  94. */
  95. public void setInitialDelay(long initialDelay) {
  96. this.initialDelay = initialDelay;
  97. }
  98. /**
  99. * Initializes list of supported and transmission formats if possible
  100. */
  101. private void initFormats() {
  102. if (this.getNativeFormats().size() > 0) {
  103. supportedFormats.clean();
  104. supportedFormats.addAll(getNativeFormats());
  105. }
  106. formats.clean();
  107. formats.addAll(supportedFormats);
  108. }
  109. /**
  110. * (Non Java-doc).
  111. *
  112. * @see org.mobicents.media.MediaSource#getMediaTime();
  113. */
  114. public long getMediaTime() {
  115. return timestamp;
  116. }
  117. /**
  118. * (Non Java-doc).
  119. *
  120. * @see org.mobicents.media.MediaSource#setMediaTime(long timestamp);
  121. */
  122. public void setMediaTime(long timestamp) {
  123. this.initialOffset = timestamp;
  124. }
  125. /**
  126. * (Non Java-doc).
  127. *
  128. * @see org.mobicents.media.MediaSource#getDuration(long duration);
  129. */
  130. public long getDuration() {
  131. return duration;
  132. }
  133. /**
  134. * (Non Java-doc).
  135. *
  136. * @see org.mobicents.media.MediaSource#setDuration(long duration);
  137. */
  138. public void setDuration(long duration) {
  139. this.duration = duration;
  140. }
  141. /**
  142. * Assigns the digital signaling processor of this component.
  143. * The DSP allows to get more output formats.
  144. *
  145. * @param dsp the dsp instance
  146. */
  147. public void setDsp(Processor dsp) {
  148. //assign processor
  149. this.dsp = dsp;
  150. this.rebuildFormats();
  151. }
  152. /**
  153. * Rebuilds the list of supported formats.
  154. */
  155. protected void rebuildFormats() {
  156. //rebuild list of supported formats
  157. if (dsp == null) {
  158. this.initFormats();
  159. return;
  160. }
  161. //add formats wich are results of transcoding
  162. Formats fmts = this.getNativeFormats();
  163. supportedFormats.clean();
  164. int fcount = fmts.size();
  165. for (int i = 0; i < fcount; i++) {
  166. supportedFormats.add(fmts.get(i));
  167. for (Codec c : dsp.getCodecs()) {
  168. if (c.getSupportedInputFormat().matches(fmts.get(i))) {
  169. supportedFormats.add(c.getSupportedOutputFormat());
  170. }
  171. }
  172. }
  173. formats.clean();
  174. formats.addAll(supportedFormats);
  175. dsp.setFormats(formats);
  176. }
  177. /**
  178. * Gets the digital signalling processor associated with this media source
  179. *
  180. * @return DSP instance.
  181. */
  182. public Processor getDsp() {
  183. return this.dsp;
  184. }
  185. /**
  186. * (Non Java-doc.)
  187. *
  188. *
  189. * @see org.mobicents.media.MediaSource#setFormats(org.mobicents.media.server.spi.format.Formats)
  190. */
  191. public void setFormats(Formats formats) throws FormatNotSupportedException {
  192. supportedFormats.intersection(formats, this.formats);
  193. if (dsp != null) {
  194. dsp.setFormats(this.formats);
  195. }
  196. }
  197. /**
  198. * (Non Java-doc.)
  199. *
  200. * @see org.mobicents.media.MediaSource#getFormats()
  201. */
  202. public Formats getFormats() {
  203. return supportedFormats;
  204. }
  205. /**
  206. * (Non Java-doc).
  207. *
  208. * @see org.mobicents.media.MediaSource#start().
  209. */
  210. public synchronized void start() {
  211. //check scheduler
  212. try {
  213. //prevent duplicate starting
  214. if (started) {
  215. return;
  216. }
  217. if (scheduler == null) {
  218. throw new IllegalArgumentException("Scheduler is not assigned");
  219. }
  220. this.txBytes = 0;
  221. this.txPackets = 0;
  222. //reset media time and sequence number
  223. timestamp = this.initialOffset;
  224. this.initialOffset = 0;
  225. sn = 0;
  226. //switch indicator that source has been started
  227. started = true;
  228. //just started component always synchronized as well
  229. this.isSynchronized = true;
  230. //scheduler worker
  231. worker.setDeadLine(scheduler.getClock().getTime() + initialDelay);
  232. scheduler.submit(worker);
  233. //disable last used initial delay
  234. this.initialDelay = 0;
  235. //started!
  236. started();
  237. } catch (Exception e) {
  238. started = false;
  239. failed(e);
  240. }
  241. }
  242. /**
  243. * Restores synchronization
  244. */
  245. public void wakeup() {
  246. synchronized(worker) {
  247. if (!started) {
  248. return;
  249. }
  250. if (!this.isSynchronized) {
  251. this.isSynchronized = true;
  252. worker.setDeadLine(scheduler.getClock().getTime() + 1L);
  253. scheduler.submit(worker);
  254. }
  255. }
  256. }
  257. /**
  258. * (Non Java-doc).
  259. *
  260. * @see org.mobicents.media.MediaSource#stop().
  261. */
  262. public void stop() {
  263. if (started) {
  264. stopped();
  265. }
  266. started = false;
  267. if (worker != null) {
  268. worker.cancel();
  269. }
  270. timestamp = 0;
  271. }
  272. /**
  273. * (Non Java-doc).
  274. *
  275. * @see org.mobicents.media.MediaSource#connect(org.mobicents.media.server.spi.io.Pipe)
  276. */
  277. public void connect(Pipe pipe) {
  278. this.pipe = (PipeImpl) pipe;
  279. this.pipe.source = this;
  280. }
  281. /**
  282. * (Non Java-doc).
  283. *
  284. * @see org.mobicents.media.MediaSource#disconnect(org.mobicents.media.server.spi.io.Pipe)
  285. */
  286. public void disconnect(Pipe pipe) {
  287. if (this.pipe != pipe) {
  288. throw new IllegalArgumentException(pipe + " is not connected");
  289. }
  290. this.pipe.source = null;
  291. this.pipe = null;
  292. }
  293. /**
  294. * (Non Java-doc).
  295. *
  296. * @see org.mobicents.media.MediaSink#isConnected().
  297. */
  298. public boolean isConnected() {
  299. return pipe != null;
  300. }
  301. /**
  302. * (Non Java-doc).
  303. *
  304. * @see org.mobicents.media.MediaSource#isStarted().
  305. */
  306. public boolean isStarted() {
  307. return this.started;
  308. }
  309. /**
  310. * This method must be overriden by concrete media source. T
  311. * he media have to fill buffer with media data and
  312. * attributes.
  313. *
  314. * @param buffer the buffer object for media.
  315. * @param sequenceNumber
  316. * the number of timer ticks from the begining.
  317. */
  318. public abstract Frame evolve(long timestamp);
  319. /**
  320. * Gets the list of formats supported by this component without possible
  321. * transcoding.
  322. *
  323. * @return the list of format descriptors
  324. */
  325. public abstract Formats getNativeFormats();
  326. /**
  327. * Sends notification that media processing has been started.
  328. */
  329. protected void started() {
  330. }
  331. /**
  332. * Sends failure notification.
  333. *
  334. * @param e the exception caused failure.
  335. */
  336. protected void failed(Exception e) {
  337. }
  338. /**
  339. * Sends notification that signal is completed.
  340. *
  341. */
  342. protected void completed() {
  343. this.started = false;
  344. }
  345. /**
  346. * Called when source is stopped by request
  347. *
  348. */
  349. protected void stopped() {
  350. }
  351. /**
  352. * (Non Java-doc).
  353. *
  354. * @see org.mobicents.media.MediaSource#getPacketsReceived()
  355. */
  356. public long getPacketsTransmitted() {
  357. return txPackets;
  358. }
  359. /**
  360. * (Non Java-doc).
  361. *
  362. * @see org.mobicents.media.MediaSource#getBytesTransmitted()
  363. */
  364. public long getBytesTransmitted() {
  365. return txBytes;
  366. }
  367. @Override
  368. public void reset() {
  369. this.txPackets = 0;
  370. this.txBytes = 0;
  371. this.formats.clean();
  372. this.formats.addAll(this.supportedFormats);
  373. }
  374. public String report() {
  375. return stats.toString();
  376. }
  377. /* (non-Javadoc)
  378. * @see org.mobicents.media.MediaSink#getInterface(java.lang.Class)
  379. */
  380. @Override
  381. public <T> T getInterface(Class<T> interfaceType) {
  382. //should we check default?
  383. return null;
  384. }
  385. /**
  386. * Media generator task
  387. */
  388. private class Worker extends Task {
  389. /**
  390. * Creates new instance of task.
  391. *
  392. * @param scheduler the scheduler instance.
  393. */
  394. public Worker(Scheduler scheduler) {
  395. super(scheduler);
  396. }
  397. /**
  398. * (Non Java-doc.)
  399. *
  400. * @see org.mobicents.media.server.scheduler.Task#getPriority()
  401. */
  402. public long getPriority() {
  403. return 0;
  404. }
  405. /**
  406. * (Non Java-doc.)
  407. *
  408. * @see org.mobicents.media.server.scheduler.Task#getDuration()
  409. */
  410. public long getDuration() {
  411. return duration;
  412. }
  413. /**
  414. * (Non Java-doc.)
  415. *
  416. * @see org.mobicents.media.server.scheduler.Task#perform()
  417. */
  418. public long perform() {
  419. //get next frame
  420. Frame frame = evolve(timestamp);
  421. if (frame == null) {
  422. //stop if frame was not generated
  423. isSynchronized = false;
  424. return 0;
  425. }
  426. //mark frame with media time and sequence number
  427. frame.setTimestamp(timestamp);
  428. frame.setSequenceNumber(sn);
  429. //update media time and sequence number for the next frame
  430. timestamp += frame.getDuration();
  431. sn = inc(sn);
  432. //set end_of_media flag if stream has reached the end
  433. if (duration > 0 && timestamp >= duration) {
  434. frame.setEOM(true);
  435. }
  436. //put frame into temporary transmission buffer and remember its duration
  437. long frameDuration = frame.getDuration();
  438. buffer.offer(frame);
  439. stats.rxFormat = frame.getFormat();
  440. //schedule DSP task
  441. transcoder.setDeadLine(scheduler.getClock().getTime() + frameDuration/2);
  442. scheduler.submit(transcoder);
  443. //send notifications about media termination
  444. //and do not resubmit this task again if stream has bee ended
  445. if (frame.isEOM()) {
  446. started = false;
  447. completed();
  448. return -1;
  449. }
  450. //check synchronization
  451. if (frameDuration <= 0) {
  452. //los of synchronization
  453. isSynchronized = false;
  454. return 0;
  455. }
  456. //resubmit this task again
  457. setDeadLine(getDeadLine() + frameDuration);
  458. scheduler.submit(this);
  459. return 0;
  460. }
  461. @Override
  462. public String toString() {
  463. return getName();
  464. }
  465. }
  466. /**
  467. * Implements DSP and transmission tasks.
  468. */
  469. private class Transcoder extends Task {
  470. /**
  471. * Creates new instance of task.
  472. *
  473. * @param scheduler the task scheduler.
  474. */
  475. private Transcoder(Scheduler scheduler) {
  476. super(scheduler);
  477. }
  478. @Override
  479. public long getPriority() {
  480. return 0;
  481. }
  482. @Override
  483. public long getDuration() {
  484. return 0;
  485. }
  486. @Override
  487. public long perform() {
  488. if (buffer.isEmpty()) {
  489. //nothing to do
  490. return 0;
  491. }
  492. //poll frame from temporary buffer
  493. Frame frame = buffer.poll();
  494. //do the transcoding job
  495. if (dsp != null) {
  496. frame = dsp.process(frame);
  497. }
  498. stats.txFormat = frame.getFormat();
  499. //delivering data to the other party.
  500. if (pipe != null) {
  501. pipe.write(frame);
  502. }
  503. //update transmission statistics
  504. txPackets++;
  505. txBytes += frame.getLength();
  506. return 0;
  507. }
  508. }
  509. private class Stats {
  510. protected Format rxFormat;
  511. protected Format txFormat;
  512. @Override
  513. public String toString() {
  514. return "source: " + rxFormat + ", codec: " + txFormat;
  515. }
  516. }
  517. }