[FFmpeg-devel] [PATCH 3/4] Implement b frame support.
Philip Langdale
philipl at overt.org
Tue Jan 6 08:36:17 CET 2015
To support b frames, we need to implement a queue of buffers, so that
frames can be held, pending their future reference frames. The nvenc
docs say that we need (num b frames) + 4 buffers, and the maximum
number of b frames is 16, so we need 20 buffers.
While we could allocate them dynamically, it's a small enough quantity
that static allocation and a poor man's circular queue are sufficient.
Note that we need to aggressively fetch output frames on every
iteration to avoid falling behind - as every b frame will have delayed
output.
Signed-off-by: Philip Langdale <philipl at overt.org>
---
libavcodec/nvencoder.c | 86 +++++++++++++++++++++++++++-----------------------
libavcodec/nvencoder.h | 9 ++++--
2 files changed, 53 insertions(+), 42 deletions(-)
diff --git a/libavcodec/nvencoder.c b/libavcodec/nvencoder.c
index f1d432f..2135f55 100644
--- a/libavcodec/nvencoder.c
+++ b/libavcodec/nvencoder.c
@@ -384,24 +384,26 @@ static bool allocate_io(nvencoder_t *nvenc)
create_input_buffer.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED;
create_input_buffer.bufferFmt = nvenc->buffer_fmt;
- nvenc_status = nvenc->api.nvEncCreateInputBuffer(nvenc->inst, &create_input_buffer);
- if (nvenc_status == NV_ENC_SUCCESS)
- {
- nvenc->i_buffer = create_input_buffer.inputBuffer;
- create_input_buffer.inputBuffer = NULL;
- }
-
- // Output buffer
- memset(&create_bitstream_buffer, 0, sizeof(create_bitstream_buffer));
- create_bitstream_buffer.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
- create_bitstream_buffer.size = nvenc->init_params.maxEncodeWidth * nvenc->init_params.maxEncodeHeight;
- create_bitstream_buffer.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
-
- nvenc_status = nvenc->api.nvEncCreateBitstreamBuffer(nvenc->inst, &create_bitstream_buffer);
- if (nvenc_status == NV_ENC_SUCCESS)
- {
- nvenc->o_buffer = create_bitstream_buffer.bitstreamBuffer;
- create_bitstream_buffer.bitstreamBuffer = NULL;
+ // Output buffers
+ for (uint32_t i = 0; i < MAX_BUFFERS; i++) {
+ nvenc_status = nvenc->api.nvEncCreateInputBuffer(nvenc->inst, &create_input_buffer);
+ if (nvenc_status == NV_ENC_SUCCESS)
+ {
+ nvenc->i_buffer[i] = create_input_buffer.inputBuffer;
+ create_input_buffer.inputBuffer = NULL;
+ }
+
+ memset(&create_bitstream_buffer, 0, sizeof(create_bitstream_buffer));
+ create_bitstream_buffer.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
+ create_bitstream_buffer.size = nvenc->init_params.maxEncodeWidth * nvenc->init_params.maxEncodeHeight;
+ create_bitstream_buffer.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
+
+ nvenc_status = nvenc->api.nvEncCreateBitstreamBuffer(nvenc->inst, &create_bitstream_buffer);
+ if (nvenc_status == NV_ENC_SUCCESS)
+ {
+ nvenc->o_buffer[i] = create_bitstream_buffer.bitstreamBuffer;
+ create_bitstream_buffer.bitstreamBuffer = NULL;
+ }
}
return true;
@@ -410,17 +412,18 @@ static bool allocate_io(nvencoder_t *nvenc)
static void deallocate_io(nvencoder_t *nvenc)
{
// Output buffer
- if (nvenc->o_buffer)
- {
- nvenc->api.nvEncDestroyBitstreamBuffer(nvenc->inst, nvenc->o_buffer);
- nvenc->o_buffer = NULL;
- }
-
- // Input buffer
- if (nvenc->i_buffer)
- {
- nvenc->api.nvEncDestroyInputBuffer(nvenc->inst, nvenc->i_buffer);
- nvenc->i_buffer = NULL;
+ for (uint32_t i = 0; i < MAX_BUFFERS; i++) {
+ if (nvenc->o_buffer[i])
+ {
+ nvenc->api.nvEncDestroyBitstreamBuffer(nvenc->inst, nvenc->o_buffer[i]);
+ nvenc->o_buffer[i] = NULL;
+ }
+ // Input buffer
+ if (nvenc->i_buffer[i])
+ {
+ nvenc->api.nvEncDestroyInputBuffer(nvenc->inst, nvenc->i_buffer[i]);
+ nvenc->i_buffer[i] = NULL;
+ }
}
}
@@ -557,8 +560,8 @@ static bool encode_frame(nvencoder_t *nvenc, nvenc_frame_t *nvenc_frame, bool *o
pic_params.version = NV_ENC_PIC_PARAMS_VER;
pic_params.inputWidth = nvenc_frame->width;
pic_params.inputHeight = nvenc_frame->height;
- pic_params.inputBuffer = nvenc->i_buffer;
- pic_params.outputBitstream = nvenc->o_buffer;
+ pic_params.inputBuffer = nvenc->i_buffer[nvenc->current_i % MAX_BUFFERS];
+ pic_params.outputBitstream = nvenc->o_buffer[nvenc->current_o % MAX_BUFFERS];
pic_params.bufferFmt = nvenc->buffer_fmt;
pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
pic_params.frameIdx = nvenc_frame->frame_idx;
@@ -572,11 +575,15 @@ static bool encode_frame(nvencoder_t *nvenc, nvenc_frame_t *nvenc_frame, bool *o
if (nvenc_status == NV_ENC_SUCCESS)
{
*output = true;
+ nvenc->current_i++;
+ nvenc->current_o++;
return true;
}
if (nvenc_status == NV_ENC_ERR_NEED_MORE_INPUT)
{
*output = false;
+ nvenc->current_i++;
+ nvenc->current_o++;
return true;
}
@@ -592,7 +599,7 @@ static bool feed_input(nvencoder_t *nvenc, uint8_t **planes, uint32_t *pitches,
memset(&lock_input_buffer, 0, sizeof(lock_input_buffer));
lock_input_buffer.version = NV_ENC_LOCK_BITSTREAM_VER;
- lock_input_buffer.inputBuffer = nvenc->i_buffer;
+ lock_input_buffer.inputBuffer = nvenc->i_buffer[nvenc->current_i % MAX_BUFFERS];
nvenc_status = nvenc->api.nvEncLockInputBuffer(nvenc->inst, &lock_input_buffer);
if (nvenc_status == NV_ENC_SUCCESS)
@@ -644,8 +651,7 @@ static bool feed_input(nvencoder_t *nvenc, uint8_t **planes, uint32_t *pitches,
}
}
- nvenc->api.nvEncUnlockInputBuffer(nvenc->inst, nvenc->i_buffer);
-
+ nvenc->api.nvEncUnlockInputBuffer(nvenc->inst, nvenc->i_buffer[nvenc->current_i % MAX_BUFFERS]);
return true;
}
@@ -662,7 +668,7 @@ static bool fetch_output(nvencoder_t *nvenc, nvenc_bitstream_t *nvenc_bitstream)
memset(&lock_bitstream, 0, sizeof(lock_bitstream));
lock_bitstream.version = NV_ENC_LOCK_BITSTREAM_VER;
lock_bitstream.doNotWait = 0;
- lock_bitstream.outputBitstream = nvenc->o_buffer;
+ lock_bitstream.outputBitstream = nvenc->o_buffer[nvenc->fetch_o % MAX_BUFFERS];
nvenc_status = nvenc->api.nvEncLockBitstream(nvenc->inst, &lock_bitstream);
if (nvenc_status == NV_ENC_SUCCESS)
@@ -680,7 +686,8 @@ static bool fetch_output(nvencoder_t *nvenc, nvenc_bitstream_t *nvenc_bitstream)
nvenc_bitstream->pic_type = lock_bitstream.pictureType;
}
- nvenc->api.nvEncUnlockBitstream(nvenc->inst, nvenc->o_buffer);
+ nvenc->api.nvEncUnlockBitstream(nvenc->inst, nvenc->o_buffer[nvenc->fetch_o % MAX_BUFFERS]);
+ nvenc->fetch_o++;
return true;
}
@@ -809,13 +816,12 @@ int nvenc_encode(nvenc_t *nvenc, nvenc_frame_t *nvenc_frame, nvenc_bitstream_t *
if (feed_input(_nvenc, nvenc_frame->planes, nvenc_frame->stride, nvenc_frame->format) &&
encode_frame(_nvenc, nvenc_frame, &output, false))
{
- if (!output)
- {
- return 1;
- }
+ // Always try to fetch output to avoid running behind with b-frames
if (fetch_output(_nvenc, nvenc_bitstream))
{
return 0;
+ } else {
+ return 1;
}
}
}
diff --git a/libavcodec/nvencoder.h b/libavcodec/nvencoder.h
index 787ba35..79d9844 100644
--- a/libavcodec/nvencoder.h
+++ b/libavcodec/nvencoder.h
@@ -56,6 +56,8 @@ typedef struct CUctx_st *CUcontext;
#endif
+#define MAX_BUFFERS 20
+
// Main encoding context
typedef struct nvencoder_t
{
@@ -76,8 +78,11 @@ typedef struct nvencoder_t
NV_ENC_CONFIG config;
NV_ENC_BUFFER_FORMAT buffer_fmt;
- NV_ENC_INPUT_PTR i_buffer;
- NV_ENC_OUTPUT_PTR o_buffer;
+ NV_ENC_INPUT_PTR *i_buffer[MAX_BUFFERS];
+ NV_ENC_OUTPUT_PTR *o_buffer[MAX_BUFFERS];
+ uint32_t current_i;
+ uint32_t current_o;
+ uint32_t fetch_o;
struct
{
--
2.1.0
More information about the ffmpeg-devel
mailing list