Reallocate Shared Video Frame Buffer Without Copying

This commit is contained in:
copecone committed 2026-10-11 23:28:47 +09:00
1 parent dd2585ee9f
commit c0e902fc1a
1 file changed
+42 -16
+42 -16
View File
@@ -824,16 +824,7 @@ namespace ADOFAIRenderer.Util.FFmpeg
if (_pSrcFrame == null || _pDstFrame == null || _pPacket == null)
throw new Exception("Failed to allocate frame or packet structure.");
// Set format and size of destination frame, then allocate actual pixel buffer.
// av_frame_get_buffer: Allocates memory matching format, width, height and
// sets data[]/linesize[] pointers. Second argument 0 means default alignment.
_pDstFrame->format = (int)_dstPixFmt;
_pDstFrame->width = _settings.Width;
_pDstFrame->height = _settings.Height;
_pDstFrame->color_range = AVColorRange.AVCOL_RANGE_JPEG;
if (ffmpeg.av_frame_get_buffer(_pDstFrame, 0) < 0)
throw new Exception($"Failed to allocate {_dstPixFmt} frame buffer.");
AllocateDstFrameBuffer();
// sws_getContext: Initialize software scaler (colorspace converter).
// Input: RGBA (Unity capture format, 4 bytes/pixel)
@@ -1201,12 +1192,50 @@ namespace ADOFAIRenderer.Util.FFmpeg
}
}
/// <summary>
/// Sets the format, size and color range of the destination frame (_pDstFrame) and allocates a new pixel buffer for it.
/// av_frame_get_buffer allocates memory matching format, width and height and sets the data[]/linesize[] pointers.
/// Second argument 0 means default alignment.
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html (av_frame_get_buffer)
/// </summary>
private void AllocateDstFrameBuffer()
{
_pDstFrame->format = (int)_dstPixFmt;
_pDstFrame->width = _settings.Width;
_pDstFrame->height = _settings.Height;
_pDstFrame->color_range = AVColorRange.AVCOL_RANGE_JPEG;
var ret = ffmpeg.av_frame_get_buffer(_pDstFrame, 0);
if (ret < 0)
throw new Exception($"Failed to allocate {_dstPixFmt} frame buffer — {GetFFmpegErrorString(ret)} (Code: {ret})");
}
/// <summary>
/// Makes the pixel buffer of the destination frame (_pDstFrame) writable before the next frame is written into it.
///
/// After avcodec_send_frame, the encoder may keep a reference to the buffer of the submitted frame
/// until that frame is encoded (e.g. QSV in system memory input mode). The buffer is then shared and not writable
/// (av_frame_is_writable returns 0).
/// In that case the frame releases its reference (av_frame_unref) and a new buffer is allocated.
/// The encoder keeps the previous buffer until it no longer needs it. The previous contents are not copied,
/// because every plane is overwritten by the next frame.
///
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html (av_frame_is_writable, av_frame_unref)
/// </summary>
private void PrepareDstFrameForWrite()
{
if (ffmpeg.av_frame_is_writable(_pDstFrame) != 0) return;
ffmpeg.av_frame_unref(_pDstFrame);
AllocateDstFrameBuffer();
}
/// <summary>
/// Encodes a frame by directly copying already pre-converted NV12 or YUV420P frame data.
/// </summary>
private void WritePreConvertedFrame(byte* buffer)
{
ffmpeg.av_frame_make_writable(_pDstFrame);
PrepareDstFrameForWrite();
var width = _settings.Width;
var height = _settings.Height;
@@ -1320,20 +1349,17 @@ namespace ADOFAIRenderer.Util.FFmpeg
/// Core logic for actual frame encoding.
///
/// Processing flow:
/// 1. av_frame_make_writable: Check if destination frame buffer is writable
/// 1. PrepareDstFrameForWrite: Makes the destination frame buffer writable
/// (Allocates a new buffer if encoder is still referencing previous frame's buffer)
/// 2. sws_scale: RGBA → YUV420P/NV12 colorspace conversion
/// 3. (QSV hw_frames mode) av_hwframe_transfer_data: Upload NV12 → QSV hardware surface
/// 4. Pass to encoder after setting PTS
///
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html (av_frame_make_writable)
/// Reference: https://ffmpeg.org/doxygen/trunk/group__libsws.html (sws_scale)
/// </summary>
private void WriteFrame()
{
// Ensure writable state since encoder might still be referencing previous frame's buffer.
// Internally allocates a new buffer if referenced.
ffmpeg.av_frame_make_writable(_pDstFrame);
PrepareDstFrameForWrite();
// ── Colorspace conversion: RGBA → YUV420P or NV12 ──
// sws_scale parameters explanation: