Пример #1
0
        /// <summary>
        ///   Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources
        /// </summary>
        private void Dispose()
        {
            this.clipper?.Dispose();
            this.clipper = null;

            this.toolbar?.Dispose();
            this.toolbar = null;

            this.motionSession?.Dispose();
            this.motionSession = null;

            this.codec?.Dispose();
            this.codec = null;

            this.captureDevice?.Dispose();
            this.captureDevice = null;

            this.stream?.Dispose();
            this.stream = null;

            if (this.handlers != null)
            {
                foreach (Handler handler in this.handlers)
                {
                    handler.Dispose();
                }
            }

            this.handlers = null;
        }
Пример #2
0
        internal void Start()
        {
            this.videoProvider = VideoProviderFactory.Create(this.region);
            this.actions       = this.task.Actions.Select(a => {
                try {
                    // set options if needed
                    if (a.Options != null &&
                        Application.PluginManager.Actions.First(a2 => a2.Type.Name == a.ActionType).Configurable)
                    {
                        // create uninitialized instance
                        var action = FormatterServices.GetSafeUninitializedObject(
                            Type.GetType(a.ActionType, true, true) ??
                            throw new InvalidOperationException(Resources.TaskHelper_NoSuchActionMessage)) as Action;

                        // set options property
                        a.GetType().GetProperty("Options")?.SetValue(action, a.Options);

                        // call parameterless constructor
                        action?.GetType()
                        .GetConstructor(
                            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                            null,
                            Type.EmptyTypes,
                            null)
                        ?.Invoke(action, new object[] { this.codec });
                    }

                    return(Activator.CreateInstance(Type.GetType(a.ActionType) ??
                                                    throw new InvalidOperationException(
                                                        Resources.TaskHelper_NoSuchActionMessage),
                                                    this.codec) as Action);
                } catch (Exception exception) {
                    Log.WriteLine(LogLevel.Warning, $"error initializing action {a.ActionType}: {exception}");

                    // create dummy action for displaying error
                    var action = new Action(this.codec);
                    action.SetStatus(ActionStatus.Failed,
                                     new Exception(Resources.TaskHelper_ActionInitializationFailedCaption, exception));
                    return(action);
                }
            })
                                 .Where(a => a != null)
                                 .ToList();

            this.stream = new MultiStream();
            this.actions.ForEach(a => {
                this.stream.Add(a);
                Application.ActionManager.AddAction(a);
            });

            if (this.codec is ID3D11Codec acceleratedCodec &&
                this.videoProvider is ID3D11VideoProvider acceleratedVideoProvider &&
                acceleratedVideoProvider.SurfacePointer != IntPtr.Zero)
            {
                acceleratedCodec.SurfacePointer = acceleratedVideoProvider.SurfacePointer;
                this.isAcceleratedEncoding      = true;
                Log.WriteLine(LogLevel.Informational, "performing hardware-assisted encoding");
            }

            this.codec?.Initialize(this.videoProvider.CaptureBounds.Size, this.stream);

            this.recordingThread = new Thread(Record)
            {
                Priority = ThreadPriority.Highest
            };
            this.recordingThread.SetApartmentState(ApartmentState.MTA);
            this.recordingThread.Start();

            State = RecordingState.Recording;
            Application.TrayIcon.AnimateIndicator(IndicatorStatus.Recording, 500);
        }
Пример #3
0
        /// <summary>
        ///   Starts the workflow interactively
        /// </summary>
        internal async Task StartAsync(HudContainerInfo container)
        {
            this.currentContainer = container;

            Log.Info($"starting {Type} workflow on {container.ContainerType} container");
            await AcquireRegionAsync();

            if (Region.Size.Width <= Clipper.MinimumWidth || Region.Size.Height <= Clipper.MinimumHeight)
            {
                Log.Trace("capture dismissed");
                Finish();
                return;
            }

            if (this.captureDevice == null || !Equals(this.captureDevice.Size.ToTuple(), Region.Size.ToTuple()))
            {
                // dispose old video provider if size changed
                this.captureDevice?.Dispose();

                // video provider will capture frames
                Log.Debug("creating capture device");
                this.captureDevice = CaptureDeviceFactory.CreateVideoCaptureDevice(
                    Region.Location.X,
                    Region.Location.Y,
                    Region.Size.Width,
                    Region.Size.Height);

                // TODO: create and bind transforms
            }

            // create destination stream, a MultiStream which will act as some kind of stream multiplexer wrapping multiple
            // "sub-streams" onto one
            // TODO: add IImmediateHandler's to the MultiStream
            this.stream = new MultiStream();

            // create codec instance
            if (Type == WorkflowType.Still)
            {
                this.codec = Application.ExtensionManager.CreateObject <StillImageCodec>(Codec.TypeName,
                                                                                         Region.Size.Width,
                                                                                         Region.Size.Height,
                                                                                         this.stream);
            }
            else if (Type == WorkflowType.Motion)
            {
                this.codec = Application.ExtensionManager.CreateObject <VideoCodec>(Codec.TypeName,
                                                                                    Region.Size.Width,
                                                                                    Region.Size.Height,
                                                                                    this.stream);
            }

            // create handlers
            this.handlers = Handlers.Select(handlerData => {
                Handler handler = Application.ExtensionManager.CreateObject <Handler>(
                    handlerData.TypeName,
                    this,
                    this.codec,
                    this.stream,
                    handlerData.Options);

                if (handler is IStreamWrapper streamWrapperHandler)
                {
                    // add stream wrapper to the multi-stream
                    this.stream.Add(streamWrapperHandler.OutputStream);
                    Log.Trace("added stream wrapper handler: " + handlerData.TypeName);
                }

                return(handler);
            }).ToArray();

            if (Type == WorkflowType.Still)
            {
                // still image -- we do not need our stream to be filesystem-backed, as still images can't really be *that*
                // large... I hope
                this.stream.Add(new MemoryStream());

                Log.Info("capturing screen");
                this.captureDevice.AcquireFrame();
                VideoFrame frame = this.captureDevice.LockFrame();

                Log.Info("encoding capture");
                this.codec.Start();
                this.codec.Feed(frame);
                this.codec.Dispose();

                // unlock and release capture
                this.captureDevice.UnlockFrame(frame);
                this.captureDevice.ReleaseFrame();

                Finish();
            }
            else
            {
                // motion capture -- stand back and don't underestimate my expertise on googling
                // save output to a temporary file
                Log.Info("starting recording session");

                // create toolbar
                this.toolbar = new Toolbar(Application.HudManager.GetContainer());
                this.clipper?.AttachToolbar(this.toolbar);

                /* bind toolbar events */
                // option events
                this.toolbar.OnOptionsRequested += delegate(object sender, ToolbarOptionRequestType optionType) {
                    switch (optionType)
                    {
                    case ToolbarOptionRequestType.Generic:
                        try {
                            new OptionsWindow().Show();
                        } catch {
                            /* already open */
                        }

                        break;

                    default:
                        throw new NotImplementedException();
                    }
                };

                // recording control intents
                this.toolbar.OnRecordingIntentReceived += delegate(object sender, ToolbarRecordingControlIntent controlIntent) {
                    try {
                        Log.Info($"received recording control intent: {controlIntent}");
                        this.toolbar.SetPrimaryButtonState(enabled: false);

                        switch (controlIntent)
                        {
                        case ToolbarRecordingControlIntent.Start:
                            /* start recording */
                            if (!(this.motionSession?.Disposed ?? true))
                            {
                                throw new InvalidOperationException("A previous motion capture session has not been closed");
                            }

                            if (!(this.codec is VideoCodec))
                            {
                                throw new NotSupportedException("The current codec does not allow multiple frames");
                            }

                            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                            if (
                                this.captureDevice is DxgiVideoCaptureDevice
                                dxgiCaptureDevice && // make sure our capture device is DXGI-enabled
                                this.codec is IDxgiEnabledVideoCodec dxgiCodec)
                            {
                                // make sure our codec is DXGI-compatible
                                // make sure we have the same D3D device for all sources
                                Device device = dxgiCaptureDevice.Devices.First();
                                if (dxgiCaptureDevice.Devices.All(d => d == device))
                                {
                                    // set device to be bound to the DXGI device manager
                                    // TODO: optimize DxgiVideoProvider so that it only creates one device for each adapter
                                    // TODO: allow ID3DCodec's to operate on multiple DXGI devices
                                    dxgiCodec.BindDevice(device);
                                    Log.Info("bound DXGI device to codec");
                                }
                            }

                            // create motion capture session and change toolbar button intent
                            this.motionSession = new MotionCaptureSession((VideoCodec)this.codec, this.captureDevice);
                            this.toolbar.SetPrimaryButtonState(ToolbarRecordingControlIntent.Stop);
                            break;

                        case ToolbarRecordingControlIntent.Stop:
                            this.motionSession.Dispose();
                            Finish();
                            break;

                        default:
                            throw new NotImplementedException();
                        }
                    } finally {
                        this.toolbar?.SetPrimaryButtonState(enabled: true);
                    }
                };
            }
        }