PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/PeerCastStation/PeerCastStation.FLV/RTMP/RTMPSourceStream.cs

https://github.com/kumaryu/peercaststation
C# | 860 lines | 789 code | 65 blank | 6 comment | 48 complexity | fec4be73b392306d347d7e7c551360fd MD5 | raw file
  1. using System;
  2. using System.Linq;
  3. using System.IO;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using PeerCastStation.Core;
  10. namespace PeerCastStation.FLV.RTMP
  11. {
  12. public class RTMPSourceStreamFactory
  13. : SourceStreamFactoryBase
  14. {
  15. public RTMPSourceStreamFactory(PeerCast peercast)
  16. : base(peercast)
  17. {
  18. }
  19. public override string Name {
  20. get { return "RTMP Source"; }
  21. }
  22. public override string Scheme {
  23. get { return "rtmp"; }
  24. }
  25. public override SourceStreamType Type {
  26. get { return SourceStreamType.Broadcast; }
  27. }
  28. public override Uri DefaultUri {
  29. get { return new Uri("rtmp://localhost/live/livestream"); }
  30. }
  31. public override bool IsContentReaderRequired {
  32. get { return false; }
  33. }
  34. public override ISourceStream Create(Channel channel, Uri source, IContentReader reader)
  35. {
  36. return new RTMPSourceStream(PeerCast, channel, source);
  37. }
  38. }
  39. public class RTMPSourceConnection
  40. : SourceConnectionBase
  41. {
  42. public RTMPSourceConnection(PeerCast peercast, Channel channel, Uri source_uri, bool use_content_bitrate)
  43. : base(peercast, channel, source_uri)
  44. {
  45. IContentSink sink = new ChannelContentSink(channel, use_content_bitrate);
  46. sink =
  47. System.Text.RegularExpressions.Regex.Matches(source_uri.Query, @"(&|\?)([^&=]+)=([^&=]+)")
  48. .Cast<System.Text.RegularExpressions.Match>()
  49. .Where(param => Uri.UnescapeDataString(param.Groups[2].Value).ToLowerInvariant()=="filters")
  50. .SelectMany(param => Uri.UnescapeDataString(param.Groups[3].Value).Split(','))
  51. .Select(name => PeerCast.ContentFilters.FirstOrDefault(filter => filter.Name.ToLowerInvariant()==name.ToLowerInvariant()))
  52. .Where(filter => filter!=null)
  53. .Aggregate(sink, (r,filter) => filter.Activate(r));
  54. this.flvBuffer = new FLVContentBuffer(channel, new AsynchronousContentSink(sink));
  55. this.useContentBitrate = use_content_bitrate;
  56. }
  57. private class ConnectionStoppedExcception : ApplicationException {}
  58. private FLVContentBuffer flvBuffer;
  59. private bool useContentBitrate;
  60. public override ConnectionInfo GetConnectionInfo()
  61. {
  62. ConnectionStatus status;
  63. switch (state) {
  64. case ConnectionState.Waiting: status = ConnectionStatus.Connecting; break;
  65. case ConnectionState.Connected: status = ConnectionStatus.Connecting; break;
  66. case ConnectionState.Receiving: status = ConnectionStatus.Connected; break;
  67. case ConnectionState.Error: status = ConnectionStatus.Error; break;
  68. default: status = ConnectionStatus.Idle; break;
  69. }
  70. IPEndPoint endpoint = null;
  71. if (connection!=null) {
  72. endpoint = connection.RemoteEndPoint;
  73. }
  74. return new ConnectionInfoBuilder {
  75. ProtocolName = "RTMP Source",
  76. Type = ConnectionType.Source,
  77. Status = status,
  78. RemoteName = SourceUri.ToString(),
  79. RemoteEndPoint = endpoint,
  80. RemoteHostStatus = (endpoint!=null && endpoint.Address.IsSiteLocal()) ? RemoteHostStatus.Local : RemoteHostStatus.None,
  81. ContentPosition = flvBuffer.Position,
  82. RecvRate = RecvRate,
  83. SendRate = SendRate,
  84. AgentName = clientName,
  85. }.Build();
  86. }
  87. private enum ConnectionState {
  88. Waiting,
  89. Connected,
  90. Receiving,
  91. Error,
  92. Closed,
  93. };
  94. private ConnectionState state = ConnectionState.Waiting;
  95. private string clientName = "";
  96. private IEnumerable<IPEndPoint> GetBindAddresses(Uri uri)
  97. {
  98. IEnumerable<IPAddress> addresses;
  99. if (uri.HostNameType==UriHostNameType.IPv4 ||
  100. uri.HostNameType==UriHostNameType.IPv6) {
  101. addresses = new IPAddress[] { IPAddress.Parse(uri.Host) };
  102. }
  103. else {
  104. try {
  105. addresses = Dns.GetHostAddresses(uri.DnsSafeHost);
  106. }
  107. catch (SocketException) {
  108. return Enumerable.Empty<IPEndPoint>();
  109. }
  110. }
  111. return addresses.Select(addr => new IPEndPoint(addr, uri.Port<0 ? 1935 : uri.Port));
  112. }
  113. protected override async Task<SourceConnectionClient> DoConnect(Uri source, CancellationToken cancellationToken)
  114. {
  115. TcpClient client = null;
  116. var bind_addr = GetBindAddresses(source);
  117. if (bind_addr.Count()==0) {
  118. this.state = ConnectionState.Error;
  119. throw new BindErrorException(String.Format("Cannot resolve bind address: {0}", source.DnsSafeHost));
  120. }
  121. var listeners = bind_addr.Select(addr => {
  122. var listener = new TcpListener(addr);
  123. if (addr.AddressFamily==AddressFamily.InterNetworkV6) {
  124. listener.Server.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, true);
  125. }
  126. return listener;
  127. }).ToArray();
  128. try {
  129. var cancel_task = cancellationToken.CreateCancelTask<TcpClient>();
  130. var tasks = listeners.Select(listener => {
  131. listener.Start(1);
  132. Logger.Debug("Listening on {0}", listener.LocalEndpoint);
  133. return listener.AcceptTcpClientAsync();
  134. }).Concat(Enumerable.Repeat(cancel_task, 1)).ToArray();
  135. var result = await Task.WhenAny(tasks).ConfigureAwait(false);
  136. if (!result.IsCanceled) {
  137. client = result.Result;
  138. Logger.Debug("Client accepted");
  139. }
  140. else {
  141. Logger.Debug("Listen cancelled");
  142. }
  143. }
  144. catch (SocketException) {
  145. this.state = ConnectionState.Error;
  146. throw new BindErrorException(String.Format("Cannot bind address: {0}", bind_addr));
  147. }
  148. finally {
  149. foreach (var listener in listeners) {
  150. listener.Stop();
  151. }
  152. }
  153. if (client!=null) {
  154. var c = new SourceConnectionClient(client);
  155. c.Stream.CloseTimeout = 0;
  156. return c;
  157. }
  158. else {
  159. return null;
  160. }
  161. }
  162. protected override async Task DoProcess(CancellationToken cancellationToken)
  163. {
  164. this.state = ConnectionState.Waiting;
  165. try {
  166. await Handshake(cancellationToken).ConfigureAwait(false);
  167. await ProcessRTMPMessages(cancellationToken).ConfigureAwait(false);
  168. this.state = ConnectionState.Closed;
  169. }
  170. catch (IOException e) {
  171. Logger.Error(e);
  172. Stop(StopReason.ConnectionError);
  173. this.state = ConnectionState.Error;
  174. }
  175. catch (ConnectionStoppedExcception) {
  176. this.state = ConnectionState.Closed;
  177. }
  178. }
  179. protected async Task ProcessRTMPMessages(CancellationToken cancel_token)
  180. {
  181. this.state = ConnectionState.Connected;
  182. var messages = new Queue<RTMPMessage>();
  183. while (!cancel_token.IsCancellationRequested &&
  184. await RecvMessage(messages, cancel_token).ConfigureAwait(false)) {
  185. await ProcessMessages(messages, cancel_token).ConfigureAwait(false);
  186. messages.Clear();
  187. }
  188. }
  189. protected override void DoPost(Host from, Atom packet)
  190. {
  191. //Do nothing
  192. }
  193. System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
  194. private async Task<bool> Handshake(CancellationToken cancel_token)
  195. {
  196. Logger.Debug("Handshake start");
  197. var rand = new Random();
  198. var c0 = await RecvAsync(1, cancel_token).ConfigureAwait(false);
  199. await connection.Stream.WriteByteAsync(0x03, cancel_token).ConfigureAwait(false);
  200. var s1vec = new byte[1528];
  201. rand.NextBytes(s1vec);
  202. await SendAsync(writer => {
  203. writer.Write(0);
  204. writer.Write(0);
  205. writer.Write(s1vec);
  206. }, cancel_token).ConfigureAwait(false);
  207. using (var reader=await RecvAsync(1536, cancel_token).ConfigureAwait(false)) {
  208. await SendAsync(writer => {
  209. writer.Write(reader.ReadInt32());
  210. writer.Write(reader.ReadInt32());
  211. writer.Write(reader.ReadBytes(1528));
  212. }, cancel_token).ConfigureAwait(false);
  213. }
  214. using (var reader=await RecvAsync(1536, cancel_token).ConfigureAwait(false)) {
  215. reader.ReadInt32();
  216. reader.ReadInt32();
  217. if (!s1vec.SequenceEqual(reader.ReadBytes(1528))) {
  218. Logger.Debug("Handshake failed");
  219. return false;
  220. }
  221. }
  222. timer.Reset();
  223. timer.Start();
  224. Logger.Debug("Handshake succeeded");
  225. return true;
  226. }
  227. private long Now {
  228. get { return timer.ElapsedMilliseconds; }
  229. }
  230. int nextClientId = 1;
  231. int nextStreamId = 1;
  232. int objectEncoding = 0;
  233. int sendChunkSize = 128;
  234. int recvChunkSize = 128;
  235. long sendWindowSize = 0x7FFFFFFF;
  236. PeerBandwidthLimitType sendWindowLimitType = PeerBandwidthLimitType.Hard;
  237. long recvWindowSize = 0x7FFFFFFF;
  238. long receivedSize = 0;
  239. long sequenceNumber = 0;
  240. private async Task<int> RecvStream(byte[] buf, int offset, int len, CancellationToken cancel_token)
  241. {
  242. if (len+receivedSize>=recvWindowSize) {
  243. var len1 = (int)(recvWindowSize-receivedSize);
  244. await connection.Stream.ReadBytesAsync(buf, offset, len1, cancel_token).ConfigureAwait(false);
  245. receivedSize += len1;
  246. sequenceNumber += len1;
  247. await SendMessage(2, new AckMessage(this.Now, 0, sequenceNumber), cancel_token).ConfigureAwait(false);
  248. var len2 = len - len1;
  249. await connection.Stream.ReadBytesAsync(buf, offset+len1, len2, cancel_token).ConfigureAwait(false);
  250. receivedSize = len2; //reset
  251. sequenceNumber += len2;
  252. }
  253. else {
  254. await connection.Stream.ReadBytesAsync(buf, offset, len, cancel_token).ConfigureAwait(false);
  255. receivedSize += len;
  256. sequenceNumber += len;
  257. }
  258. return len;
  259. }
  260. private async Task<byte[]> RecvStream(int len, CancellationToken cancel_token)
  261. {
  262. var buf = new byte[len];
  263. await RecvStream(buf, 0, len, cancel_token).ConfigureAwait(false);
  264. return buf;
  265. }
  266. private class RTMPMessageBuilder
  267. {
  268. public long Timestamp { get; set; }
  269. public long TimestampDelta { get; set; }
  270. public int TypeId { get; set; }
  271. public long ChunkMessageStreamId { get; set; }
  272. public int BodyLength { get; set; }
  273. public int ReceivedLength { get; set; }
  274. public byte[] Body { get; set; }
  275. public static readonly RTMPMessageBuilder NullPacket = new RTMPMessageBuilder(null, 0, 0, 0, 0);
  276. public RTMPMessageBuilder(
  277. RTMPMessageBuilder x,
  278. long timestamp,
  279. int type_id,
  280. long chunk_message_stream_id,
  281. int body_length)
  282. {
  283. Timestamp = timestamp;
  284. TimestampDelta = x!=null ? timestamp-x.Timestamp : 0;
  285. TypeId = type_id;
  286. ChunkMessageStreamId = chunk_message_stream_id;
  287. BodyLength = body_length;
  288. ReceivedLength = 0;
  289. Body = new byte[BodyLength];
  290. }
  291. public RTMPMessageBuilder(
  292. RTMPMessageBuilder x,
  293. long timestamp_delta,
  294. int type_id,
  295. int body_length)
  296. {
  297. Timestamp = x.Timestamp + timestamp_delta;
  298. TimestampDelta = timestamp_delta;
  299. TypeId = type_id;
  300. ChunkMessageStreamId = x.ChunkMessageStreamId;
  301. BodyLength = body_length;
  302. ReceivedLength = 0;
  303. Body = new byte[BodyLength];
  304. }
  305. public RTMPMessageBuilder(
  306. RTMPMessageBuilder x,
  307. long timestamp_delta)
  308. {
  309. Timestamp = x.Timestamp + timestamp_delta;
  310. TimestampDelta = timestamp_delta;
  311. TypeId = x.TypeId;
  312. ChunkMessageStreamId = x.ChunkMessageStreamId;
  313. BodyLength = x.BodyLength;
  314. ReceivedLength = 0;
  315. Body = new byte[BodyLength];
  316. }
  317. public RTMPMessageBuilder(RTMPMessageBuilder x)
  318. {
  319. Timestamp = x.Timestamp + x.TimestampDelta;
  320. TypeId = x.TypeId;
  321. ChunkMessageStreamId = x.ChunkMessageStreamId;
  322. BodyLength = x.BodyLength;
  323. ReceivedLength = 0;
  324. Body = new byte[BodyLength];
  325. }
  326. public RTMPMessage ToMessage()
  327. {
  328. return new RTMPMessage((RTMPMessageType)TypeId, Timestamp, ChunkMessageStreamId, Body);
  329. }
  330. public void Abort()
  331. {
  332. this.ReceivedLength = this.BodyLength;
  333. }
  334. }
  335. private Dictionary<int, RTMPMessageBuilder> lastMessages = new Dictionary<int,RTMPMessageBuilder>();
  336. private async Task<bool> RecvMessage(Queue<RTMPMessage> messages, CancellationToken cancel_token)
  337. {
  338. var basic_header = (await RecvStream(1, cancel_token).ConfigureAwait(false))[0];
  339. var chunk_stream_id = basic_header & 0x3F;
  340. if (chunk_stream_id==0) {
  341. chunk_stream_id = (await RecvStream(1, cancel_token).ConfigureAwait(false))[0] + 64;
  342. }
  343. else if (chunk_stream_id==1) {
  344. var buf = await RecvStream(2, cancel_token).ConfigureAwait(false);
  345. chunk_stream_id = (buf[1]*256 | buf[0]) + 64;
  346. }
  347. RTMPMessageBuilder msg = null;
  348. RTMPMessageBuilder last_msg = null;
  349. if (!lastMessages.TryGetValue(chunk_stream_id, out last_msg)) {
  350. last_msg = RTMPMessageBuilder.NullPacket;
  351. }
  352. switch ((basic_header & 0xC0)>>6) {
  353. case 0:
  354. using (var reader=new RTMPBinaryReader(await RecvStream(11, cancel_token).ConfigureAwait(false))) {
  355. long timestamp = reader.ReadUInt24();
  356. var body_length = reader.ReadUInt24();
  357. var type_id = reader.ReadByte();
  358. var stream_id = reader.ReadUInt32LE();
  359. if (timestamp==0xFFFFFF) {
  360. using (var ext_reader=new RTMPBinaryReader(await RecvStream(4, cancel_token).ConfigureAwait(false))) {
  361. timestamp = ext_reader.ReadUInt32();
  362. }
  363. }
  364. msg = new RTMPMessageBuilder(
  365. last_msg,
  366. timestamp,
  367. type_id,
  368. stream_id,
  369. body_length);
  370. lastMessages[chunk_stream_id] = msg;
  371. }
  372. break;
  373. case 1:
  374. using (var reader=new RTMPBinaryReader(await RecvStream(7, cancel_token).ConfigureAwait(false))) {
  375. long timestamp_delta = reader.ReadUInt24();
  376. var body_length = reader.ReadUInt24();
  377. var type_id = reader.ReadByte();
  378. if (timestamp_delta==0xFFFFFF) {
  379. using (var ext_reader=new RTMPBinaryReader(await RecvStream(4, cancel_token).ConfigureAwait(false))) {
  380. timestamp_delta = ext_reader.ReadUInt32();
  381. }
  382. }
  383. msg = new RTMPMessageBuilder(
  384. last_msg,
  385. timestamp_delta,
  386. type_id,
  387. body_length);
  388. lastMessages[chunk_stream_id] = msg;
  389. }
  390. break;
  391. case 2:
  392. using (var reader=new RTMPBinaryReader(await RecvStream(3, cancel_token).ConfigureAwait(false))) {
  393. long timestamp_delta = reader.ReadUInt24();
  394. if (timestamp_delta==0xFFFFFF) {
  395. using (var ext_reader=new RTMPBinaryReader(await RecvStream(4, cancel_token).ConfigureAwait(false))) {
  396. timestamp_delta = ext_reader.ReadUInt32();
  397. }
  398. }
  399. msg = new RTMPMessageBuilder(last_msg, timestamp_delta);
  400. lastMessages[chunk_stream_id] = msg;
  401. }
  402. break;
  403. case 3:
  404. msg = last_msg;
  405. if (msg.ReceivedLength>=msg.BodyLength) {
  406. msg = new RTMPMessageBuilder(last_msg);
  407. lastMessages[chunk_stream_id] = msg;
  408. }
  409. break;
  410. }
  411. msg.ReceivedLength += await RecvStream(
  412. msg.Body,
  413. msg.ReceivedLength,
  414. Math.Min(recvChunkSize, msg.BodyLength-msg.ReceivedLength),
  415. cancel_token).ConfigureAwait(false);
  416. if (msg.ReceivedLength>=msg.BodyLength) {
  417. messages.Enqueue(msg.ToMessage());
  418. }
  419. return true; //TODO:接続エラー時はfalseを返す
  420. }
  421. private async Task SendMessage(int chunk_stream_id, RTMPMessage msg, CancellationToken cancel_token)
  422. {
  423. int offset = 0;
  424. int fmt = 0;
  425. while (msg.Body.Length-offset>0) {
  426. switch (fmt) {
  427. case 0:
  428. await SendAsync(writer => {
  429. writer.Write((byte)((fmt<<6) | chunk_stream_id));
  430. if (msg.Timestamp>0xFFFFFF) {
  431. writer.WriteUInt24(0xFFFFFF);
  432. }
  433. else {
  434. writer.WriteUInt24((int)msg.Timestamp);
  435. }
  436. writer.WriteUInt24(msg.Body.Length);
  437. writer.Write((byte)msg.MessageType);
  438. writer.WriteUInt32LE(msg.StreamId);
  439. if (msg.Timestamp>0xFFFFFF) {
  440. writer.WriteUInt32(msg.Timestamp);
  441. }
  442. int chunk_len = Math.Min(sendChunkSize, msg.Body.Length-offset);
  443. writer.Write(msg.Body, offset, chunk_len);
  444. offset += chunk_len;
  445. }, cancel_token).ConfigureAwait(false);
  446. fmt = 3;
  447. break;
  448. case 3:
  449. await SendAsync(writer => {
  450. writer.Write((byte)((fmt<<6) | chunk_stream_id));
  451. int chunk_len = Math.Min(sendChunkSize, msg.Body.Length-offset);
  452. writer.Write(msg.Body, offset, chunk_len);
  453. offset += chunk_len;
  454. }, cancel_token).ConfigureAwait(false);
  455. break;
  456. }
  457. }
  458. }
  459. private async Task ProcessMessages(IEnumerable<RTMPMessage> messages, CancellationToken cancel_token)
  460. {
  461. foreach (var msg in messages) {
  462. await ProcessMessage(msg, cancel_token).ConfigureAwait(false);
  463. }
  464. }
  465. private async Task ProcessMessage(RTMPMessage msg, CancellationToken cancel_token)
  466. {
  467. switch (msg.MessageType) {
  468. case RTMPMessageType.SetChunkSize:
  469. await OnSetChunkSize(new SetChunkSizeMessage(msg), cancel_token).ConfigureAwait(false);
  470. break;
  471. case RTMPMessageType.Abort:
  472. await OnAbort(new AbortMessage(msg), cancel_token).ConfigureAwait(false);
  473. break;
  474. case RTMPMessageType.Ack:
  475. //Do nothing
  476. break;
  477. case RTMPMessageType.UserControl:
  478. await OnUserControl(new UserControlMessage(msg), cancel_token).ConfigureAwait(false);
  479. break;
  480. case RTMPMessageType.SetWindowSize:
  481. await OnSetWindowSize(new SetWindowSizeMessage(msg), cancel_token).ConfigureAwait(false);
  482. break;
  483. case RTMPMessageType.SetPeerBandwidth:
  484. await OnSetPeerBandwidth(new SetPeerBandwidthMessage(msg), cancel_token).ConfigureAwait(false);
  485. break;
  486. case RTMPMessageType.Audio:
  487. await OnAudio(msg, cancel_token).ConfigureAwait(false);
  488. break;
  489. case RTMPMessageType.Video:
  490. await OnVideo(msg, cancel_token).ConfigureAwait(false);
  491. break;
  492. case RTMPMessageType.DataAMF3:
  493. await OnData(new DataAMF3Message(msg), cancel_token).ConfigureAwait(false);
  494. break;
  495. case RTMPMessageType.DataAMF0:
  496. await OnData(new DataAMF0Message(msg), cancel_token).ConfigureAwait(false);
  497. break;
  498. case RTMPMessageType.CommandAMF3:
  499. await OnCommand(new CommandAMF3Message(msg), cancel_token).ConfigureAwait(false);
  500. break;
  501. case RTMPMessageType.CommandAMF0:
  502. await OnCommand(new CommandAMF0Message(msg), cancel_token).ConfigureAwait(false);
  503. break;
  504. case RTMPMessageType.Aggregate:
  505. await OnAggregate(new AggregateMessage(msg), cancel_token).ConfigureAwait(false);
  506. break;
  507. case RTMPMessageType.SharedObjectAMF3:
  508. case RTMPMessageType.SharedObjectAMF0:
  509. //TODO:Not implemented
  510. break;
  511. default:
  512. //TODO:Unknown message
  513. break;
  514. }
  515. }
  516. private Task OnSetChunkSize(SetChunkSizeMessage msg, CancellationToken cancel_token)
  517. {
  518. recvChunkSize = Math.Min(msg.ChunkSize, 0xFFFFFF);
  519. return Task.Delay(0);
  520. }
  521. private Task OnAbort(AbortMessage msg, CancellationToken cancel_token)
  522. {
  523. RTMPMessageBuilder builder;
  524. if (lastMessages.TryGetValue(msg.TargetChunkStream, out builder)) {
  525. builder.Abort();
  526. }
  527. return Task.Delay(0);
  528. }
  529. private Task OnUserControl(UserControlMessage msg, CancellationToken cancel_token)
  530. {
  531. return Task.Delay(0);
  532. }
  533. private Task OnSetWindowSize(SetWindowSizeMessage msg, CancellationToken cancel_token)
  534. {
  535. recvWindowSize = msg.WindowSize;
  536. receivedSize = 0;
  537. return Task.Delay(0);
  538. }
  539. private Task OnSetPeerBandwidth(SetPeerBandwidthMessage msg, CancellationToken cancel_token)
  540. {
  541. switch (msg.LimitType) {
  542. case PeerBandwidthLimitType.Hard:
  543. sendWindowSize = msg.PeerBandwidth;
  544. sendWindowLimitType = msg.LimitType;
  545. break;
  546. case PeerBandwidthLimitType.Soft:
  547. sendWindowSize = Math.Min(sendWindowSize, msg.PeerBandwidth);
  548. sendWindowLimitType = msg.LimitType;
  549. break;
  550. case PeerBandwidthLimitType.Dynamic:
  551. if (sendWindowLimitType==PeerBandwidthLimitType.Hard) {
  552. sendWindowSize = msg.PeerBandwidth;
  553. sendWindowLimitType = msg.LimitType;
  554. }
  555. break;
  556. }
  557. return Task.Delay(0);
  558. }
  559. private Task OnAudio(RTMPMessage msg, CancellationToken cancel_token)
  560. {
  561. flvBuffer.OnAudio(msg);
  562. return Task.Delay(0);
  563. }
  564. private Task OnVideo(RTMPMessage msg, CancellationToken cancel_token)
  565. {
  566. flvBuffer.OnVideo(msg);
  567. return Task.Delay(0);
  568. }
  569. private Task OnData(DataMessage msg, CancellationToken cancel_token)
  570. {
  571. flvBuffer.OnData(msg);
  572. return Task.Delay(0);
  573. }
  574. private async Task OnCommand(CommandMessage msg, CancellationToken cancel_token)
  575. {
  576. if (msg.StreamId==0) {
  577. Logger.Debug("NetConnection command: {0}", msg.CommandName);
  578. //NetConnection commands
  579. switch (msg.CommandName) {
  580. case "connect": await OnCommandConnect(msg, cancel_token).ConfigureAwait(false); break;
  581. case "call": await OnCommandCall(msg, cancel_token).ConfigureAwait(false); break;
  582. case "close": await OnCommandClose(msg, cancel_token).ConfigureAwait(false); break;
  583. case "createStream": await OnCommandCreateStream(msg, cancel_token).ConfigureAwait(false); break;
  584. case "deleteStream": await OnCommandDeleteStream(msg, cancel_token).ConfigureAwait(false); break;
  585. }
  586. }
  587. else {
  588. Logger.Debug("NetStream ({0}) command: {1}", msg.StreamId, msg.CommandName);
  589. //NetStream commands
  590. switch (msg.CommandName) {
  591. case "publish": await OnCommandPublish(msg, cancel_token).ConfigureAwait(false); break;
  592. case "deleteStream": await OnCommandDeleteStream(msg, cancel_token).ConfigureAwait(false); break;
  593. case "play":
  594. case "play2":
  595. case "closeStream":
  596. case "receiveAudio":
  597. case "receiveVideo":
  598. case "seek":
  599. case "pause":
  600. default:
  601. break;
  602. }
  603. }
  604. }
  605. private async Task OnCommandConnect(CommandMessage msg, CancellationToken cancel_token)
  606. {
  607. objectEncoding = ((int)msg.CommandObject["objectEncoding"])==3 ? 3 : 0;
  608. clientName = (string)msg.CommandObject["flashVer"];
  609. Logger.Debug("connect: objectEncoding {0}, flashVer: {1}", objectEncoding, clientName);
  610. await SendMessage(2, new SetWindowSizeMessage(this.Now, 0, recvWindowSize), cancel_token).ConfigureAwait(false);
  611. await SendMessage(2, new SetPeerBandwidthMessage(this.Now, 0, sendWindowSize, PeerBandwidthLimitType.Hard), cancel_token).ConfigureAwait(false);
  612. await SendMessage(2, new UserControlMessage.StreamBeginMessage(this.Now, 0, 0), cancel_token).ConfigureAwait(false);
  613. var response = CommandMessage.Create(
  614. objectEncoding,
  615. this.Now,
  616. msg.StreamId,
  617. "_result",
  618. msg.TransactionId,
  619. new AMF.AMFValue(new AMF.AMFObject {
  620. { "fmsVer", "FMS/3,5,5,2004" },
  621. { "capabilities", 31 },
  622. { "mode", 1 },
  623. }),
  624. new AMF.AMFValue(new AMF.AMFObject {
  625. { "level", "status" },
  626. { "code", "NetConnection.Connect.Success" },
  627. { "description", "Connection succeeded" },
  628. { "data", new AMF.AMFObject { { "version", "3,5,5,2004" } } },
  629. { "clientId", nextClientId++ },
  630. { "objectEncoding", objectEncoding },
  631. })
  632. );
  633. if (msg.TransactionId!=0) {
  634. await SendMessage(3, response, cancel_token).ConfigureAwait(false);
  635. }
  636. }
  637. private Task OnCommandCall(CommandMessage msg, CancellationToken cancel_token)
  638. {
  639. return Task.Delay(0);
  640. }
  641. private Task OnCommandClose(CommandMessage msg, CancellationToken cancel_token)
  642. {
  643. return Task.Delay(0);
  644. }
  645. private async Task OnCommandCreateStream(CommandMessage msg, CancellationToken cancel_token)
  646. {
  647. var new_stream_id = nextStreamId++;
  648. var response = CommandMessage.Create(
  649. objectEncoding,
  650. this.Now,
  651. msg.StreamId,
  652. "_result",
  653. msg.TransactionId,
  654. null,
  655. new AMF.AMFValue(new_stream_id)
  656. );
  657. if (msg.TransactionId!=0) {
  658. await SendMessage(3, response, cancel_token).ConfigureAwait(false);
  659. }
  660. }
  661. private Task OnCommandDeleteStream(CommandMessage msg, CancellationToken cancel_token)
  662. {
  663. Stop(StopReason.OffAir);
  664. return Task.Delay(0);
  665. }
  666. private async Task OnCommandPublish(CommandMessage msg, CancellationToken cancel_token)
  667. {
  668. var name = (string)msg.Arguments[0];
  669. var type = (string)msg.Arguments[1];
  670. Logger.Debug("publish: name {0}, type: {1}", name, type);
  671. await SendMessage(2, new UserControlMessage.StreamBeginMessage(this.Now, 0, msg.StreamId), cancel_token).ConfigureAwait(false);
  672. var status = CommandMessage.Create(
  673. objectEncoding,
  674. this.Now,
  675. msg.StreamId,
  676. "onStatus",
  677. 0,
  678. null,
  679. new AMF.AMFValue(new AMF.AMFObject {
  680. { "level", "status" },
  681. { "code", "NetStream.Publish.Start" },
  682. { "description", name },
  683. })
  684. );
  685. await SendMessage(3, status, cancel_token).ConfigureAwait(false);
  686. var result = CommandMessage.Create(
  687. objectEncoding,
  688. this.Now,
  689. msg.StreamId,
  690. "_result",
  691. msg.TransactionId,
  692. null
  693. );
  694. if (msg.TransactionId!=0) {
  695. await SendMessage(3, result, cancel_token).ConfigureAwait(false);
  696. }
  697. this.state = ConnectionState.Receiving;
  698. }
  699. private Task OnAggregate(AggregateMessage msg, CancellationToken cancel_token)
  700. {
  701. return ProcessMessages(msg.Messages, cancel_token);
  702. }
  703. protected Task SendAsync(Action<RTMPBinaryWriter> proc, CancellationToken cancel_token)
  704. {
  705. var memstream = new MemoryStream();
  706. using (memstream) {
  707. using (var writer=new RTMPBinaryWriter(memstream)) {
  708. proc.Invoke(writer);
  709. }
  710. }
  711. return connection.Stream.WriteAsync(memstream.ToArray(), cancel_token);
  712. }
  713. protected async Task<RTMPBinaryReader> RecvAsync(int len, CancellationToken cancel_token)
  714. {
  715. var buf = await connection.Stream.ReadBytesAsync(len, cancel_token).ConfigureAwait(false);
  716. return new RTMPBinaryReader(new MemoryStream(buf, false), false);
  717. }
  718. protected Task SendAsync(byte[] data, CancellationToken cancel_token)
  719. {
  720. return connection.Stream.WriteAsync(data, cancel_token);
  721. }
  722. }
  723. public class RTMPSourceStream
  724. : SourceStreamBase
  725. {
  726. public RTMPSourceStream(PeerCast peercast, Channel channel, Uri source_uri)
  727. : base(peercast, channel, source_uri)
  728. {
  729. this.UseContentBitrate = channel.ChannelInfo==null || channel.ChannelInfo.Bitrate==0;
  730. }
  731. public bool UseContentBitrate { get; private set; }
  732. public override SourceStreamType Type {
  733. get { return SourceStreamType.Broadcast; }
  734. }
  735. public override ConnectionInfo GetConnectionInfo()
  736. {
  737. var conn = sourceConnection;
  738. if (!conn.IsCompleted) {
  739. return conn.Connection.GetConnectionInfo();
  740. }
  741. else {
  742. ConnectionStatus status;
  743. switch (StoppedReason) {
  744. case StopReason.UserReconnect: status = ConnectionStatus.Connecting; break;
  745. case StopReason.UserShutdown: status = ConnectionStatus.Idle; break;
  746. default: status = ConnectionStatus.Error; break;
  747. }
  748. IPEndPoint endpoint = null;
  749. string client_name = "";
  750. return new ConnectionInfoBuilder {
  751. ProtocolName = "RTMP Source",
  752. Type = ConnectionType.Source,
  753. Status = status,
  754. RemoteName = SourceUri.ToString(),
  755. RemoteEndPoint = endpoint,
  756. RemoteHostStatus = RemoteHostStatus.None,
  757. AgentName = client_name,
  758. }.Build();
  759. }
  760. }
  761. protected override ISourceConnection CreateConnection(Uri source_uri)
  762. {
  763. return new RTMPSourceConnection(PeerCast, Channel, source_uri, UseContentBitrate);
  764. }
  765. protected override void OnConnectionStopped(ISourceConnection connection, ConnectionStoppedArgs args)
  766. {
  767. switch (args.Reason) {
  768. case StopReason.UserReconnect:
  769. case StopReason.UserShutdown:
  770. case StopReason.NoHost:
  771. break;
  772. default:
  773. args.Delay = 3000;
  774. args.Reconnect = true;
  775. break;
  776. }
  777. }
  778. }
  779. [Plugin]
  780. class RTMPSourceStreamPlugin
  781. : PluginBase
  782. {
  783. override public string Name { get { return "RTMP Source"; } }
  784. private RTMPSourceStreamFactory factory;
  785. override protected void OnAttach()
  786. {
  787. if (factory==null) factory = new RTMPSourceStreamFactory(Application.PeerCast);
  788. Application.PeerCast.SourceStreamFactories.Add(factory);
  789. }
  790. override protected void OnDetach()
  791. {
  792. Application.PeerCast.SourceStreamFactories.Remove(factory);
  793. }
  794. }
  795. }