mp4broadcaster.cpp
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:11k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is MPEG4IP.
  13.  * 
  14.  * The Initial Developer of the Original Code is Cisco Systems Inc.
  15.  * Portions created by Cisco Systems Inc. are
  16.  * Copyright (C) Cisco Systems Inc. 2001.  All Rights Reserved.
  17.  * 
  18.  * Contributor(s): 
  19.  * Dave Mackie dmackie@cisco.com
  20.  */
  21. #include "mpeg4ip.h"
  22. #include <arpa/inet.h>
  23. #include "mp4.h"
  24. // forward declarations
  25. static bool AssembleSdp(
  26. MP4FileHandle mp4File, 
  27. const char* sdpFileName,
  28. const char* destIpAddress);
  29. static bool InitSockets(
  30. u_int32_t numSockets, 
  31. int* pSockets, 
  32. const char* destIpAddress);
  33. static u_int64_t GetUsecTime();
  34. // globals
  35. char* ProgName;
  36. u_int16_t UdpBasePort = 20000;
  37. u_int32_t MulticastTtl = 2; // increase value if necessary
  38. const u_int32_t SecsBetween1900And1970 = 2208988800U;
  39. // the main show
  40. int main(int argc, char** argv)
  41. {
  42. // since we're a test program
  43. // keep command line processing to a minimum
  44. // and assume some defaults
  45. ProgName = argv[0];
  46. char* sdpFileName = "./mp4broadcaster.sdp";
  47. char* destIpAddress = "224.1.2.3";
  48. if (argc < 2) {
  49. fprintf(stderr, "Usage: %s <file>n", ProgName);
  50. exit(1);
  51. }
  52. char* mp4FileName = argv[1];
  53. u_int32_t verbosity = 
  54. MP4_DETAILS_ERROR | MP4_DETAILS_READ 
  55. | MP4_DETAILS_SAMPLE | MP4_DETAILS_HINT;
  56. // open the mp4 file
  57. MP4FileHandle mp4File = MP4Read(mp4FileName, verbosity);
  58. if (mp4File == MP4_INVALID_FILE_HANDLE) {
  59. // library will print an error message
  60. exit(1);
  61. }
  62. // check for hint tracks
  63. u_int32_t numHintTracks = 
  64. MP4GetNumberOfTracks(mp4File, MP4_HINT_TRACK_TYPE);
  65. if (numHintTracks == 0) {
  66. fprintf(stderr, "%s: File %s does not contain any hint tracks", 
  67. ProgName, mp4FileName);
  68. exit(2);
  69. }
  70. // assemble and write out sdp file
  71. AssembleSdp(mp4File, sdpFileName, destIpAddress);
  72. // create two UDP sockets for each track that will be streamed
  73. // one for the RTP media, one for the RTCP control
  74. int* udpSockets = new int[numHintTracks * 2]; 
  75. if (!InitSockets(numHintTracks, udpSockets, destIpAddress)) {
  76. fprintf(stderr, "%s: Couldn't create UDP socketsn",
  77. ProgName);
  78. exit(3);
  79. }
  80. // initialize RTCP packet
  81. u_int32_t ssrc = random();
  82. char rtcpCName[256]; 
  83. char* username = getlogin();
  84. char hostname[256];
  85. gethostname(hostname, sizeof(hostname));
  86. snprintf(rtcpCName, sizeof(rtcpCName), "%s@%s", username, hostname);
  87. u_int8_t rtcpPacket[512];
  88. u_int16_t rtcpPacketLength = 38;
  89. // RTCP SR
  90. rtcpPacket[0] = 0x80;
  91. rtcpPacket[1] = 0xC8;
  92. rtcpPacket[2] = 0x00;
  93. rtcpPacket[3] = 0x06;
  94. *(u_int32_t*)&rtcpPacket[4] = htonl(ssrc);
  95. // RTCP SDES CNAME
  96. rtcpPacket[28] = 0x80;
  97. rtcpPacket[29] = 0xCA;
  98. *(u_int32_t*)&rtcpPacket[32] = htonl(ssrc);
  99. rtcpPacket[36] = 0x01;
  100. rtcpPacket[37] = strlen(rtcpCName);
  101. strcpy((char*)&rtcpPacket[38], rtcpCName);
  102. rtcpPacketLength += strlen(rtcpCName) + 1;
  103. // pad with zero's to 32 bit boundary
  104. while (rtcpPacketLength % 4 != 0) {
  105. rtcpPacket[rtcpPacketLength++] = 0; 
  106. }
  107. *(u_int16_t*)&rtcpPacket[30] = ntohs((rtcpPacketLength - 32) / 4);
  108. // initialize per track variables that we will use in main loop
  109. MP4TrackId* hintTrackIds = new MP4TrackId[numHintTracks]; 
  110. MP4SampleId* nextHintIds = new MP4SampleId[numHintTracks]; 
  111. MP4SampleId* maxHintIds = new MP4SampleId[numHintTracks]; 
  112. u_int64_t* nextHintTimes = new MP4Timestamp[numHintTracks];
  113. u_int32_t* packetsSent = new u_int32_t[numHintTracks];
  114. u_int32_t* bytesSent = new u_int32_t[numHintTracks];
  115. u_int32_t i;
  116. for (i = 0; i < numHintTracks; i++) {
  117. hintTrackIds[i] = MP4FindTrackId(mp4File, i, MP4_HINT_TRACK_TYPE);
  118. nextHintIds[i] = 1;
  119. maxHintIds[i] = MP4GetTrackNumberOfSamples(mp4File, hintTrackIds[i]);
  120. nextHintTimes[i] = (u_int64_t)-1;
  121. packetsSent[i] = 0;
  122. bytesSent[i] = 0;
  123. }
  124. // remember the starting time
  125. u_int64_t start = GetUsecTime();
  126. u_int64_t lastSR = 0;
  127. // main loop to stream data
  128. while (true) {
  129. u_int32_t nextTrackIndex = (u_int32_t)-1;
  130. u_int64_t nextTime = (u_int64_t)-1;
  131. // find the next hint to send
  132. for (i = 0; i < numHintTracks; i++) {
  133. if (nextHintIds[i] > maxHintIds[i]) {
  134. // have finished this track
  135. continue;
  136. }
  137. // need to get the time of the next hint
  138. if (nextHintTimes[i] == (u_int64_t)-1) {
  139. MP4Timestamp hintTime =
  140. MP4GetSampleTime(mp4File, hintTrackIds[i], nextHintIds[i]);
  141. nextHintTimes[i] = 
  142. MP4ConvertFromTrackTimestamp(mp4File, hintTrackIds[i],
  143. hintTime, MP4_USECS_TIME_SCALE);
  144. }
  145. // check if this track's next hint is the earliest yet
  146. if (nextHintTimes[i] > nextTime) {
  147. continue;
  148. }
  149. // make this our current choice for the next hint
  150. nextTime = nextHintTimes[i];
  151. nextTrackIndex = i;
  152. }
  153. // check exit condition, i.e all hints for all tracks have been used
  154. if (nextTrackIndex == (u_int32_t)-1) {
  155. break;
  156. }
  157. // wait until the correct time to send next hint
  158. // we assume we're not going to fall behind for testing purposes
  159. // in a real application some skipping of samples might be needed
  160. u_int64_t now = GetUsecTime();
  161. int64_t waitTime = (start + nextTime) - now;
  162. if (waitTime > 0) {
  163. usleep(waitTime);
  164. }
  165. now = GetUsecTime();
  166. // emit RTCP Sender Reports every 5 seconds for all media streams
  167. // not quite what a real app would do, but close enough for testing
  168. if (now - lastSR >= 5000000) {
  169. for (i = 0; i < numHintTracks; i++) {
  170. now = GetUsecTime();
  171. u_int32_t ntpSecs =
  172. (now / MP4_USECS_TIME_SCALE) + SecsBetween1900And1970;
  173. *(u_int32_t*)&rtcpPacket[8] = 
  174. htonl(ntpSecs);
  175. u_int32_t usecs = now % MP4_USECS_TIME_SCALE;
  176. u_int32_t ntpUSecs =
  177. (usecs << 12) + (usecs << 8) - ((usecs * 3650) >> 6); 
  178. *(u_int32_t*)&rtcpPacket[12] = 
  179. htonl(ntpUSecs);
  180. MP4Timestamp rtpStart =
  181. MP4GetRtpTimestampStart(mp4File, hintTrackIds[i]);
  182. MP4Timestamp rtpOffset =
  183. MP4ConvertToTrackTimestamp(mp4File, hintTrackIds[i],
  184. now - start, MP4_USECS_TIME_SCALE);
  185. *(u_int32_t*)&rtcpPacket[16] =
  186.  htonl(rtpStart + rtpOffset); 
  187. *(u_int32_t*)&rtcpPacket[20] =
  188. htonl(packetsSent[i]);
  189. *(u_int32_t*)&rtcpPacket[24] =
  190. htonl(bytesSent[i]);
  191. send(udpSockets[i * 2 + 1], rtcpPacket, rtcpPacketLength, 0);
  192. }
  193. lastSR = now;
  194. }
  195. // send all the packets for this hint
  196. // since this is just a test program 
  197. // we don't attempt to smooth out the transmission of the packets
  198. u_int16_t numPackets;
  199. MP4ReadRtpHint(
  200. mp4File, 
  201. hintTrackIds[nextTrackIndex], 
  202. nextHintIds[nextTrackIndex],
  203. &numPackets);
  204. // move along in this hint track
  205. nextHintIds[nextTrackIndex]++;
  206. nextHintTimes[nextTrackIndex] = (u_int64_t)-1; 
  207. u_int16_t packetIndex;
  208. for (packetIndex = 0; packetIndex < numPackets; packetIndex++) {
  209. u_int8_t* pPacket = NULL;
  210. u_int32_t packetSize;
  211. // get the packet from the library
  212. MP4ReadRtpPacket(
  213. mp4File, 
  214. hintTrackIds[nextTrackIndex], 
  215. packetIndex,
  216. &pPacket,
  217. &packetSize,
  218. ssrc);
  219. if (pPacket == NULL) {
  220. // error, but forge on
  221. continue;
  222. }
  223. // send it out via UDP
  224. send(udpSockets[nextTrackIndex * 2], pPacket, packetSize, 0);
  225. // free packet buffer
  226. free(pPacket);
  227. bytesSent[nextTrackIndex] += packetSize - 12;
  228. }
  229. packetsSent[nextTrackIndex] += numPackets;
  230. }
  231. // main loop finished
  232. // close the UDP sockets
  233. for (i = 0; i < numHintTracks; i++) {
  234. close(udpSockets[i]);
  235. }
  236. // close mp4 file
  237. MP4Close(mp4File);
  238. // free up memory
  239. delete [] hintTrackIds;
  240. delete [] nextHintIds;
  241. delete [] maxHintIds;
  242. delete [] nextHintTimes;
  243. delete [] packetsSent;
  244. delete [] bytesSent;
  245. exit(0);
  246. }
  247. static bool AssembleSdp(
  248. MP4FileHandle mp4File, 
  249. const char* sdpFileName,
  250. const char* destIpAddress)
  251. {
  252. // open the destination sdp file
  253. FILE* sdpFile = fopen(sdpFileName, "w");
  254. if (sdpFile == NULL) {
  255. fprintf(stderr, 
  256. "%s: couldn't open sdp file %sn",
  257. ProgName, sdpFileName);
  258. return false;
  259. }
  260. // add required header fields
  261. fprintf(sdpFile,
  262. "v=01512"
  263. "o=- 1 1 IN IP4 127.0.0.11512"
  264. "s=mp4broadcaster1512"
  265. "e=NONE1512"
  266. "c=IN IP4 %s/%u1512"
  267. "b=RR:01512",
  268. destIpAddress,
  269. MulticastTtl);
  270. // add session level info from mp4 file
  271. fprintf(sdpFile, "%s", 
  272. MP4GetSessionSdp(mp4File));
  273. // add per track info
  274. u_int32_t numHintTracks = 
  275. MP4GetNumberOfTracks(mp4File, MP4_HINT_TRACK_TYPE);
  276. for (u_int32_t i = 0; i < numHintTracks; i++) {
  277. MP4TrackId hintTrackId = 
  278. MP4FindTrackId(mp4File, i, MP4_HINT_TRACK_TYPE);
  279. if (hintTrackId == MP4_INVALID_TRACK_ID) {
  280. continue;
  281. }
  282. // get track sdp info from mp4 file
  283. const char* mediaSdp =
  284. MP4GetHintTrackSdp(mp4File, hintTrackId);
  285. // since we're going to broadcast instead of use RTSP for on-demand
  286. // we need to fill in the UDP port numbers for the media
  287. const char* portZero = strchr(mediaSdp, '0');
  288. if (portZero == NULL) {
  289. continue; // mal-formed sdp
  290. }
  291. fprintf(sdpFile, "%.*s%u%s",
  292. portZero - mediaSdp,
  293. mediaSdp,
  294. UdpBasePort + (i * 2),
  295. portZero + 1);
  296. }
  297. fclose(sdpFile);
  298. return true;
  299. }
  300. static bool InitSockets(
  301. u_int32_t numSocketPairs, 
  302. int* pSockets, 
  303. const char* destIpAddress)
  304. {
  305. u_int32_t i;
  306. for (i = 0; i < numSocketPairs * 2; i++) {
  307. // create the socket
  308. pSockets[i] = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  309. if (pSockets[i] < 0) {
  310. return false;
  311. }
  312. // allow local listeners to multicast
  313. int reuse = 1;
  314. setsockopt(pSockets[i], SOL_SOCKET, SO_REUSEADDR,
  315. &reuse, sizeof(reuse));
  316. #ifdef SO_REUSEPORT
  317. // not all implementations have this option
  318. setsockopt(pSockets[i], SOL_SOCKET, SO_REUSEPORT,
  319. &reuse, sizeof(reuse));
  320. #endif
  321. // get a local source address
  322. struct sockaddr_in sin;
  323. sin.sin_family = AF_INET;
  324. sin.sin_port = 0;
  325. sin.sin_addr.s_addr = INADDR_ANY;
  326. if (bind(pSockets[i], (struct sockaddr*)&sin, sizeof(sin))) {
  327. return false;
  328. }
  329. // bind to the destination address
  330. sin.sin_port = htons(UdpBasePort + i);
  331. inet_aton(destIpAddress, &sin.sin_addr);
  332. if (connect(pSockets[i], (struct sockaddr*)&sin, sizeof(sin)) < 0) {
  333. return false;
  334. }
  335. // set the multicast time to live
  336. setsockopt(pSockets[i], IPPROTO_IP, IP_MULTICAST_TTL,
  337. &MulticastTtl, sizeof(MulticastTtl));
  338. }
  339. return true;
  340. }
  341. // get absolute time expressed in microseconds
  342. static u_int64_t GetUsecTime()
  343. {
  344. struct timeval tv;
  345. gettimeofday(&tv, NULL);
  346. return (tv.tv_sec * 1000000) + tv.tv_usec;
  347. }