Esempio n. 1
0
        // Tune to a channel, and get a live stream to that channel.
        //
        // Post-data is an object with:
        //   channel - The channel to tune to.
        //   upcomingRecordingAllocation - The allocation of the next upcoming recording, or null if there isn't one.
        //   stream - The live stream in case of retuning an existing stream, or null for a new one.
        //
        // Returns a LiveStreamResult value to indicate success or failure.
        public Stream LiveTune(Stream body)
        {
            var args   = Bind <TuneLiveStreamArguments>(body);
            var stream = args.stream;

            LiveStreamResult result = Service.TuneLiveStream(args.channel, args.upcomingRecordingAllocation, ref stream);

            return(AsJson(new
            {
                result = result,
                stream = stream
            }));
        }
Esempio n. 2
0
 private void ShowLiveStreamResultMessageBox(LiveStreamResult result)
 {
     if (result == LiveStreamResult.NotSupported)
     {
         MessageBox.Show(this, "No tuner was able to start the live stream.", "Information", MessageBoxButtons.OK);
     }
     else if (result == LiveStreamResult.NoFreeCardFound)
     {
         MessageBox.Show(this, "No free card was found to start the live stream.", "Information", MessageBoxButtons.OK);
     }
     else if (result == LiveStreamResult.ChannelTuneFailed)
     {
         MessageBox.Show(this, "Failed to tune to channel.", "Information", MessageBoxButtons.OK);
     }
     else
     {
         MessageBox.Show(this, "Unable to start live streaming.", "Information", MessageBoxButtons.OK);
     }
 }
Esempio n. 3
0
        private LiveStreamResult StartAndPlayNewLiveStream(Channel channel, LiveStream liveStream)
        {
            LiveStreamResult result = this.ControlAgent.TuneLiveStream(channel, ref liveStream);

            Log.Debug("ChannelNavigator: start a new live stream, result = {0}", result);

            if (result == LiveStreamResult.Succeeded)
            {
                result = PlayLiveStream(liveStream);
                if (result == LiveStreamResult.Succeeded)
                {
                    if (_autoFullScreen)
                    {
                        g_Player.ShowFullScreenWindow();
                    }
                }
            }
            return(result);
        }
Esempio n. 4
0
        private LiveStreamResult StartAndPlayNewLiveStream(Channel channel, LiveStream liveStream)
        {
            var res = Proxies.ControlService.TuneLiveStream(channel, liveStream).Result;

            liveStream = res.LiveStream;
            LiveStreamResult result = res.LiveStreamResult;

            Log.Debug("ChannelNavigator: start a new live stream, result = {0}", result);

            if (result == LiveStreamResult.Succeeded)
            {
                result = PlayLiveStream(liveStream);
                if (result == LiveStreamResult.Succeeded)
                {
                    if (_autoFullScreen)
                    {
                        g_Player.ShowFullScreenWindow();
                    }
                }
            }
            return(result);
        }
