Ejemplo n.º 1
0
        public void Start()
        {
            try
            {
                // Register TCP Channel
                channel = new TcpServerChannel("Archiver", Constants.TCPListeningPort);
                System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(channel, false);

                // Register the SAO
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(ArchiveServer),
                                                                   "ArchiveServer", WellKnownObjectMode.Singleton);

                string configFileName = System.Reflection.Assembly.GetEntryAssembly().Location + ".config";
                if (System.IO.File.Exists(configFileName))
                {
                    RemotingConfiguration.Configure(configFileName, false);
                }

                eventLog.WriteEntry(Strings.ArchiveServiceStarted, EventLogEntryType.Information,
                                    ArchiveServiceEventLog.ID.Information);
            }
            catch
            {
                if (channel != null)
                {
                    channel.StopListening(null);
                    ChannelServices.UnregisterChannel(channel);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Records the conference.
        /// </summary>
        /// <param name="conferenceDescription">A description of the conference being recorded.</param>
        /// <param name="venueIdentifier">Venue name for the conference.</param>
        /// <param name="venue">Venue to record from.</param>
        /// <returns>The number of ongoing requests to record this conference.</returns>
        public int Record(string conferenceDescription, string venueIdentifier, IPEndPoint venue)
        {
            try
            {
                lock (this)
                {
                    CheckForIPv6(venue);

                    ConferenceRecorder ongoingRecording = CheckVenueForPlayback(venue);

                    int    refs;
                    object refsObj = references[venue];
                    if (refsObj == null)
                    {
                        refs = 0;
                    }
                    else
                    {
                        refs = (int)refsObj;
                    }

                    // If a recording exists, the references to it should not equal zero
                    Debug.Assert(ongoingRecording == null || refs != 0);

                    ConferenceRecorder recorder = null;
                    if (refs == 0)
                    {
                        // Start a ConferenceRecorder
                        eventLog.WriteEntry("Recording a new conference."
                                            + "\n  Conference Description: " + conferenceDescription
                                            + "\n  Venue Identifier: " + venueIdentifier
                                            + "\n  Venue IP/Port: " + venue,
                                            EventLogEntryType.Information, ArchiveServiceEventLog.ID.Starting);

                        recorder = new ConferenceRecorder();
                        recorder.RecordConference(conferenceDescription, venueIdentifier, venue);

                        sessions.Add(venue, recorder);
                    }
                    else
                    {
                        recorder = ongoingRecording;
                    }

                    ++refs;
                    references[venue] = refs;

                    return(refs);
                }
            }
            catch (Exception ex)
            {
                eventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error, ArchiveServiceEventLog.ID.Error);
                throw;
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// we have a new stream - start recording
        /// </summary>
        /// <param name="SSRC">what's arrived</param>
        private void OnNewRtpStream(object sender, RtpEvents.RtpStreamEventArgs ea)
        {
            if (this.rtpSession != sender)
            {
                return;
            }

            RtpStream stream = ea.RtpStream;

            if (streams.ContainsKey(stream.SSRC))
            {
                return;
            }

            Trace.WriteLine("New stream found: " + stream.Properties.CName + ", " + stream.Properties.Name);

            if (participants[stream.Properties.CName] != null)
            {
                ParticipantWrapper participant = (ParticipantWrapper)participants[stream.Properties.CName];

                StreamRecorder sm = new StreamRecorder(participant.participantID, stream, perfCounter);
                streams[stream.SSRC] = sm;

                perfCounter.AddInstanceForCollection(sm);

                // Make sure we don't stop recording now
                if (this.stopTimer != null)
                {
                    stopTimer.Dispose();
                    stopTimer = null;
                }
            }
            else
            {
                eventLog.WriteEntry("Detected stream for unknown participant, ignoring.  CNAME: " +
                                    stream.Properties.CName, EventLogEntryType.Error, ArchiveServiceEventLog.ID.Error);
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Called by the threadpool thread when canPopulate.Set is called.
        /// </summary>
        private void InitiatePopulation(object o, bool timedOut)
        {
            Debug.Assert(!timedOut);   // I don't think this ever happens, but just to be safe...

            if (!timedOut)
            {
                if (populating == false)
                {
                    // (pbristow) Why does this happen???  (as of 15-Sep-04, I can't recall having seen this happen.  did it ever?)
                    eventLog.WriteEntry(Strings.PopulateCalledOnSenderError, EventLogEntryType.Error,
                                        ArchiveServiceEventLog.ID.ImproperPopulateCall);
                }
                else
                {
                    // lock to ensure we'e getting the correct startingTick value
                    lock ( populatingLockObj )
                    {
                        // While Populate takes starting ticks as a param, we can use the instance variable we set in EnablePopulation
                        Populate(this.startingTick);
                    }
                }
            }
        }
Ejemplo n.º 5
0
        private void ProcessFrame(object argument)
        {
            lock (this)
            {
                FrameWithTicks frame = (FrameWithTicks)argument;

                this.receivedBytes = this.receivedBytes + (long)frame.frame.Length;
                ++receivedFrames;

                // Verify the frame fits in a buffer.  If not, write it to the database directly.
                if (frame.frame.Length < Constants.BufferSize)
                {
                    // if there is no room in the current buffer
                    if (!currentBuffer.HasRoom(frame.frame.Length))
                    {
                        // With bufferAvailable we try to prevent writing the dropped frame event to the EventLog too often
                        //  Incidentally, that means we'll be writing once per every time we use up the buffer pool.  If we
                        //  have a sudden flood of data, that means it'll only get written once or twice, which helps perf.
                        bool bufferAvailable = false;

                        if (currentBuffer.Available)
                        {
                            currentBuffer.EnableWrite();
                            ++writtenBuffers;
                            bufferAvailable = true;
                        }

                        if (!IncrementBuffer())
                        {
                            // Discard the packet by doing nothing with it

                            if (bufferAvailable)
                            {
                                // Warn the user
                                eventLog.WriteEntry("Dropping a frame in StreamRecorder::OnFrameReceived due to no buffers being available.",
                                                    EventLogEntryType.Error, ArchiveServiceEventLog.ID.FrameDropped);
                            }

                            ++droppedFrames;
                            return;
                        }
                    }

                    currentBuffer.AddFrame(frame);
                }
                else
                {
                    Trace.WriteLine("Writing frame directly to disk.  Frame size: " + frame.frame.Length);

                    // Force pending data to be written to disk
                    if (currentBuffer.Available)
                    {
                        currentBuffer.EnableWrite();
                        ++writtenBuffers;
                    }

                    // Get our new buffer
                    IncrementBuffer();

                    // And write this frame directly to disk
                    BufferRecorder.DirectWrite(frame, this.streamID);
                }

                ++writtenFrames;
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Writes the buffer to SQL and empties it.
        /// </summary>
        private void WriteData()
        {
            lock (this)
            {
                // This and the lock are to circumvent a small multithreading dillema
                if (this.Available)
                {
                    Trace.WriteLine(String.Format("BufferRecorder::WriteData called when Available at time {0}.  Returning.", DateTime.Now.Ticks));
                    return;
                }

                // Going to try a spin-wait just to see if we can resolve this multithreading problem
                while (this.writing)
                {
                    Thread.Sleep(10);
                }

                writing = true;
                bool overflowed;

                do
                {
                    overflowed = false;

                    try
                    {
                        // Don't try to write an empty buffer
                        if (currentIndex > 0)
                        {
#if TIMING
                            long startTimer = DateTime.Now.Ticks;
#endif

                            // TODO: Find out why this gets called twice on one data set OR find a workaround.
                            Trace.WriteLine(String.Format("BufferRecorder::WriteData doing SBAI on buffer {0}, stream {1} at {2}",
                                                          number, streamID, DateTime.Now.Ticks));
                            DBHelper.SaveBufferAndIndices(
                                this.streamID,
                                this.indices,
                                this.currentIndex,
                                buffer);

#if TIMING
                            long takenTime = DateTime.Now.Ticks - startTimer;
                            Trace.WriteLine("TIMING: SBAI took: " + (takenTime / Constants.TicksPerMs) + " ms");
#endif
                        }
                    }
                    catch (SqlException ex)
                    {
                        // Catch the overflow case, where we have more data than the 'int' type holds
                        if (ex.Message.ToLower().IndexOf("overflow") >= 0)
                        {
                            overflowed = true; // by setting this, we directly try to write the data again
                            this.Overflowed(this, EventArgs.Empty);
                        }

                        // Two exceptions are seen here:
                        //   Timeouts in SQL due to taking a *really* bad performance beating
                        //   OR Constraint violation in Frame table due to unusual multithreading problem not yet solved (mentioned above)
                        // Dont do anything, b/c failed DB ops are already event-logged.  Just move on & hope that the stream is playable.
                    }
                    catch (InvalidOperationException ex)
                    {
                        // In the worst of performance cases, we run out of pooled connections and get this exception
                        eventLog.WriteEntry("Database operation failed.  Exception: \n" + ex.ToString(), EventLogEntryType.Error,
                                            ArchiveServiceEventLog.ID.DBOpFailed);

                        // Again, ignore & move on, dropping the frames.
                    }
                } // In the specific case where we've overflowed, try again immediately (to hopefully preempt other buffers from going out-of-order)
                while (overflowed);

                writing     = false;
                isAvailable = true;

                bufferIndex  = 0;
                currentIndex = 0;

                Trace.WriteLine("BufferRecorder::WriteData completed.");
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Sends all of the frames this stream needs to send by the given time boundary.
        /// Also calls populate on the underlying buffer as necessary
        /// </summary>
        /// <returns>the time of the next frame to be sent, in ticks</returns>
        public long OnWakeUp(long timeBoundary)
        {
            if (this.currentStreamEnded)
            {
                return(long.MaxValue);
            }

#if TIMING
            long startTimer, takenTime;
#endif

            // We must return the time until the next frame.  Keep up with it via this variable.
            long timeUntilFrame = 0;

            // Allowing the rtpSender to be created late allows the playback to properly replicate the situation seen
            // during recording.  "Late joiner" support, if you will.  This also deals with the underlying 500ms delay
            // implanted into RtpSession.CreateRtpSender.

            if (rtpSender != null)
            {
#if TIMING
                startTimer = DateTime.Now.Ticks;
#endif

                #region Send Frames
                if (!activeBuffer.Populating && !activeBuffer.Empty)
                {
                    if (this.outOfDataLoggedFlag)
                    {
                        this.outOfDataLoggedFlag = false;
                    }

                    totalFramesSent += activeBuffer.SendFrames(rtpSender, timeBoundary, out timeUntilFrame, ref totalBytesSent, ref totalLateness);
                }
                #endregion

#if TIMING
                takenTime = DateTime.Now.Ticks - startTimer;
                if (takenTime > Constants.TicksSpent)
                {
                    Trace.WriteLine("TIMING: TIME WASTED SENDING FRAMES: " + (takenTime / Constants.TicksPerMs) + " ms");
                }

                startTimer = DateTime.Now.Ticks;
#endif

                #region Empty Buffer
                if (activeBuffer.Empty)
                {
                    if (!activeBuffer.EndOfStream)  // not the end of the stream, so we go get new data to play out
                    {
                        // guaranteed atomic
                        BufferPlayer oldBuffer = activeBuffer;

                        // Get the next buffer
                        int          newBufferIndex = (activeBufferIndex + 1) % Constants.BuffersPerStream;
                        BufferPlayer newBuffer      = buffers[newBufferIndex];

                        if (!newBuffer.Populating)      // we can't enable population on the old buffer until we can get the 'LastTick' from the new buffer
                        {
                            if (!newBuffer.EndOfStream) // if we're at the end of the stream, don't enable population on the old buffer
                            {
                                // Broken here so we only work with 2 buffers
                                oldBuffer.EnablePopulation(newBuffer.LastTick + 1);
                            }
                        }
                        else // we're in a performance rut and have run out of data
                        {
                            // the "outOfDataLoggedFlag" prevents us from event-logging too much
                            if (!outOfDataLoggedFlag)
                            {
                                // The database can't keep up; both buffers are empty!
                                eventLog.WriteEntry("StreamPlayer::OnWakeUp reached and no data in buffers! ID: " + streamID, EventLogEntryType.Warning,
                                                    ArchiveServiceEventLog.ID.EmptyBuffersInPlayback);

                                this.emptyErrors++;
                                this.outOfDataLoggedFlag = true;
                            }
                        }

                        timeUntilFrame = 0; // we need to come back quick to see when the first frame is in the next buffer

                        // save the "new" buffer as the "current" one
                        activeBufferIndex = newBufferIndex;
                        activeBuffer      = buffers[newBufferIndex];
                    }
                    else // end of stream
                    {
                        if (!currentStreamEnded)
                        {
                            Trace.WriteLine("End of stream reached.  Stopping sending this stream.  ID: " + streamID);

                            currentStreamEnded = true;

                            DisposeSender();

                            ThreadPool.QueueUserWorkItem(new WaitCallback(FireEndOfStream), this);
                        }

                        timeUntilFrame = long.MaxValue;
                    }
                }
                #endregion

#if TIMING
                takenTime = DateTime.Now.Ticks - startTimer;
                if (takenTime > Constants.TicksSpent)
                {
                    Trace.WriteLine("TIMING: TIME WASTED ON EMPTY BUFFER: " + (takenTime / Constants.TicksPerMs) + " ms");
                }
#endif
            }
            else // Sender isn't created yet.  Check if we need to.
            {
#if TIMING
                startTimer = DateTime.Now.Ticks;
#endif

                // Pri2: Change this to be compatable with use of the "playback speed" feature.  TimeBoundary is speed-based...
                timeUntilFrame = (firstStreamTicks - timeBoundary);

                #region Sender creation
                if (timeUntilFrame <= Constants.SenderCreationLeadTime)  // <x> ms of "prep time" to get fired up
                {
                    if (!createSenderFired)
                    {
                        createSenderFired = true;
                        Trace.WriteLine("RtpSender being created for stream: " + streamID);
                        ThreadPool.QueueUserWorkItem(new WaitCallback(CreateSender));
                    }
                }
                else
                {
                    timeUntilFrame -= Constants.SenderCreationLeadTime;
                }
                #endregion

#if TIMING
                takenTime = DateTime.Now.Ticks - startTimer;
                if (takenTime > Constants.TicksSpent)
                {
                    Trace.WriteLine("TIMING: TIME WASTED CREATING SENDER: " + (takenTime / Constants.TicksPerMs) + " ms");
                }
#endif
            }

            return(timeUntilFrame);
        }
        public void Play(IPEndPoint venue, int[] streams, bool receiveData)
        {
            if (this.playing)
            {
                throw new InvalidOperationException(Strings.PlayerAlreadyPlayingError);
            }
            if (streams == null)
            {
                throw new ArgumentNullException(Strings.StreamsCannotBeNull);
            }

            venueIPE = venue;

            // Create an RtpSession
            RtpParticipant me = new RtpParticipant(Constants.PersistenceCName,
                                                   string.Format(CultureInfo.CurrentCulture, Strings.Playing, Constants.PersistenceName));

            rtpSession = new RtpSession(venue, me, true, receiveData);

            // andrew: connect to diagnostic server
            rtpSession.VenueName = "Archived: " + venue.ToString();

            // Hook the static stream-ended event
            StreamPlayer.EndOfStreamReached += new EventHandler(EndOfStreamReached);

            // Create a new perf counter for this ConferencePlayer instance
            this.perfCounter = new ConferencePlayerPC(venue.ToString());

            // Keep track of the streamPlayers
            ArrayList avStreams    = new ArrayList();
            ArrayList otherStreams = new ArrayList();

            // Find the first stored ticks of all the streams while creating them...
            this.firstStoredTick = long.MaxValue;

            // Create all of our StreamPlayers (one per stream...)
            for (int i = 0; i < streams.Length; i++)
            {
                StreamPlayer newStream = null;

                try
                {
                    newStream = new StreamPlayer(rtpSession, streams[i], perfCounter);
                }
                catch (Exception ex)
                {
                    eventLog.WriteEntry(String.Format(CultureInfo.CurrentCulture, Strings.StreamWithBadDataReached,
                                                      streams[i], ex.ToString()), EventLogEntryType.Warning,
                                        ArchiveServiceEventLog.ID.BadStreamInDB);
                }

                if (newStream != null)
                {
                    perfCounter.AddInstanceForCollection(newStream);

                    if (newStream.FirstStreamsTicks < this.firstStoredTick)
                    {
                        this.firstStoredTick = newStream.FirstStreamsTicks;
                    }

                    // Add the new StreamPlayer to the right collection
                    // Pri3: Consider other methods here.  Maybe a smarter detection of large frame sizes,
                    //  or initializing the stream so it has enough packets ahead of time?
                    if (newStream.Payload == PayloadType.dynamicAudio || newStream.Payload == PayloadType.dynamicVideo)
                    {
                        avStreams.Add(newStream);
                    }
                    else // RTDocs stream or other large-payload stream, most likely
                    {
                        otherStreams.Add(newStream);
                    }
                }
            }

            // Start the StreamsGroupPlayers
            // Pri2: Change this to be compatable with use of the "playback speed" feature.
            long startTime = DateTime.Now.Ticks + 1500 * Constants.TicksPerMs; // we'll start in 1.5 seconds (for init time)

            // Create the StreamsGroupPlayer(s)
            avPlayer    = new StreamsGroupPlayer(avStreams, this.firstStoredTick, startTime);
            otherPlayer = new StreamsGroupPlayer(otherStreams, this.firstStoredTick, startTime);

            this.playing = true;
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Saves the conference details when we start recording.
        /// this is to allow the incoming rtp participants and streams to be associated with a particular conference instance
        /// </summary>
        /// <param name="groupID">the group that this conference is for</param>
        /// <param name="venueId">the venue that this conference is for</param>
        /// <param name="description">a friendly description of this conference</param>
        /// <param name="startTime">the time the conference starts</param>
        /// <returns>an identifier for the conference</returns>
        static public int CreateConference(string conferenceDescription, string venueIdentifier, DateTime startTime)
        {
            try
            {
                SqlConnection conn = new SqlConnection(Constants.SQLConnectionString);

                SqlCommand cmd = new SqlCommand("CreateConference", conn);
                cmd.CommandType = CommandType.StoredProcedure;

                SqlParameter description = cmd.Parameters.Add("@description", SqlDbType.VarChar, 50);
                description.Direction = ParameterDirection.Input;
                description.Value     = conferenceDescription;

                SqlParameter venue_identifier = cmd.Parameters.Add("@venue_identifier", SqlDbType.VarChar, 50);
                venue_identifier.Direction = ParameterDirection.Input;
                venue_identifier.Value     = venueIdentifier;

                SqlParameter start_dttm = cmd.Parameters.Add("@start_dttm", SqlDbType.DateTime);
                start_dttm.Direction = ParameterDirection.Input;
                start_dttm.Value     = startTime;

                SqlParameter conference_id = cmd.Parameters.Add("@conference_id", SqlDbType.Int);
                conference_id.Direction = ParameterDirection.ReturnValue;

                conn.Open();
                cmd.ExecuteNonQuery();
                conn.Close();
                return((int)conference_id.Value);
            }
            catch (SqlException ex)
            {
                eventLog.WriteEntry(string.Format(CultureInfo.CurrentCulture, Strings.DatabaseOperationFailedError,
                                                  ex.ToString()), EventLogEntryType.Error, ArchiveServiceEventLog.ID.DBOpFailed);
                throw;
            }
        }
Ejemplo n.º 10
0
        private void OnNewRtpParticipant(object sender, RtpEvents.RtpParticipantEventArgs ea)
        {
            try {
                RtpParticipant participant = ea.RtpParticipant;

                eventLog.WriteEntry("OnNewRtpParticipant: " + participant.CName,
                                    EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);

                if (this.rtpSession != sender)
                {
                    eventLog.WriteEntry("OnNewRtpParticipant: this.rtpSession and sender don't match.",
                                        EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                    if (this.rtpSession == null)
                    {
                        //Note this can happen because participants are added during the rtpSession constructor!!
                        eventLog.WriteEntry("OnNewRtpParticipant: this.rtpSession is null.",
                                            EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                    }
                    if (sender == null)
                    {
                        eventLog.WriteEntry("OnNewRtpParticipant: sender is null.",
                                            EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                    }
                    //return;
                }

                //eventLog.WriteEntry(string.Format(CultureInfo.CurrentCulture, "New participant: {0}", participant.CName));

                // we want to ignore ourselves
                if (participant.CName != Constants.PersistenceCName)
                {
                    // Make sure this isn't someone who briefly lost connectivity.
                    if (!participants.ContainsKey(participant.CName))
                    {
                        string newPartName = participant.Name;
                        if (newPartName == null || newPartName == String.Empty)
                        {
                            newPartName = participant.CName;
                        }

                        int participantID = DBHelper.CreateParticipant(
                            conferenceID,
                            participant.CName,
                            newPartName);

                        participants.Add(participant.CName, new ParticipantWrapper(participant, participantID));

                        eventLog.WriteEntry("OnNewRtpParticipant completed participant Add.",
                                            EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                    }
                    else
                    {
                        eventLog.WriteEntry("OnNewRtpParticipant already in participants hashtable ",
                                            EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                    }

                    foreach (uint ssrc in participant.SSRCs)
                    {
                        if (this.rtpSession == null)
                        {
                            eventLog.WriteEntry("OnNewRtpParticipant: Failed to add streams because this.rtpSession is null.",
                                                EventLogEntryType.Warning, ArchiveServiceEventLog.ID.Warning);
                        }
                        else
                        {
                            OnNewRtpStream(this.rtpSession, new RtpEvents.RtpStreamEventArgs(rtpSession.Streams[ssrc]));
                        }
                    }
                }
                else
                {
                    eventLog.WriteEntry("OnNewRtpParticipant ignoring ourself. ",
                                        EventLogEntryType.Information, ArchiveServiceEventLog.ID.Information);
                }
            }
            catch (Exception e) {
                eventLog.WriteEntry("OnNewRtpParticipant exception: " + e.ToString(),
                                    EventLogEntryType.Warning, ArchiveServiceEventLog.ID.Warning);
            }
        }