public MainPresenter(IMainView view) { if (view == null) { throw new ArgumentNullException("view"); } // Load configuration this.configuration = Configuration.Load(); // Initialize recorder this.displayProvider = new DisplayProvider(); this.displaySettings = new DisplaySettings() { Mouse = this.displayProvider.MouseSettings, Tracking = this.displayProvider.TrackingSettings, Watermark = this.displayProvider.WatermarkSettings, }; this.soundProvider = new SoundProvider(); this.soundSettings = new SoundSettings() { DeviceId = this.soundProvider.DeviceId, Format = this.soundProvider.Format, }; this.recorder = new Recorder(); this.recorder.Error += new RecordErrorEventHandler(recorder_Error); // Initialize view this.view = view; this.InitializeView(); // Apply configuration this.ApplyConfiguration(null); // Update View this.view.AllowUpdate = true; // this.UpdateView(); }
private void RecordPrivate(DisplayProvider displayProvider, SoundProvider soundProvider) { bool recordDisplay = displayProvider != null; bool recordSound = soundProvider != null; AviFile aviFile = null; AcmEncoder audioEncoder = null; this.duration = TimeSpan.Zero; try { DisplayFormat videoFormat = null; SoundFormat audioFormat = null; int soundReadInterval = 0; if (recordDisplay) { displayProvider.Open(); videoFormat = displayProvider.Format; } if (recordSound) { soundProvider.Open(); soundReadInterval = (int)Math.Ceiling(soundProvider.BufferLength / 2.0); // ms audioFormat = soundProvider.Format; audioEncoder = soundProvider.GetEncoder(); } // Open AVI file aviFile = new AviFile(); aviFile.Open(fileName, videoFormat, fps, this.compressor, audioFormat, audioEncoder); // Initialize helper variables int frameIndex = 0; int frameDuration = recordDisplay ? (int)(msPerSecond / this.fps) : 0; int frameBufferLength = recordDisplay ? displayProvider.BitmapBytes : 0; int startingFrameIndex = 0; int soundSampleIndex = 0; long startTime = DateTime.Now.Ticks; long lastSoundRead = DateTime.Now.Ticks; TimeSpan prevDuration = TimeSpan.Zero; TimeSpan currentDuration = TimeSpan.Zero; // Update state lock (syncRoot) { this.state = RecordingState.Recording; } if (recordSound) { // Start sound recording soundProvider.Start(); } // Recording loop; this is a long one huh?! do { // Check if paused if (this.state == RecordingState.Paused) { prevDuration = prevDuration.Add(currentDuration); if (recordSound) { // Read remaining sound data and stop sound recording byte[] soundData = soundProvider.Read(true); soundSampleIndex += aviFile.AddSound(soundSampleIndex, soundData, true); soundProvider.Stop(); } // Let the thread executing Pause() know that pause is done this.stateTransition.Set(); while (this.state == RecordingState.Paused) { Thread.Sleep(pauseDelay); } // State is changed, check new state if (this.state == RecordingState.Idle) { return; } // Resume() is called if (recordSound) { soundProvider.Start(); lastSoundRead = DateTime.Now.Ticks; } if (recordDisplay) { startingFrameIndex = frameIndex; } // Reset duration variables startTime = DateTime.Now.Ticks; currentDuration = TimeSpan.Zero; // Let that executing thread known resume is done this.stateTransition.Set(); } // Add a video from if (recordDisplay) { // Render display and add rendered bitmap to the avi file displayProvider.Render(); IntPtr pFrameData = displayProvider.Lock(); try { aviFile.AddFrame(pFrameData, frameIndex, 1, frameBufferLength); } finally { displayProvider.Unlock(); } frameIndex++; } // Add sound if (recordSound) { // Read recorded sound if it's time to do so if ((DateTime.Now.Ticks - lastSoundRead) / ticksPerMs >= soundReadInterval) { // Read sound data SoundFormat sourceFormat = soundProvider.SourceFormat; byte[] soundData = soundProvider.Read(); int samplesRead = (int)(soundData.Length / sourceFormat.BlockAlign); // Get number of out of sync samples TimeSpan durationByNow = prevDuration + new TimeSpan(DateTime.Now.Ticks - startTime); int nOutOfSyncSamples = GetOutOfSyncSamples(soundProvider, soundSampleIndex , durationByNow, samplesRead); if (nOutOfSyncSamples > 0) { // Add silence samples if we have less than expected samples soundSampleIndex += aviFile.AddSilence(soundSampleIndex, nOutOfSyncSamples); } else if (nOutOfSyncSamples < 0) { // Drop read samples as much as possible if we have more than expected samples int nSamplesToKeep = Math.Max(0, samplesRead + nOutOfSyncSamples); if (nSamplesToKeep > 0) { int nBytesToKeep = nSamplesToKeep * sourceFormat.BlockAlign; int nBytesToDrop = soundData.Length - nBytesToKeep; byte[] droppedSoundData = new byte[nBytesToKeep]; Array.Copy(soundData, nBytesToDrop, droppedSoundData, 0, nBytesToKeep); soundData = droppedSoundData; } samplesRead = nSamplesToKeep; } // Add sound data to the avi file if (samplesRead > 0) { soundSampleIndex += aviFile.AddSound(soundSampleIndex, soundData, false); } lastSoundRead = DateTime.Now.Ticks; } } // Synchronize display if (recordDisplay) { long delay = (DateTime.Now.Ticks - startTime) / ticksPerMs - frameDuration * ((frameIndex - startingFrameIndex) - 1); if (delay < frameDuration) { // Extra delay to synchornize with fps Thread.Sleep((int)(frameDuration - delay)); } else { // Calculate how many frames are lost int lostFrames = (int)Math.Floor((decimal)delay / frameDuration); frameIndex += lostFrames; // Extra delay to synchornize with fps Thread.Sleep((int)(frameDuration - delay % frameDuration)); } } else { /* No display recording, just sleep for a while so that sound buffers get filled */ Thread.Sleep(1); } // Update duration currentDuration = new TimeSpan(DateTime.Now.Ticks - startTime); this.duration = prevDuration + currentDuration; } while (this.state != RecordingState.Idle); // Read remaining sound data and stop sound recording if (recordSound) { byte[] soundData = soundProvider.Read(true); soundSampleIndex += aviFile.AddSound(soundSampleIndex, soundData, true); soundProvider.Stop(); } } finally { if (recordSound) { soundProvider.Close(); if (audioEncoder != null) { audioEncoder.Dispose(); } } if (recordDisplay) { displayProvider.Close(); } if (aviFile != null) { aviFile.Dispose(); } } }
private static int GetOutOfSyncSamples(SoundProvider soundProvider, int sampleIndex, TimeSpan duration, int samplesRead) { SoundFormat sourceFormat = soundProvider.SourceFormat; SoundFormat audioFormat = soundProvider.Format; // Get number of source samples read and expected number of source double secondsPassed = (double)duration.Ticks / ticksPerSec; int expectedSamples = (int)(secondsPassed * sourceFormat.SamplesPerSecond); // Get sample ratio double samplesRatio = (double)sourceFormat.SamplesPerSecond / audioFormat.SamplesPerSecond; // Get number of samples in each packet int packetSamples = (int)((soundProvider.PacketLength * sourceFormat.SamplesPerSecond) / 1000.0); // Get total number of source samples read int totalSamplesRead = samplesRead + (int)(sampleIndex * samplesRatio); // Check if more than two packets are missing or out of sync if ((totalSamplesRead + 2 * packetSamples < expectedSamples) || (totalSamplesRead > 2 * packetSamples + expectedSamples)) { return expectedSamples - totalSamplesRead; } // Few or zero samples are out of sync // We can ignore this now. return 0; }
public void Record(DisplayProvider displayProvider, SoundProvider soundProvider) { if (this.fileName == null) { throw new InvalidOperationException("FileName is not specified"); } // Check state lock (syncRoot) { if (state != RecordingState.Idle) { throw new InvalidOperationException("Invalid state."); } state = RecordingState.Preparing; } record = new RecordDelegate(this.RecordPrivate); AsyncCallback callback = new AsyncCallback(this.RecordCallBack); record.BeginInvoke(displayProvider, soundProvider, callback, null); // Start a new thread for recording }