Esempio n. 5
0
 private void ShowLiveStreamResultMessageBox(LiveStreamResult result)
 {
     if (result == LiveStreamResult.NotSupported)
     {
         MessageBox.Show(this, "No tuner was able to start the live stream.", "Information", MessageBoxButtons.OK);
     }
     else if (result == LiveStreamResult.NoFreeCardFound)
     {
         MessageBox.Show(this, "No free card was found to start the live stream.", "Information", MessageBoxButtons.OK);
     }
     else if (result == LiveStreamResult.ChannelTuneFailed)
     {
         MessageBox.Show(this, "Failed to tune to channel.", "Information", MessageBoxButtons.OK);
     }
     else
     {
         MessageBox.Show(this, "Unable to start live streaming.", "Information", MessageBoxButtons.OK);
     }
 }
        private LiveStreamResult PlayLiveStream(LiveStream liveStream)
        {
            Log.Debug("ChannelNavigator: PlayLiveStream()");
            LiveStreamResult result = LiveStreamResult.Succeeded;
            string fileName;
            bool isRTSP;
            GetPlayerFileNameAndOffset(liveStream, out fileName, out isRTSP);

            if (liveStream != null)
                _isAnalog = (ControlAgent.GetLiveStreamTuningDetails(liveStream).CardType == CardType.Analog);

            if (!isRTSP)
            {
                for (int i = 0; i < 10; i++)
                {
                    if (System.IO.File.Exists(fileName))
                        break;

                    Thread.Sleep(100);
                    Log.Info("Channelnavigator: startplay: waiting for TS file {0}", fileName);
                }
                if (!System.IO.File.Exists(fileName))
                {
                    result = LiveStreamResult.UnknownError;
                    g_Player.Stop();
                }
            }

            if (result == LiveStreamResult.Succeeded)
            {
                for (int i = 0; i < 50; i++)
                {
                    if (g_Player.Play(fileName, liveStream.Channel.ChannelType == ChannelType.Television ? g_Player.MediaType.TV : g_Player.MediaType.Radio))
                    {
                        double duration = g_Player.Duration;
                        if (duration > 0.0)
                        {
                            result = LiveStreamResult.Succeeded;
                            g_Player.SeekAbsolute(duration);
                            _liveStream = liveStream;
                            break;
                        }
                        else
                        {
                            result = LiveStreamResult.UnknownError;
                            g_Player.Stop();
                            Thread.Sleep(50);
                            Log.Debug("ChannelNavigator: PlayLiveStream_timeout = {0}", i);
                        }
                    }
                    else
                    {
                        result = LiveStreamResult.UnknownError;
                        break;
                    }
                }
            }
            Log.Debug("ChannelNavigator: PlayLiveStream_Result = {0}", result);
            return result;
        }
        private void ChannelTuneFailedNotifyUser(LiveStreamResult result, Channel channel)
        {
            string TuningResult = string.Empty;
            switch (result)
            {
                case LiveStreamResult.ChannelTuneFailed:
                    TuningResult = (Utility.GetLocalizedText(TextId.ChannelTuneFailed));
                    break;

                case LiveStreamResult.NoFreeCardFound:
                    TuningResult = (Utility.GetLocalizedText(TextId.NoFreeCardFound));
                    break;

                case LiveStreamResult.NotSupported:
                    TuningResult = (Utility.GetLocalizedText(TextId.NotSupported));
                    break;

                case LiveStreamResult.NoRetunePossible:
                    TuningResult = (Utility.GetLocalizedText(TextId.NoRetunePossible));
                    break;

                case LiveStreamResult.IsScrambled:
                    TuningResult = (Utility.GetLocalizedText(TextId.IsScrambled));
                    break;

                case LiveStreamResult.UnknownError:
                    TuningResult = (Utility.GetLocalizedText(TextId.UnknownError));
                    break;
            }

            if (GUIWindowManager.ActiveWindow == (int)(int)GUIWindow.Window.WINDOW_TVFULLSCREEN)
            {
                // If failed and wasPlaying TV, left screen as it is and show zaposd with error message
                GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_TV_ERROR_NOTIFY, GUIWindowManager.ActiveWindow, 0,
                                                0, 0, 0,
                                                null);

                msg.SendToTargetWindow = true;
                msg.Object = TuningResult; // forward error info object
                msg.TargetWindowId = (int)(int)GUIWindow.Window.WINDOW_TVFULLSCREEN;
                GUIGraphicsContext.SendMessage(msg);
            }
            else
            {
                // if not fulscreen, show notify dialog with the error message
                string caption = string.Empty;
                string tvlogo = string.Empty;
                if (channel != null)
                {
                    _navigatorChannels[_currentChannel.ChannelType].PreviousChannel = channel;
                    _currentChannel = null;

                    using (SchedulerServiceAgent SchedulerAgent = new SchedulerServiceAgent())
                    {
                        tvlogo = Utility.GetLogoImage(channel, SchedulerAgent);
                    }

                    if (channel.ChannelType == ChannelType.Television)
                        caption = GUILocalizeStrings.Get(605) + " - " + channel.DisplayName;
                    else
                        caption = GUILocalizeStrings.Get(665) + " - " + channel.DisplayName;
                }

                GUIDialogNotify pDlgNotify = (GUIDialogNotify)GUIWindowManager.GetWindow((int)GUIWindow.Window.WINDOW_DIALOG_NOTIFY);
                if (pDlgNotify != null)
                {
                    pDlgNotify.Reset();
                    pDlgNotify.ClearAll();
                    pDlgNotify.SetHeading(caption);
                    if (!string.IsNullOrEmpty(TuningResult))
                    {
                        pDlgNotify.SetText(TuningResult);
                    }
                    pDlgNotify.SetImage(tvlogo);
                    pDlgNotify.TimeOut = 5;
                    pDlgNotify.DoModal(GUIWindowManager.ActiveWindow);
                }
            }
        }
        private void TuneLiveStream(Channel channel)
        {
            Log.Debug("ChannelNavigator: TuneLiveStream(), channel = {0}", channel.DisplayName);
            if (channel != null)
            {
                using (SchedulerServiceAgent tvSchedulerAgent = new SchedulerServiceAgent())
                {
                    LiveStream liveStream = _liveStream;
                    CurrentAndNextProgram currentAndNext = tvSchedulerAgent.GetCurrentAndNextForChannel(channel.ChannelId, true, _liveStream);//null);

                    _currentChannel = channel;
                    _doingChannelChange = true;
                    RenderBlackImage();

                    if (liveStream != null)
                    {
                        try
                        {
                            g_Player.PauseGraph();
                            g_Player.OnZapping(0x80);

                            result = this.ControlAgent.TuneLiveStream(channel, ref liveStream);
                            Log.Debug("ChannelNavigator: First try to re-tune the existing TV stream (staying on the same card), result = {0}", result);

                            if (result == LiveStreamResult.Succeeded)
                            {
                                if (_isAnalog)
                                    g_Player.OnZapping(-1);

                                double duration = g_Player.Duration;
                                if (g_Player.Duration < 0.0)
                                    result = LiveStreamResult.UnknownError;
                                else
                                {
                                    g_Player.SeekAbsolute(duration);
                                    g_Player.ContinueGraph();
                                }
                            }
                            else if (result == LiveStreamResult.NoRetunePossible)// not mapped to card, card in use by recorder or other user ---> start new stream
                            {
                                // Now re-try the new channel with a new stream.
                                Log.Debug("ChannelNavigator: Seems a re-tune has failed, stop the current stream and start a new one");
                                SilentlyStopLiveStream(liveStream);
                                result = StartAndPlayNewLiveStream(channel, liveStream);
                            }
                        }
                        catch
                        {
                            result = LiveStreamResult.UnknownError;
                            Log.Error("ChannelNavigator: TuneLiveStream error");
                        }
                    }
                    else
                    {
                        result = StartAndPlayNewLiveStream(channel,liveStream);
                    }

                    _doingChannelChange = false;
                    if (result == LiveStreamResult.Succeeded)
                    {
                        _lastChannelChangeFailed = false;
                        StopRenderBlackImage();
                    }
                    else
                    {
                        _lastChannelChangeFailed = true;
                        SilentlyStopLiveStream(liveStream);
                        ChannelTuneFailedNotifyUser(result, channel);
                    }
                }
            }
        }
        private LiveStreamResult StartAndPlayNewLiveStream(Channel channel,LiveStream liveStream)
        {
            LiveStreamResult result = this.ControlAgent.TuneLiveStream(channel, ref liveStream);
            Log.Debug("ChannelNavigator: start a new live stream, result = {0}", result);

            if (result == LiveStreamResult.Succeeded)
            {
                result = PlayLiveStream(liveStream);
                if (result == LiveStreamResult.Succeeded)
                {
                    if (_autoFullScreen) g_Player.ShowFullScreenWindow();
                }
            }
            return result;
        }
        public override LiveStreamResult TuneLiveStream(Channel channel, CardChannelAllocation upcomingRecordingAllocation, ref LiveStream liveStream)
        {
            try
            {
                TvDatabase.Channel     mpChannel;
                List <TvDatabase.Card> availableCards = GetCardsForChannel(channel, out mpChannel);

                // Sort the cards by priority.
                availableCards.Sort(delegate(TvDatabase.Card c1, TvDatabase.Card c2) { return(c1.Priority.CompareTo(c2.Priority)); });

                if (liveStream != null &&
                    _liveStreamUsers.ContainsKey(liveStream.RtspUrl))
                {
                    IUser tve3User = _liveStreamUsers[liveStream.RtspUrl];
                    foreach (TvDatabase.Card card in availableCards)
                    {
                        if (card.IdCard == tve3User.CardId)
                        {
                            if (Utility.CardFreeOrUsingSameTransponder(card, mpChannel, tve3User))
                            {
                                Log("TuneLiveTvStream(): tuning on card {0} {1}", card.IdCard, card.Name);
                                lock (_liveStreamsLock)
                                {
                                    _liveStreams[liveStream.RtspUrl].StreamLastAliveTimeUtc = DateTime.UtcNow;
                                }
                                LiveStreamResult result = StartTimeShifting(channel, card, mpChannel, ref tve3User, ref liveStream);
                                if (result != LiveStreamResult.NoFreeCardFound)
                                {
                                    return(result);
                                }
                            }
                        }
                    }

                    return(LiveStreamResult.NoRetunePossible);
                }
                else
                {
                    if (liveStream != null)
                    {
                        StopLiveStream(liveStream);
                        liveStream = null;
                    }

                    List <int> inUseHybridGroups = new List <int>();

                    foreach (TvDatabase.Card card in availableCards)
                    {
                        if (!Utility.IsInSameHybridGroup(card, inUseHybridGroups) &&
                            Utility.CardFreeOrUsingSameTransponder(card, mpChannel))
                        {
                            string userName = String.Format(CultureInfo.InvariantCulture, "{0}{1}", _argusLiveUserName, Guid.NewGuid());

                            IUser tve3User = new User(userName, true, card.IdCard);
                            tve3User.IdChannel  = mpChannel.IdChannel;
                            tve3User.SubChannel = -1;

                            Log("TuneLiveTvStream(): tuning on card {0} {1}", card.IdCard, card.Name);

                            LiveStreamResult result = StartTimeShifting(channel, card, mpChannel, ref tve3User, ref liveStream);
                            if (result != LiveStreamResult.NoFreeCardFound)
                            {
                                return(result);
                            }
                        }
                        // For hybrid cards, keep track of the groups that are in use.
                        inUseHybridGroups.AddRange(Utility.GetHybridGroupIds(card));
                    }

                    return(LiveStreamResult.NoFreeCardFound);
                }
            }
            catch (Exception ex)
            {
                Log(TraceEventType.Error, ex.Message);
                return(LiveStreamResult.UnknownError);
            }
        }
