/// <inheritdoc /> /// <summary> /// Processes and encodes a frame into the destination stream /// </summary> /// <param name="frame"> /// A <see cref="VideoFrame" /> instance containing information about the locked frame bitmap or texture /// </param> public override void Feed(VideoFrame frame) { // create sample Sample sample = MediaFactory.CreateSample(); MediaBuffer buffer; switch (frame) { case D3D11VideoFrame d3D11Frame: // Direct3D 11 texture // create media buffer MediaFactory.CreateDXGISurfaceBuffer(typeof(Texture2D).GUID, d3D11Frame.Texture, 0, new RawBool(false), out buffer); // set buffer length using (var buffer2D = buffer.QueryInterface <Buffer2D>()) { buffer.CurrentLength = buffer2D.ContiguousLength; } break; case BitmapVideoFrame bmpFrame: // WIC bitmap // create media buffer MediaFactory.CreateWICBitmapBuffer(typeof(Bitmap).GUID, bmpFrame.Bitmap, out buffer); // calculate buffer length buffer.CurrentLength = bmpFrame.BitmapLock.Stride * bmpFrame.Height; // copy pixels Utilities.CopyMemory(buffer.Lock(out _, out _), bmpFrame.BitmapLock.Data.DataPointer, buffer.CurrentLength); // unlock bits buffer.Unlock(); break; case GdiBitmapVideoFrame gdiFrame: // GDI-compatible bitmap // create media buffer // create buffer for copying the bitmap data MediaFactory.Create2DMediaBuffer(frame.Width, frame.Height, new FourCC((int)Format.X8R8G8B8), new RawBool(false), out buffer); // calculate buffer length buffer.CurrentLength = gdiFrame.BitmapData.Stride * frame.Height; // copy data Utilities.CopyMemory(buffer.Lock(out _, out _), gdiFrame.BitmapData.Scan0, buffer.CurrentLength); // unlock bits buffer.Unlock(); break; default: throw new NotSupportedException("The supplied frame does not have a supported type"); } // add buffer to sample sample.AddBuffer(buffer); // set up sample timing if (this.lastFrameTime != 0) { sample.SampleDuration = frame.PresentTime - this.lastFrameTime; } else { // set first frame present time so that we can set the timestamp of subsequent frames relative to the // beggining of the video this.firstFramePresentTime = frame.PresentTime; } sample.SampleTime = frame.PresentTime - this.firstFramePresentTime; this.lastFrameTime = frame.PresentTime; try { this.sinkWriter.WriteSample(this.streamIdx, sample); } catch (SharpDXException) { buffer.Dispose(); sample.Dispose(); } }
/// <inheritdoc /> /// <summary> /// Encodes a single frame and writes the output to the destination stream. /// </summary> /// <param name="frame">Video frame to be encoded</param> /// <exception cref="T:System.InvalidOperationException">A frame has already been fed to the encoder</exception> /// <exception cref="T:System.NotSupportedException">The supplied video frame does not have a supported format</exception> public override void Feed(VideoFrame frame) { using (var frameEncode = new BitmapFrameEncode(this.encoder)) { frameEncode.Initialize(); switch (frame) { case BitmapVideoFrame bmpVideoFrame: // GDI-compatible bitmap (SharpDX.WIC) // already got a WIC bitmap frameEncode.WriteSource(bmpVideoFrame.Bitmap); break; case GdiBitmapVideoFrame gdiVideoFrame: // GDI-compatible bitmap (System.Drawing) // construct a WIC bitmap from a GDI-compatible bitmap using (var bmp = new Bitmap(this.factory, frame.Width, frame.Height, PixelFormat.Format32bppBGRA, new DataRectangle(gdiVideoFrame.BitmapData.Scan0, gdiVideoFrame.BitmapData.Stride))) { frameEncode.WriteSource(bmp); } break; case D3D11VideoFrame d3D11VideoFrame: // ID3D11Texture2D try { // map texture to system memory so that we can access the pixel data DataBox box = d3D11VideoFrame.Texture.Device.ImmediateContext.MapSubresource(d3D11VideoFrame.Texture, 0, MapMode.Read, MapFlags.None); using (var bmp = new Bitmap(this.factory, frame.Width, frame.Height, PixelFormat.Format32bppBGRA, new DataRectangle(box.DataPointer, box.RowPitch))) { frameEncode.WriteSource(bmp); } } finally { // unmap texture from system memory and release related resources using (DeviceContext ctx = d3D11VideoFrame.Texture.Device.ImmediateContext) { ctx.UnmapSubresource(d3D11VideoFrame.Texture, 0); ctx.ClearState(); ctx.Flush(); } #if DEBUG // HACK: we are accessing the `Device` property of the Texture2D in order to map the data to system memory. // Internally, SharpDX gets the Device if not previously accessed. We are releasing the device on the // Dispose() methods of the video providers but this weak reference stays tracked by SharpDX even // after its memory has been released ObjectTracker.UnTrack(d3D11VideoFrame.Texture.Device); #endif } break; } frameEncode.Commit(); this.encoder.Commit(); } }