/// <summary> /// Copy the frame content to a <xref href="System.Byte"/>[] buffer as a contiguous block of memory /// containing the Y, U, and V planes one after another, and the alpha plane at the end if /// present. /// </summary> /// <param name="buffer">The destination buffer to copy the frame to.</param> public void CopyTo(byte[] buffer) { unsafe { fixed(void *ptr = buffer) { // Note : System.Buffer.MemoryCopy() essentially does the same (without stride), but gets transpiled by IL2CPP // into the C++ corresponding to the IL instead of a single memcpy() call. This results in a large overhead, // especially in Debug config where one can lose 5-10 FPS just because of this. void *dst = ptr; ulong sizeY = (ulong)strideY * height; PeerConnection.MemCpyStride(dst, strideY, (void *)dataY, strideY, (int)width, (int)height); dst = (void *)((ulong)dst + sizeY); ulong sizeU = (ulong)strideU * height / 2; PeerConnection.MemCpyStride(dst, strideU, (void *)dataU, strideU, (int)width / 2, (int)height / 2); dst = (void *)((ulong)dst + sizeU); ulong sizeV = (ulong)strideV * height / 2; PeerConnection.MemCpyStride(dst, strideV, (void *)dataV, strideV, (int)width / 2, (int)height / 2); if (dataA.ToPointer() != null) { dst = (void *)((ulong)dst + sizeV); PeerConnection.MemCpyStride(dst, strideA, (void *)dataA, strideA, (int)width, (int)height); } } } }
/// <summary> /// Try to enqueue a new video frame encoded in raw ARGB format. /// If the internal queue reached its maximum capacity, do nothing and drop the frame. /// </summary> /// <param name="frame">The video frame to enqueue</param> /// <returns>Return <c>true</c> if the frame was enqueued successfully, or <c>false</c> if it was dropped</returns> /// <remarks>This should only be used if the queue has storage for a compatible video frame encoding.</remarks> public bool Enqueue(ARGBVideoFrame frame) { double curTime = _stopwatch.Elapsed.TotalMilliseconds; // Always update queued time, which refers to calling Enqueue(), even // if the queue is full and the frame is dropped. float queuedDt = (float)(curTime - _lastQueuedTimeMs); _lastQueuedTimeMs = curTime; // Try to get some storage for that new frame ulong byteSize = (ulong)frame.stride * frame.height * 4; T storage = GetStorageFor(byteSize); if (storage == null) { // Too many frames in queue, drop the current one float droppedDt = (float)(curTime - _lastDroppedTimeMs); _lastDroppedTimeMs = curTime; _droppedFrameTimeAverage.Push(1000f / droppedDt); return(false); } // Copy the new frame to its storage unsafe { fixed(void *dst = storage.Buffer) { void *src = (void *)frame.data; PeerConnection.MemCpyStride(dst, frame.stride, (void *)frame.data, frame.stride, (int)frame.width * 4, (int)frame.height); } } storage.Width = frame.width; storage.Height = frame.height; // Enqueue for later delivery _frameQueue.Enqueue(storage); _queuedFrameTimeAverage.Push(1000f / queuedDt); _droppedFrameTimeAverage.Push(0f); return(true); }