Esempio n. 11
0
        private LiveStreamResult StartAndPlayNewLiveStream(Channel channel,LiveStream liveStream)
        {
            var res = Proxies.ControlService.TuneLiveStream(channel, liveStream).Result;
            liveStream = res.LiveStream;
            LiveStreamResult result = res.LiveStreamResult;
            Log.Debug("ChannelNavigator: start a new live stream, result = {0}", result);

            if (result == LiveStreamResult.Succeeded)
            {
                result = PlayLiveStream(liveStream);
                if (result == LiveStreamResult.Succeeded)
                {
                    if (_autoFullScreen) g_Player.ShowFullScreenWindow();
                }
            }
            return result;
        }
Esempio n. 12
0
        private LiveStreamResult PlayLiveStream(LiveStream liveStream)
        {
            Log.Debug("ChannelNavigator: PlayLiveStream()");
            LiveStreamResult result = LiveStreamResult.Succeeded;
            string           fileName;
            bool             isRTSP;

            GetPlayerFileNameAndOffset(liveStream, out fileName, out isRTSP);

            if (liveStream != null)
            {
                _isAnalog = (ControlAgent.GetLiveStreamTuningDetails(liveStream).CardType == CardType.Analog);
            }

            if (!isRTSP)
            {
                for (int i = 0; i < 10; i++)
                {
                    if (System.IO.File.Exists(fileName))
                    {
                        break;
                    }

                    Thread.Sleep(100);
                    Log.Info("Channelnavigator: startplay: waiting for TS file {0}", fileName);
                }
                if (!System.IO.File.Exists(fileName))
                {
                    result = LiveStreamResult.UnknownError;
                    g_Player.Stop();
                }
            }

            if (result == LiveStreamResult.Succeeded)
            {
                for (int i = 0; i < 50; i++)
                {
                    if (g_Player.Play(fileName, liveStream.Channel.ChannelType == ChannelType.Television ? g_Player.MediaType.TV : g_Player.MediaType.Radio))
                    {
                        double duration = g_Player.Duration;
                        if (duration > 0.0)
                        {
                            result = LiveStreamResult.Succeeded;
                            g_Player.SeekAbsolute(duration);
                            _liveStream = liveStream;
                            break;
                        }
                        else
                        {
                            result = LiveStreamResult.UnknownError;
                            g_Player.Stop();
                            Thread.Sleep(50);
                            Log.Debug("ChannelNavigator: PlayLiveStream_timeout = {0}", i);
                        }
                    }
                    else
                    {
                        result = LiveStreamResult.UnknownError;
                        break;
                    }
                }
            }
            Log.Debug("ChannelNavigator: PlayLiveStream_Result = {0}", result);
            return(result);
        }
