SHA256
Reallocate Shared Video Frame Buffer Without Copying
This commit is contained in:
1 file changed
+42
-16
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user