/// <summary> /// Stops the server-side stream. /// </summary> public override void Stop() { lock (this.SyncRoot) { if (_state != State.PLAYING && _state != State.PAUSED) { return; } if (_liveJobName != null) { _schedulingService.RemoveScheduledJob(_liveJobName); _liveJobName = null; } if (_vodJobName != null) { _schedulingService.RemoveScheduledJob(_vodJobName); _vodJobName = null; } if (_msgIn != null) { _msgIn.Unsubscribe(this); _msgIn = null; } if (_nextRTMPMessage != null) { _nextRTMPMessage = null; } _state = State.STOPPED; } }
/// <summary> /// Send VOD init control message /// </summary> /// <param name="msgIn"></param> /// <param name="start"></param> private void SendVODInitCM(IMessageInput msgIn, int start) { OOBControlMessage oobCtrlMsg = new OOBControlMessage(); oobCtrlMsg.Target = typeof(IPassive).Name; oobCtrlMsg.ServiceName = "init"; oobCtrlMsg.ServiceParameterMap.Add("startTS", start); _msgIn.SendOOBControlMessage(this, oobCtrlMsg); }
public IMessageInput GetProviderInput(IScope scope, string name) { IMessageInput input = this.GetLiveProviderInput(scope, name, false); if (input == null) { return(this.GetVODProviderInput(scope, name)); } return(input); }
public IMessageInput GetProviderInput(IScope scope, string name) { IMessageInput msgIn = GetLiveProviderInput(scope, name, false); if (msgIn == null) { return(GetVODProviderInput(scope, name)); } return(msgIn); }
/// <summary> /// Play a specific IPlayItem. /// The strategy for now is VOD first, Live second. /// </summary> /// <param name="item">Item to play</param> protected void Play(IPlayItem item) { // Return if already playing if (_state != State.STOPPED) { return; } // Assume this is not live stream bool isLive = false; IProviderService providerService = ScopeUtils.GetScopeService(this.Scope, typeof(IProviderService)) as IProviderService; _msgIn = providerService.GetVODProviderInput(this.Scope, item.Name); if (_msgIn == null) { _msgIn = providerService.GetLiveProviderInput(this.Scope, item.Name, true); isLive = true; } if (_msgIn == null) { log.Warn("Can't get both VOD and Live input from providerService"); return; } _state = State.PLAYING; _currentItem = item; SendResetMessage(); _msgIn.Subscribe(this, null); if (isLive) { if (item.Length >= 0) { PlayItemScheduledJob job = new PlayItemScheduledJob(this); _liveJobName = _schedulingService.AddScheduledOnceJob(item.Length, job); } } else { long start = item.Start; if (start < 0) { start = 0; } SendVODInitCM(_msgIn, (int)start); StartBroadcastVOD(); } }
/// <summary> /// Send VOD seek control message /// </summary> /// <param name="msgIn">Message input</param> /// <param name="position">New timestamp to play from</param> private void SendVODSeekCM(IMessageInput msgIn, int position) { OOBControlMessage oobCtrlMsg = new OOBControlMessage(); oobCtrlMsg.Target = typeof(ISeekableProvider).Name; oobCtrlMsg.ServiceName = "seek"; oobCtrlMsg.ServiceParameterMap.Add("position", position); msgIn.SendOOBControlMessage(this, oobCtrlMsg); lock (this.SyncRoot) { // Reset properties _vodStartTS = 0; _serverStartTS = System.Environment.TickCount; if (_nextRTMPMessage != null) { try { PushMessage(_nextRTMPMessage); } catch (IOException ex) { log.Error("Error while sending message.", ex); } _nextRTMPMessage = null; } ResetMessage reset = new ResetMessage(); try { PushMessage(reset); } catch (IOException ex) { log.Error("Error while sending message.", ex); } ScheduleNextMessage(); } }
/// <summary> /// Play stream /// </summary> /// <param name="item">Playlist item.</param> /// <param name="withReset">Send reset status before playing.</param> public void Play(IPlayItem item, bool withReset) { lock (this.SyncRoot) { // Can't play if state is not stopped if (_playlistSubscriberStream.State != State.STOPPED) throw new IllegalStateException(); if (_msgIn != null) { _msgIn.Unsubscribe(this); _msgIn = null; } // Play type determination // http://livedocs.adobe.com/flex/3/langref/flash/net/NetStream.html#play%28%29 // The start time, in seconds. Allowed values are -2, -1, 0, or a positive number. // The default value is -2, which looks for a live stream, then a recorded stream, // and if it finds neither, opens a live stream. // If -1, plays only a live stream. // If 0 or a positive number, plays a recorded stream, beginning start seconds in. // // -2: live then recorded, -1: live, >=0: recorded int type = (int)(item.Start / 1000); // see if it's a published stream IScope thisScope = _playlistSubscriberStream.Scope; string itemName = item.Name; //check for input and type InputType sourceType = _providerService.LookupProviderInputType(thisScope, itemName); bool isPublishedStream = sourceType == InputType.Live; bool isFileStream = sourceType == InputType.Vod; bool sendNotifications = true; // decision: 0 for Live, 1 for File, 2 for Wait, 3 for N/A switch (type) { case -2: if (isPublishedStream) _playDecision = 0; else if (isFileStream) _playDecision = 1; else _playDecision = 2; break; case -1: if (isPublishedStream) _playDecision = 0; else _playDecision = 2; break; default: if (isFileStream) _playDecision = 1; break; } if (log.IsDebugEnabled) log.Debug(string.Format("Play decision is {0} (0=Live, 1=File, 2=Wait, 3=N/A)", _playDecision)); _currentItem = item; long itemLength = item.Length; switch (_playDecision) { case 0: //get source input without create _msgIn = _providerService.GetLiveProviderInput(thisScope, itemName, false); // Drop all frames up to the next keyframe _videoFrameDropper.Reset(FrameDropperState.SEND_KEYFRAMES_CHECK); if (_msgIn is IBroadcastScope) { // Send initial keyframe IClientBroadcastStream stream = (_msgIn as IBroadcastScope).GetAttribute(Constants.BroadcastScopeStreamAttribute) as IClientBroadcastStream; if (stream != null && stream.CodecInfo != null) { IVideoStreamCodec videoCodec = stream.CodecInfo.VideoCodec; if (videoCodec != null) { if (withReset) { SendReset(); SendResetStatus(item); SendStartStatus(item); } sendNotifications = false; //send decoder configuration if it exists ByteBuffer config = videoCodec.GetDecoderConfiguration(); if (config != null) { VideoData conf = new VideoData(config); try { conf.Timestamp = 0; RtmpMessage confMsg = new RtmpMessage(); confMsg.body = conf; _msgOut.PushMessage(confMsg); } finally { //conf.release(); } } //Check for a keyframe to send ByteBuffer keyFrame = videoCodec.GetKeyframe(); if (keyFrame != null) { VideoData video = new VideoData(keyFrame); try { video.Timestamp = 0; RtmpMessage videoMsg = new RtmpMessage(); videoMsg.body = video; _msgOut.PushMessage(videoMsg); // Don't wait for keyframe _videoFrameDropper.Reset(); } finally { //video.release(); } } } } } _msgIn.Subscribe(this, null); break; case 2: //get source input with create _msgIn = _providerService.GetLiveProviderInput(thisScope, itemName, true); _msgIn.Subscribe(this, null); _waiting = true; if (type == -1 && itemLength >= 0) { //log.debug("Creating wait job"); // Wait given timeout for stream to be published PlaylistSubscriberStreamJob1 job = new PlaylistSubscriberStreamJob1(this, itemName); _waitLiveJob = _schedulingService.AddScheduledOnceJob(item.Length, job); } else if (type == -2) { //log.debug("Creating wait job"); // Wait x seconds for the stream to be published PlaylistSubscriberStreamJob2 job = new PlaylistSubscriberStreamJob2(this, itemName); _waitLiveJob = _schedulingService.AddScheduledOnceJob(15000, job); } else { ConnectToProvider(itemName); } break; case 1: _msgIn = _providerService.GetVODProviderInput(thisScope, itemName); if (_msgIn == null) { SendStreamNotFoundStatus(_currentItem); throw new StreamNotFoundException(itemName); } if (!_msgIn.Subscribe(this, null)) { log.Error("Input source subscribe failed"); } break; default: SendStreamNotFoundStatus(_currentItem); throw new StreamNotFoundException(itemName); } _playlistSubscriberStream.State = State.PLAYING; IMessage msg = null; _streamOffset = 0; _streamStartTS = -1; if (_playDecision == 1) { if (withReset) { ReleasePendingMessage(); } SendVODInitCM(_msgIn, item); // Don't use pullAndPush to detect IOExceptions prior to sending NetStream.Play.Start if (item.Start > 0) { _streamOffset = SendVODSeekCM(_msgIn, (int)item.Start); // We seeked to the nearest keyframe so use real timestamp now if (_streamOffset == -1) { _streamOffset = (int)item.Start; } } msg = _msgIn.PullMessage(); if (msg is RtmpMessage) { IRtmpEvent body = ((RtmpMessage)msg).body; if (itemLength == 0) { // Only send first video frame body = ((RtmpMessage)msg).body; while (body != null && !(body is VideoData)) { msg = _msgIn.PullMessage(); if (msg == null) break; if (msg is RtmpMessage) body = ((RtmpMessage)msg).body; } } if (body != null) { // Adjust timestamp when playing lists body.Timestamp = body.Timestamp + _timestampOffset; } } } if (sendNotifications) { if (withReset) { SendReset(); SendResetStatus(item); } SendStartStatus(item); if (!withReset) { SendSwitchStatus(); } } if (msg != null) { SendMessage((RtmpMessage)msg); } _playlistSubscriberStream.NotifyItemPlay(_currentItem, !_isPullMode); if (withReset) { long currentTime = System.Environment.TickCount; _playbackStart = currentTime - _streamOffset; _nextCheckBufferUnderrun = currentTime + _bufferCheckInterval; if (_currentItem.Length != 0) { EnsurePullAndPushRunning(); } } } }
/// <summary> /// Close stream /// </summary> public void Close() { lock (this.SyncRoot) { if (_msgIn != null) { _msgIn.Unsubscribe(this); _msgIn = null; } _playlistSubscriberStream.State = State.CLOSED; ClearWaitJobs(); ReleasePendingMessage(); _lastMessage = null; SendClearPing(); } }
/// <summary> /// Stop playback /// </summary> public void Stop() { lock (this.SyncRoot) { if (_playlistSubscriberStream.State != State.PLAYING && _playlistSubscriberStream.State != State.PAUSED) throw new IllegalStateException(); _playlistSubscriberStream.State = State.STOPPED; if (_msgIn != null && !_isPullMode) { _msgIn.Unsubscribe(this); _msgIn = null; } _playlistSubscriberStream.NotifyItemStop(_currentItem); ClearWaitJobs(); if (!_playlistSubscriberStream.HasMoreItems) { ReleasePendingMessage(); _bwController.ResetBuckets(_bwContext); _waitingForToken = false; if (_playlistSubscriberStream.ItemSize > 0) SendCompleteStatus(); _bytesSent = 0; SendClearPing(); SendStopStatus(_currentItem); } else { if (_lastMessage != null) { // Remember last timestamp so we can generate correct headers in playlists. _timestampOffset = _lastMessage.Timestamp; } _playlistSubscriberStream.NextItem(); } } }
/// <summary> /// Send VOD check video control message /// </summary> /// <param name="msgIn"></param> /// <returns></returns> private bool SendCheckVideoCM(IMessageInput msgIn) { OOBControlMessage oobCtrlMsg = new OOBControlMessage(); oobCtrlMsg.Target = typeof(IStreamTypeAwareProvider).Name; oobCtrlMsg.ServiceName = "hasVideo"; msgIn.SendOOBControlMessage(this, oobCtrlMsg); if (oobCtrlMsg.Result is Boolean) { return (Boolean)oobCtrlMsg.Result; } else { return false; } }
/// <summary> /// Send VOD seek control message /// </summary> /// <param name="msgIn"></param> /// <param name="position"></param> /// <returns></returns> private int SendVODSeekCM(IMessageInput msgIn, int position) { OOBControlMessage oobCtrlMsg = new OOBControlMessage(); oobCtrlMsg.Target = typeof(ISeekableProvider).Name; oobCtrlMsg.ServiceName = "seek"; oobCtrlMsg.ServiceParameterMap.Add("position", position); msgIn.SendOOBControlMessage(this, oobCtrlMsg); if (oobCtrlMsg.Result is int) { return (int)oobCtrlMsg.Result; } else { return -1; } }
/// <summary> /// Send VOD init control message /// </summary> /// <param name="msgIn"></param> /// <param name="item"></param> private void SendVODInitCM(IMessageInput msgIn, IPlayItem item) { OOBControlMessage oobCtrlMsg = new OOBControlMessage(); oobCtrlMsg.Target = typeof(IPassive).Name; oobCtrlMsg.ServiceName = "init"; oobCtrlMsg.ServiceParameterMap.Add("startTS", item.Start); _msgIn.SendOOBControlMessage(this, oobCtrlMsg); }
private void ConnectToProvider(string itemName) { if (log.IsDebugEnabled) log.Debug(string.Format("Attempting connection to {0}", itemName)); IScope thisScope = _playlistSubscriberStream.Scope; _msgIn = _providerService.GetLiveProviderInput(thisScope, itemName, true); if (_msgIn != null) { log.Debug(string.Format("Provider: {0}", _msgIn)); if (_msgIn.Subscribe(this, null)) { if (log.IsDebugEnabled) log.Debug(string.Format("Subscribed to {0} provider", itemName)); } else { if (log.IsWarnEnabled) log.Warn(string.Format("Subscribe to {0} provider failed", itemName)); } } else { if (log.IsWarnEnabled) log.Warn(string.Format("Provider was not found for {0}", itemName)); } }
/// <summary> /// Stops the server-side stream. /// </summary> public override void Stop() { lock (this.SyncRoot) { if (_state != State.PLAYING && _state != State.PAUSED) return; if (_liveJobName != null) { _schedulingService.RemoveScheduledJob(_liveJobName); _liveJobName = null; } if (_vodJobName != null) { _schedulingService.RemoveScheduledJob(_vodJobName); _vodJobName = null; } if (_msgIn != null) { _msgIn.Unsubscribe(this); _msgIn = null; } if (_nextRTMPMessage != null) { _nextRTMPMessage = null; } _state = State.STOPPED; } }
/// <summary> /// Play a specific IPlayItem. /// The strategy for now is VOD first, Live second. /// </summary> /// <param name="item">Item to play</param> protected void Play(IPlayItem item) { // Return if already playing if (_state != State.STOPPED) return; // Assume this is not live stream bool isLive = false; IProviderService providerService = ScopeUtils.GetScopeService(this.Scope, typeof(IProviderService)) as IProviderService; _msgIn = providerService.GetVODProviderInput(this.Scope, item.Name); if (_msgIn == null) { _msgIn = providerService.GetLiveProviderInput(this.Scope, item.Name, true); isLive = true; } if (_msgIn == null) { log.Warn("Can't get both VOD and Live input from providerService"); return; } _state = State.PLAYING; _currentItem = item; SendResetMessage(); _msgIn.Subscribe(this, null); if (isLive) { if (item.Length >= 0) { PlayItemScheduledJob job = new PlayItemScheduledJob(this); _liveJobName = _schedulingService.AddScheduledOnceJob(item.Length, job); } } else { long start = item.Start; if (start < 0) start = 0; SendVODInitCM(_msgIn, (int)start); StartBroadcastVOD(); } }