Esempio n. 13
0
        private void ChannelTuneFailedNotifyUser(LiveStreamResult result, Channel channel)
        {
            string TuningResult = string.Empty;

            switch (result)
            {
            case LiveStreamResult.ChannelTuneFailed:
                TuningResult = (Utility.GetLocalizedText(TextId.ChannelTuneFailed));
                break;

            case LiveStreamResult.NoFreeCardFound:
                TuningResult = (Utility.GetLocalizedText(TextId.NoFreeCardFound));
                break;

            case LiveStreamResult.NotSupported:
                TuningResult = (Utility.GetLocalizedText(TextId.NotSupported));
                break;

            case LiveStreamResult.NoRetunePossible:
                TuningResult = (Utility.GetLocalizedText(TextId.NoRetunePossible));
                break;

            case LiveStreamResult.IsScrambled:
                TuningResult = (Utility.GetLocalizedText(TextId.IsScrambled));
                break;

            case LiveStreamResult.UnknownError:
                TuningResult = (Utility.GetLocalizedText(TextId.UnknownError));
                break;
            }

            if (GUIWindowManager.ActiveWindow == (int)(int)GUIWindow.Window.WINDOW_TVFULLSCREEN)
            {
                // If failed and wasPlaying TV, left screen as it is and show zaposd with error message
                GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_TV_ERROR_NOTIFY, GUIWindowManager.ActiveWindow, 0,
                                                0, 0, 0,
                                                null);

                msg.SendToTargetWindow = true;
                msg.Object             = TuningResult; // forward error info object
                msg.TargetWindowId     = (int)(int)GUIWindow.Window.WINDOW_TVFULLSCREEN;
                GUIGraphicsContext.SendMessage(msg);
            }
            else
            {
                // if not fulscreen, show notify dialog with the error message
                string caption = string.Empty;
                string tvlogo  = string.Empty;
                if (channel != null)
                {
                    _navigatorChannels[_currentChannel.ChannelType].PreviousChannel = channel;
                    _currentChannel = null;

                    using (SchedulerServiceAgent SchedulerAgent = new SchedulerServiceAgent())
                    {
                        tvlogo = Utility.GetLogoImage(channel, SchedulerAgent);
                    }

                    if (channel.ChannelType == ChannelType.Television)
                    {
                        caption = GUILocalizeStrings.Get(605) + " - " + channel.DisplayName;
                    }
                    else
                    {
                        caption = GUILocalizeStrings.Get(665) + " - " + channel.DisplayName;
                    }
                }

                GUIDialogNotify pDlgNotify = (GUIDialogNotify)GUIWindowManager.GetWindow((int)GUIWindow.Window.WINDOW_DIALOG_NOTIFY);
                if (pDlgNotify != null)
                {
                    pDlgNotify.Reset();
                    pDlgNotify.ClearAll();
                    pDlgNotify.SetHeading(caption);
                    if (!string.IsNullOrEmpty(TuningResult))
                    {
                        pDlgNotify.SetText(TuningResult);
                    }
                    pDlgNotify.SetImage(tvlogo);
                    pDlgNotify.TimeOut = 5;
                    pDlgNotify.DoModal(GUIWindowManager.ActiveWindow);
                }
            }
        }
Esempio n. 14
0
        private void TuneLiveStream(Channel channel)
        {
            Log.Debug("ChannelNavigator: TuneLiveStream(), channel = {0}", channel.DisplayName);
            if (channel != null)
            {
                using (SchedulerServiceAgent tvSchedulerAgent = new SchedulerServiceAgent())
                {
                    LiveStream            liveStream     = _liveStream;
                    CurrentAndNextProgram currentAndNext = tvSchedulerAgent.GetCurrentAndNextForChannel(channel.ChannelId, true, _liveStream);//null);

                    _currentChannel     = channel;
                    _doingChannelChange = true;
                    RenderBlackImage();

                    if (liveStream != null)
                    {
                        try
                        {
                            g_Player.PauseGraph();
                            g_Player.OnZapping(0x80);

                            result = this.ControlAgent.TuneLiveStream(channel, ref liveStream);
                            Log.Debug("ChannelNavigator: First try to re-tune the existing TV stream (staying on the same card), result = {0}", result);

                            if (result == LiveStreamResult.Succeeded)
                            {
                                if (_isAnalog)
                                {
                                    g_Player.OnZapping(-1);
                                }

                                double duration = g_Player.Duration;
                                if (g_Player.Duration < 0.0)
                                {
                                    result = LiveStreamResult.UnknownError;
                                }
                                else
                                {
                                    g_Player.SeekAbsolute(duration);
                                    g_Player.ContinueGraph();
                                }
                            }
                            else if (result == LiveStreamResult.NoRetunePossible)// not mapped to card, card in use by recorder or other user ---> start new stream
                            {
                                // Now re-try the new channel with a new stream.
                                Log.Debug("ChannelNavigator: Seems a re-tune has failed, stop the current stream and start a new one");
                                SilentlyStopLiveStream(liveStream);
                                result = StartAndPlayNewLiveStream(channel, liveStream);
                            }
                        }
                        catch
                        {
                            result = LiveStreamResult.UnknownError;
                            Log.Error("ChannelNavigator: TuneLiveStream error");
                        }
                    }
                    else
                    {
                        result = StartAndPlayNewLiveStream(channel, liveStream);
                    }

                    _doingChannelChange = false;
                    if (result == LiveStreamResult.Succeeded)
                    {
                        _lastChannelChangeFailed = false;
                        StopRenderBlackImage();
                    }
                    else
                    {
                        _lastChannelChangeFailed = true;
                        SilentlyStopLiveStream(liveStream);
                        ChannelTuneFailedNotifyUser(result, channel);
                    }
                }
            }
        }
