PageRenderTime 65ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/media/core/server-impl/src/main/java/org/mobicents/media/server/impl/resource/mediaplayer/mpeg/RTPTrack.java

http://mobicents.googlecode.com/
Java | 445 lines | 282 code | 94 blank | 69 comment | 55 complexity | 2fb93019f050e0693ee44fc895656ebc 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.resource.mediaplayer.mpeg;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.File;
  21. import java.io.FileNotFoundException;
  22. import java.io.IOException;
  23. import java.io.RandomAccessFile;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.Random;
  27. import org.apache.log4j.Logger;
  28. /**
  29. *
  30. * @author amit bhayani
  31. *
  32. */
  33. public abstract class RTPTrack {
  34. private final Logger logger = Logger.getLogger(this.getClass());
  35. private List<RTPLocalPacket> rtPktList = new ArrayList<RTPLocalPacket>();
  36. private TrackBox hintTrackBox = null;
  37. private TrackBox trackBox = null;
  38. private RandomAccessFile hintTrackRAF = null;
  39. private RandomAccessFile trackRAF = null;
  40. private long[] hintSamplesOffSet = null;
  41. private long[] samplesOffSet = null;
  42. private long[] sampleDelta = null;
  43. private int period = 0;
  44. private int[] timeDelta = null;
  45. private volatile int hintSamplesSent = 0;
  46. private volatile int audioSamplesSent = 0;
  47. private volatile long rtpTimeStamp = Math.abs((new Random()).nextInt());
  48. private String sdpText;
  49. private long trackId = -1;
  50. long hintSampleCount = 0;
  51. double npt;
  52. boolean first = true;
  53. public RTPTrack(TrackBox audioTrackBox, TrackBox audioHintTrackBox, File file) throws FileNotFoundException {
  54. long sampleCount = 0;
  55. SampleTableBox hintSampleTableBox = null;
  56. SampleTableBox sampleTableBox = null;
  57. long[] hintChunkOffset = null;
  58. long[] hintSamplesPerChunk = null;
  59. long[] hintEntrySize = null;
  60. long hintSampleSize = 0;
  61. int timeScale = 0;
  62. this.trackBox = audioTrackBox;
  63. this.hintTrackBox = audioHintTrackBox;
  64. this.hintTrackRAF = new RandomAccessFile(file, "r");
  65. this.trackRAF = new RandomAccessFile(file, "r");
  66. npt = (this.hintTrackBox.getMediaBox().getMediaHeaderBox().getDuration()) / (this.hintTrackBox.getMediaBox().getMediaHeaderBox().getTimescale());
  67. // TODO : We are assuming that Hint Track is referencing only one Track, but there could be more.
  68. trackId = this.hintTrackBox.getTrackHeaderBox().getTrackID();
  69. for (Box b : this.hintTrackBox.getUserDataBox().getUserDefinedBoxes()) {
  70. if (b.getType().equals(TrackHintInformation.TYPE_S)) {
  71. this.sdpText = ((TrackHintInformation) b).getRtpTrackSdpHintInformation().getSdpText();
  72. }
  73. }
  74. hintSampleTableBox = this.hintTrackBox.getMediaBox().getMediaInformationBox().getSampleTableBox();
  75. sampleTableBox = this.trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox();
  76. hintChunkOffset = hintSampleTableBox.getChunkOffsetBox().getChunkOffset();
  77. long[] audioChunkOffset = sampleTableBox.getChunkOffsetBox().getChunkOffset();
  78. if (logger.isDebugEnabled()) {
  79. logger.debug("Hint ChunkOffset length = " + hintChunkOffset.length);
  80. logger.debug("ChunkOffset length = " + audioChunkOffset.length);
  81. }
  82. for (SampleEntry sampleEntry : hintSampleTableBox.getSampleDescription().getSampleEntries()) {
  83. if (sampleEntry.getType().equals(RtpHintSampleEntry.TYPE_S)) {
  84. for (Box box : ((RtpHintSampleEntry) sampleEntry).getAdditionaldata()) {
  85. if (box.getType().equals(TimeScaleEntry.TYPE_S)) {
  86. timeScale = ((TimeScaleEntry) box).getTimeScale();
  87. if (logger.isDebugEnabled()) {
  88. logger.debug("timeScale = " + timeScale);
  89. }
  90. }
  91. }
  92. }
  93. }
  94. hintSampleCount = hintSampleTableBox.getSampleSizeBox().getSampleCount();
  95. sampleCount = sampleTableBox.getSampleSizeBox().getSampleCount();
  96. TimeToSampleBox timeToSampleBox = hintSampleTableBox.getTimeToSampleBox();
  97. if (timeToSampleBox.getEntryCount() == 1) {
  98. // this.heartBeat = (int) (this.duration / this.timeScale * 1000) / this.sampleCount;
  99. this.period = (int) (((float) timeToSampleBox.getSampleDelta()[0] / (float) timeScale) * 1000);
  100. } else {
  101. // heart beat different for each sample
  102. int heartBeatArrCount = 0;
  103. this.timeDelta = new int[(int) hintSampleCount];
  104. long[] sampleCountArr = timeToSampleBox.getSampleCount();
  105. long[] sampleDelta = timeToSampleBox.getSampleDelta();
  106. for (int i = 0; i < sampleCountArr.length; i++) {
  107. long temp = sampleCountArr[i];
  108. timeDelta[heartBeatArrCount++] = (int) (((float) sampleDelta[i] / (float) timeScale) * 1000);
  109. for (int j = 1; j < temp; j++) {
  110. timeDelta[heartBeatArrCount++] = (int) sampleDelta[i];
  111. }
  112. }
  113. }
  114. this.hintSamplesOffSet = new long[(int) hintSampleCount];
  115. this.samplesOffSet = new long[(int) sampleCount];
  116. if (logger.isDebugEnabled()) {
  117. logger.debug("Heart Beat = " + this.period);
  118. if (this.period == 0) {
  119. logger.debug("heartBeat zero so use heartBeatArr. Length = " + timeDelta.length);
  120. }
  121. }
  122. hintSamplesPerChunk = new long[hintChunkOffset.length];
  123. long[] audioSamplesPerChunk = new long[audioChunkOffset.length];
  124. // Calculate the Number of Samples for each Chunk for Hint Audio Track
  125. int samplesPerChunkCount = 0;
  126. long[] hintFirstChunk = hintSampleTableBox.getSampleToChunkBox().getFirstChunk();
  127. long[] hintSamplesPerChunkTemp = hintSampleTableBox.getSampleToChunkBox().getSamplesPerChunk();
  128. long samplesAtChunk;
  129. for (int i = 0; i < (hintFirstChunk.length - 1); i++) {
  130. long temp = (hintFirstChunk[i + 1] - hintFirstChunk[i]);
  131. samplesAtChunk = hintSamplesPerChunkTemp[i];
  132. for (int j = 0; j < temp; j++) {
  133. hintSamplesPerChunk[samplesPerChunkCount++] = samplesAtChunk;
  134. }
  135. }
  136. samplesAtChunk = hintSamplesPerChunkTemp[(hintFirstChunk.length - 1)];
  137. for (int j = samplesPerChunkCount; j < hintSamplesPerChunk.length; j++) {
  138. hintSamplesPerChunk[samplesPerChunkCount++] = samplesAtChunk;
  139. }
  140. // Calculate the Number of Samples for each Chunk for Audio Track
  141. samplesPerChunkCount = 0;
  142. long[] audioFirstChunk = sampleTableBox.getSampleToChunkBox().getFirstChunk();
  143. long[] audioSamplesPerChunkTemp = sampleTableBox.getSampleToChunkBox().getSamplesPerChunk();
  144. for (int i = 0; i < (audioFirstChunk.length - 1); i++) {
  145. long temp = (audioFirstChunk[i + 1] - audioFirstChunk[i]);
  146. samplesAtChunk = audioSamplesPerChunkTemp[i];
  147. for (int j = 0; j < temp; j++) {
  148. audioSamplesPerChunk[samplesPerChunkCount++] = samplesAtChunk;
  149. }
  150. }
  151. samplesAtChunk = audioSamplesPerChunkTemp[(audioFirstChunk.length - 1)];
  152. for (int j = samplesPerChunkCount; j < audioSamplesPerChunk.length; j++) {
  153. audioSamplesPerChunk[samplesPerChunkCount++] = samplesAtChunk;
  154. }
  155. // This is debug
  156. if (logger.isDebugEnabled()) {
  157. int tempCnt = 0;
  158. for (int i = 0; i < hintSamplesPerChunk.length; i++) {
  159. tempCnt += hintSamplesPerChunk[i];
  160. }
  161. logger.debug("Total sample count for Hint Track that should match with SampleSizeBox sampleCount(" + hintSampleCount + ") = " + tempCnt);
  162. tempCnt = 0;
  163. for (int i = 0; i < audioSamplesPerChunk.length; i++) {
  164. tempCnt += audioSamplesPerChunk[i];
  165. }
  166. logger.debug("Total sample count for Track that should match with SampleSizeBox sampleCount(" + sampleTableBox.getSampleSizeBox().getSampleCount() + ") = " + tempCnt);
  167. }// Debug ends
  168. // Calculate the OffSet for each Sample for Hint Track
  169. hintSampleSize = hintSampleTableBox.getSampleSizeBox().getSampleSize();
  170. hintEntrySize = hintSampleTableBox.getSampleSizeBox().getEntrySize();
  171. int samplesOffSetCount = 0;
  172. for (int i = 0; i < hintChunkOffset.length; i++) {
  173. long chunkOffForChunk = hintChunkOffset[i];
  174. long samplesInThisChunk = hintSamplesPerChunk[i];
  175. hintSamplesOffSet[samplesOffSetCount++] = chunkOffForChunk;
  176. for (int j = 1; j < samplesInThisChunk; j++) {
  177. if (hintSampleSize == 0) {
  178. hintSamplesOffSet[samplesOffSetCount++] = hintSamplesOffSet[samplesOffSetCount - 2] + hintEntrySize[samplesOffSetCount - 2];
  179. } else {
  180. hintSamplesOffSet[samplesOffSetCount++] = (chunkOffForChunk + hintSampleSize * j);
  181. }
  182. }
  183. }
  184. // debug
  185. // if (logger.isDebugEnabled()) {
  186. // logger.info("Each Sample Off Set for Hint Track ");
  187. // StringBuffer b = new StringBuffer();
  188. // for (int i = 0; i < hintSamplesOffSet.length; i++) {
  189. // b.append(hintSamplesOffSet[i]).append(",");
  190. // }
  191. // logger.debug(b.toString());
  192. // }// debug ends
  193. // Calculate the OffSet for each Sample for Audio Track
  194. long audioSampleSize = sampleTableBox.getSampleSizeBox().getSampleSize();
  195. long[] audioEntrySize = sampleTableBox.getSampleSizeBox().getEntrySize();
  196. samplesOffSetCount = 0;
  197. for (int i = 0; i < audioChunkOffset.length; i++) {
  198. long chunkOffForChunk = audioChunkOffset[i];
  199. long samplesInThisChunk = audioSamplesPerChunk[i];
  200. samplesOffSet[samplesOffSetCount++] = chunkOffForChunk;
  201. for (int j = 1; j < samplesInThisChunk; j++) {
  202. if (audioSampleSize == 0) {
  203. samplesOffSet[samplesOffSetCount++] = samplesOffSet[samplesOffSetCount - 2] + audioEntrySize[samplesOffSetCount - 2];
  204. } else {
  205. samplesOffSet[samplesOffSetCount++] = (chunkOffForChunk + audioSampleSize * j);
  206. }
  207. }
  208. }
  209. // debug. Let us keep it till 3000 only else it will eat away all memory
  210. // if (logger.isDebugEnabled() && (samplesOffSet.length < 3000)) {
  211. // logger.info("Each Sample Off Set for Track ");
  212. // StringBuffer b = new StringBuffer();
  213. // for (int i = 0; i < this.samplesOffSet.length; i++) {
  214. // b.append(this.samplesOffSet[i]).append(",");
  215. // }
  216. // logger.debug(b.toString());
  217. // }// debug ends
  218. // Calculate SampleDelta for each sample
  219. sampleDelta = new long[(int) hintSampleCount];
  220. long[] hintSampleCountArr = hintSampleTableBox.getTimeToSampleBox().getSampleCount();
  221. long[] hintSampleDeltaArr = hintSampleTableBox.getTimeToSampleBox().getSampleDelta();
  222. samplesOffSetCount = 0;
  223. for (int i = 0; i < hintSampleCountArr.length; i++) {
  224. long smplCnt = hintSampleCountArr[i];
  225. long smplDelts = hintSampleDeltaArr[i];
  226. for (int j = 0; j < (int) smplCnt; j++) {
  227. sampleDelta[samplesOffSetCount++] = smplDelts;
  228. }
  229. }
  230. }
  231. public void setRtpTime(long rtpTime) {
  232. this.rtpTimeStamp = rtpTime;
  233. }
  234. public RTPSample process() throws IOException {
  235. RTPSample rtpSample = null;
  236. if (this.hintSampleCount == this.hintSamplesSent) {
  237. return null;
  238. }
  239. rtpSample = new RTPSample();
  240. RTPLocalPacket rtpPacket = null;
  241. long hintSampleOffset = hintSamplesOffSet[hintSamplesSent];
  242. long audioSampleOffSet = samplesOffSet[audioSamplesSent];
  243. this.hintTrackRAF.seek(hintSampleOffset);
  244. int packetCount = (this.hintTrackRAF.read() << 8 | this.hintTrackRAF.read());
  245. rtpSample.setPacketCount(packetCount);
  246. if (packetCount > 0) {
  247. this.audioSamplesSent++;
  248. }
  249. // reserved
  250. this.hintTrackRAF.skipBytes(2);
  251. if (first) {
  252. first = false;
  253. } else {
  254. rtpTimeStamp += sampleDelta[hintSamplesSent];
  255. }
  256. for (int i = 0; i < packetCount; i++) {
  257. rtpPacket = new RTPLocalPacket();
  258. rtpPacket.load(this.hintTrackRAF);
  259. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  260. // Now load the Payload
  261. for (RTPConstructor rtpCons : rtpPacket.RTPConstructorList()) {
  262. switch (rtpCons.getConstructorType()) {
  263. case RTPNoOpConstructor.TYPE:
  264. // TODO : Anything to do here?
  265. break;
  266. case RTPImmediateConstructor.TYPE:
  267. byte[] data = ((RTPImmediateConstructor) rtpCons).getData();
  268. bos.write(data, 0, data.length);
  269. break;
  270. case RTPSampleConstructor.TYPE:
  271. RTPSampleConstructor rtpSampCons = (RTPSampleConstructor) rtpCons;
  272. // TODO : Can we avoid creating the byte[] again here?
  273. byte[] rtpPaylod = null;
  274. // TODO : Verify if this is correct?
  275. /*
  276. * From page 76 of ISO/IEC 14496-12
  277. *
  278. * For hint tracks where the media is sent ??in the clearÂ’, the sample entry then specifies the
  279. * bytes to copy from the media track, by giving the sample number, data offset, and length to copy.
  280. * The track reference may index into the table of track references (a strictly positive value),
  281. * name the hint track itself (-1), or the only associated media track (0). (The value zero is
  282. * therefore equivalent to the value 1.)
  283. *
  284. * I couldn't make if bellow is what they are saying? Why do they talk so cryptic?
  285. */
  286. if (rtpSampCons.getTrackRefIndex() == -1) {
  287. this.hintTrackRAF.seek((hintSampleOffset + rtpSampCons.getSampleOffSet()));
  288. rtpPaylod = new byte[rtpSampCons.getLength()];
  289. this.hintTrackRAF.read(rtpPaylod, 0, rtpSampCons.getLength());
  290. } else {
  291. // TODO : Should OffSet be added even if we are referring to non rtp mdat?
  292. this.trackRAF.seek(audioSampleOffSet + rtpSampCons.getSampleOffSet());
  293. rtpPaylod = new byte[rtpSampCons.getLength()];
  294. int read = this.trackRAF.read(rtpPaylod, 0, rtpSampCons.getLength());
  295. }
  296. bos.write(rtpPaylod, 0, rtpPaylod.length);
  297. break;
  298. case RTPSampleDescriptionConstructor.TYPE:
  299. // TODO : What here?
  300. break;
  301. }
  302. }
  303. rtpPacket.setPayload(bos.toByteArray());
  304. rtpPacket.setRtpTimestamp(this.rtpTimeStamp);
  305. rtpSample.addRtpLocalPackets(rtpPacket);
  306. }// for
  307. if (this.period == 0.0) {
  308. rtpSample.setSamplePeriod(this.timeDelta[hintSamplesSent]);
  309. } else {
  310. rtpSample.setSamplePeriod(this.period);
  311. }
  312. hintSamplesSent++;
  313. // FIXME Do we care for extraByte?
  314. return rtpSample;
  315. }
  316. public void close() {
  317. try {
  318. if (this.hintTrackRAF != null) {
  319. this.hintTrackRAF.close();
  320. }
  321. if (this.trackRAF != null) {
  322. this.trackRAF.close();
  323. }
  324. } catch (IOException e) {
  325. // TODO Auto-generated catch block
  326. e.printStackTrace();
  327. }
  328. this.hintTrackRAF = null;
  329. this.trackRAF = null;
  330. this.hintTrackBox = null;
  331. this.trackBox = null;
  332. this.hintSamplesOffSet = null;
  333. this.samplesOffSet = null;
  334. this.timeDelta = null;
  335. }
  336. public String getSdpText() {
  337. return sdpText;
  338. }
  339. public long getTrackId() {
  340. return this.trackId;
  341. }
  342. public float getPacketPeriod() {
  343. return this.period;
  344. }
  345. public float getHeartBeat() {
  346. return this.period;
  347. }
  348. public double getNPT() {
  349. return this.npt;
  350. }
  351. }