Esempio n. 15
0
        public RecorderModule()
            : base("Recorder")
        {
            this.OnError.AddItemToStartOfPipeline((context, ex) =>
            {
                EventLogger.WriteEntry(ex);
                return(null);
            });

            // Ping Recorder service.
            //
            // Returns the version of the API on the recorder, which should be Constants.RecorderApiVersion.
            Get["/Ping"] = p =>
            {
                int result = -1;
                _staContext.Send((s) => { result = Service.Ping(); }, null);
                return(new
                {
                    result = Constants.RecorderApiVersion
                });
            };

            // Get the server's MAC address(es).  These can be stored on the client after a successful
            // connect and later used to re-connect with wake-on-lan.
            //
            // Returns an array containing one or more MAC addresses in HEX string format (e.g. "A1B2C3D4E5F6").
            Get["/MacAddresses"] = p =>
            {
                List <String> result = null;
                _staContext.Send((s) => { result = Service.GetMacAddresses(); }, null);
                return(new
                {
                    result = result
                });
            };

            // Ask the recorder to initialize by registering itself over the Recorder callback's
            // RegisterRecorder method.
            //
            // recorderId - The unique ID of this recorder.
            //
            // Post-data is an object with:
            //   schedulerBaseUrl - The callback URL for the Recorder to communicate with the Scheduler.
            Put["/Initialize/{recorderId}"] = p =>
            {
                var args = this.Bind <InitializeArguments>();

                _staContext.Send((s) => { Service.Initialize(p.recorderId, args.schedulerBaseUrl); }, null);
                return(HttpStatusCode.OK);
            };

            // Ask the recorder to allocate a (virtual) card for a channel.  The previously allocated
            // cards are also passed in, so the implementation must take into account that these cards
            // are no longer available when this call is made.  Note that the implementation must *not*
            // worry about cards being actually free at the moment the call is made!  This is purely a
            // theoretical calculation that is used by ARGUS TV to manage its upcoming recordings.
            //
            //   channel - The channel to allocate.
            //   alreadyAllocated - All previously allocated channels/cards.
            //   useReversePriority - Use reverse cards priority to avoid conflicts with live streaming.
            //
            // Returns the unique card ID of the card that can record this channel, or null if no free card was found.
            Put["/AllocateCard"] = p =>
            {
                var args = this.Bind <AllocateCardArguments>();

                string result = null;
                _staContext.Send((s) => { result = Service.AllocateCard(args.channel, args.alreadyAllocated, args.useReversePriority); }, null);
                return(new
                {
                    result = result
                });
            };

            // Tell the recorder to actually start a recording on the given card.  The implementation
            // must call /NewRecording on the Recorder callback service when the recording actually
            // starts.  If the recording can't start for some reason /Recording/StartFailed must be called.
            // In case the recording ends (prematurely or on time) /EndRecording must be called.
            // IMPORTANT: If the suggested relative path and filename was used the recorder should
            // return 'false' to /EndRecording's 'okToMoveFile'!
            //
            // Post-data is an object with:
            //   schedulerBaseUrl - The callback URL for the Recorder to communicate with the Scheduler.
            //   channelAllocation - The card allocation for the channel.
            //   startTimeUtc - The actual time to start the recording (UTC).
            //   stopTimeUtc - The actual time to stop the recording (UTC).
            //   recordingProgram - The program to record.
            //   suggestedBaseFileName - The suggested relative path and filename (without extension) of the recording file.
            //
            // Returns a boolean indicating the recording was initiated succesfully.
            Post["/Recording/Start"] = p =>
            {
                var args = this.Bind <StartRecordingArguments>();

                bool result = false;
                _staContext.Send((s) => { result = Service.StartRecording(args.schedulerBaseUrl, args.channelAllocation, args.startTimeUtc, args.stopTimeUtc, args.recordingProgram, args.suggestedBaseFileName); }, null);
                return(new
                {
                    result = result
                });
            };

            // Validate a recording is still running, and update its actual stop time.
            //
            // Post-data is an object with:
            //   channelAllocation - The card allocation for the channel.
            //   recordingProgram - The program being recorded.
            //   stopTimeUtc - The up-to-date stop time (UTC).
            //
            // Returns true if the recording was still running (and its stop time was succesfully updated), false if there was a problem or the recording is not running.
            Put["/Recording/ValidateAndUpdate"] = p =>
            {
                var args = this.Bind <ValidateAndUpdateRecordingArguments>();

                bool result = false;
                _staContext.Send((s) => { result = Service.ValidateAndUpdateRecording(args.channelAllocation, args.recordingProgram, args.stopTimeUtc); }, null);
                return(new
                {
                    result = result
                });
            };

            // Tell the recorder to abort the recording of a program.  The implementation must call
            // EndRecording() on the Recorder callback service.
            //
            // Post-data is an object with:
            //   schedulerBaseUrl - The callback URL for the Recorder to communicate with the Scheduler.
            //   recordingProgram - The program being recorded.
            //
            // Returns true if the recording was found and aborted.
            Put["/Recording/Abort"] = p =>
            {
                var args = this.Bind <AbortRecordingArguments>();

                bool result = false;
                _staContext.Send((s) => { result = Service.AbortRecording(args.schedulerBaseUrl, args.recordingProgram); }, null);
                return(new
                {
                    result = result
                });
            };

            // Retrieves the recording shares of the recorder.
            Get["/RecordingShares"] = p =>
            {
                List <string> result = null;
                _staContext.Send((s) => { result = Service.GetRecordingShares(); }, null);
                return(new
                {
                    result = result
                });
            };

            // Retrieves the timeshift shares of the recorder.
            Get["/TimeshiftShares"] = p =>
            {
                List <string> result = null;
                _staContext.Send((s) => { result = Service.GetTimeshiftShares(); }, null);
                return(new
                {
                    result = result
                });
            };

            #region Live Streaming

            // Tune to a channel, and get a live stream to that channel.
            //
            // Post-data is an object with:
            //   channel - The channel to tune to.
            //   upcomingRecordingAllocation - The allocation of the next upcoming recording, or null if there isn't one.
            //   stream - The live stream in case of retuning an existing stream, or null for a new one.
            //
            // Returns a LiveStreamResult value to indicate success or failure.
            Post["/Live/Tune"] = p =>
            {
                var args   = this.Bind <TuneLiveStreamArguments>();
                var stream = args.stream;

                LiveStreamResult result = LiveStreamResult.UnknownError;
                _staContext.Send((s) => { result = Service.TuneLiveStream(args.channel, args.upcomingRecordingAllocation, ref stream); }, null);
                return(new
                {
                    result = result,
                    stream = stream
                });
            };

            // Tell the recorder we are still showing this stream and to keep it alive. Call this every 30 seconds or so.
            //
            // Post-data is the live stream that is stil in use.
            //
            // Returns true if the live stream is still running, false otherwise.
            Put["/Live/KeepAlive"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                bool result = false;
                _staContext.Send((s) => { result = Service.KeepLiveStreamAlive(stream); }, null);
                return(new
                {
                    result = result
                });
            };

            // Stop the live stream (if it is found and belongs to the recorder).
            //
            // Post-data is the live stream to stop.
            Put["/Live/Stop"] = p =>
            {
                var stream = this.Bind <LiveStream>();
                _staContext.Send((s) => { Service.StopLiveStream(stream); }, null);
                return(HttpStatusCode.OK);
            };

            // Get all live streams.
            Get["/LiveStreams"] = p =>
            {
                return(new
                {
                    result = Service.GetLiveStreams()
                });
            };

            // Get the live tuning state of a number of channels.
            //
            // Post-data is an object with:
            //   channels - The channels to get the live state from.
            //   stream - The live stream you want to be ignored (since it's yours), or null.
            //
            // Returns an array with all the live states for the given channels.
            Put["/ChannelsLiveState"] = p =>
            {
                var args = this.Bind <GetChannelsLiveStateArguments>();

                return(new
                {
                    result = Service.GetChannelsLiveState(args.channels, args.stream)
                });
            };

            // Ask the recorder for the give live stream's tuning details (if possible).
            //
            // Post-data is the active live stream.
            //
            // Returns the service tuning details, or null if none are available.
            Put["/Live/TuningDetails"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                ServiceTuning result = null;
                _staContext.Send((s) => { result = Service.GetLiveStreamTuningDetails(stream); }, null);
                return(new
                {
                    result = result
                });
            };

            #endregion

            #region Teletext

            // Ask the recorder whether the given liveStream has teletext.
            //
            // Post-data is the live stream.
            //
            // Returns true if teletext is present.
            Put["/Live/HasTeletext"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                bool result = false;
                _staContext.Send((s) => { result = Service.HasTeletext(stream); }, null);
                return(new
                {
                    result = result
                });
            };

            // Tell the recorder to start grabbing teletext for the given live stream.
            //
            // Post-data is the live stream.
            Put["/Live/Teletext/StartGrabbing"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                _staContext.Send((s) => { Service.StartGrabbingTeletext(stream); }, null);
                return(HttpStatusCode.OK);
            };

            // Tell the recorder to stop grabbing teletext for the given live stream.
            //
            // Post-data is the live stream.
            Put["/Live/Teletext/StopGrabbing"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                _staContext.Send((s) => { Service.StopGrabbingTeletext(stream); }, null);
                return(HttpStatusCode.OK);
            };

            // Tell the recorder to start grabbing teletext for the given live stream.
            //
            // Post-data is the live stream.
            //
            // Returns true if the recorder is grabbing teletext.
            Put["/Live/Teletext/IsGrabbing"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                bool result = false;
                _staContext.Send((s) => { result = Service.IsGrabbingTeletext(stream); }, null);
                return(new
                {
                    result = result
                });
            };

            // Request a teletext page/subpage from the recorder for the given live stream.
            //
            // pageNumber - The teletext page number.
            // subPageNumber - The teletext subpage number.
            //
            // Post-data is the live stream.
            //
            // Returns an object with:
            //   result - The requested page content (base64-encoded), or null if the page was not ready yet.
            //   subPageCount - The total number of subpages of this page.
            Put["/Live/Teletext/GetPage/{pageNumber}/{subPageNumber}"] = p =>
            {
                var stream = this.Bind <LiveStream>();

                int    subPageCount = 0;
                byte[] result       = null;
                _staContext.Send((s) => { result = Service.GetTeletextPageBytes(stream, p.pageNumber, p.subPageNumber, out subPageCount); }, null);
                return(new
                {
                    result = result == null ? null : Convert.ToBase64String(result, Base64FormattingOptions.None),
                    subPageCount = subPageCount
                });
            };

            #endregion
        }