fftools/thread_queue: switch from AVFifo+ObjPool to AVContainerFifo

The queue needs to track each frame/packet's stream index, this is
achieved by maintaining a parallel AVFifo instance for that purpose.
This is simpler than implementing custom AVContainerFifo callbacks.
This commit is contained in:
Anton Khirnov 2024-12-11 11:02:37 +01:00
parent 2ac34d0854
commit 8e0cceffa0
4 changed files with 42 additions and 63 deletions

View file

@ -375,7 +375,6 @@ static int queue_alloc(ThreadQueue **ptq, unsigned nb_streams, unsigned queue_si
enum QueueType type) enum QueueType type)
{ {
ThreadQueue *tq; ThreadQueue *tq;
ObjPool *op;
if (queue_size <= 0) { if (queue_size <= 0) {
if (type == QUEUE_FRAMES) if (type == QUEUE_FRAMES)
@ -393,18 +392,11 @@ static int queue_alloc(ThreadQueue **ptq, unsigned nb_streams, unsigned queue_si
av_assert0(queue_size == DEFAULT_FRAME_THREAD_QUEUE_SIZE); av_assert0(queue_size == DEFAULT_FRAME_THREAD_QUEUE_SIZE);
} }
op = (type == QUEUE_PACKETS) ? objpool_alloc_packets() : tq = tq_alloc(nb_streams, queue_size,
objpool_alloc_frames(); (type == QUEUE_PACKETS) ? THREAD_QUEUE_PACKETS : THREAD_QUEUE_FRAMES);
if (!op) if (!tq)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
tq = tq_alloc(nb_streams, queue_size, op,
(type == QUEUE_PACKETS) ? pkt_move : frame_move);
if (!tq) {
objpool_free(&op);
return AVERROR(ENOMEM);
}
*ptq = tq; *ptq = tq;
return 0; return 0;
} }

View file

@ -44,14 +44,4 @@ static inline int err_merge(int err0, int err1)
return (err0 < 0) ? err0 : FFMIN(err1, 0); return (err0 < 0) ? err0 : FFMIN(err1, 0);
} }
static inline void pkt_move(void *dst, void *src)
{
av_packet_move_ref(dst, src);
}
static inline void frame_move(void *dst, void *src)
{
av_frame_move_ref(dst, src);
}
#endif // FFTOOLS_FFMPEG_UTILS_H #endif // FFTOOLS_FFMPEG_UTILS_H

View file

@ -20,13 +20,16 @@
#include <string.h> #include <string.h>
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/container_fifo.h"
#include "libavutil/error.h" #include "libavutil/error.h"
#include "libavutil/fifo.h" #include "libavutil/fifo.h"
#include "libavutil/frame.h"
#include "libavutil/intreadwrite.h" #include "libavutil/intreadwrite.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
#include "libavutil/thread.h" #include "libavutil/thread.h"
#include "objpool.h" #include "libavcodec/packet.h"
#include "thread_queue.h" #include "thread_queue.h"
enum { enum {
@ -34,19 +37,14 @@ enum {
FINISHED_RECV = (1 << 1), FINISHED_RECV = (1 << 1),
}; };
typedef struct FifoElem {
void *obj;
unsigned int stream_idx;
} FifoElem;
struct ThreadQueue { struct ThreadQueue {
int *finished; int *finished;
unsigned int nb_streams; unsigned int nb_streams;
AVFifo *fifo; enum ThreadQueueType type;
ObjPool *obj_pool; AVContainerFifo *fifo;
void (*obj_move)(void *dst, void *src); AVFifo *fifo_stream_index;
pthread_mutex_t lock; pthread_mutex_t lock;
pthread_cond_t cond; pthread_cond_t cond;
@ -59,14 +57,8 @@ void tq_free(ThreadQueue **ptq)
if (!tq) if (!tq)
return; return;
if (tq->fifo) { av_container_fifo_free(&tq->fifo);
FifoElem elem; av_fifo_freep2(&tq->fifo_stream_index);
while (av_fifo_read(tq->fifo, &elem, 1) >= 0)
objpool_release(tq->obj_pool, &elem.obj);
}
av_fifo_freep2(&tq->fifo);
objpool_free(&tq->obj_pool);
av_freep(&tq->finished); av_freep(&tq->finished);
@ -77,7 +69,7 @@ void tq_free(ThreadQueue **ptq)
} }
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size, ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
ObjPool *obj_pool, void (*obj_move)(void *dst, void *src)) enum ThreadQueueType type)
{ {
ThreadQueue *tq; ThreadQueue *tq;
int ret; int ret;
@ -104,12 +96,16 @@ ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
goto fail; goto fail;
tq->nb_streams = nb_streams; tq->nb_streams = nb_streams;
tq->fifo = av_fifo_alloc2(queue_size, sizeof(FifoElem), 0); tq->type = type;
tq->fifo = (type == THREAD_QUEUE_FRAMES) ?
av_container_fifo_alloc_avframe(0) : av_container_fifo_alloc_avpacket(0);
if (!tq->fifo) if (!tq->fifo)
goto fail; goto fail;
tq->obj_pool = obj_pool; tq->fifo_stream_index = av_fifo_alloc2(queue_size, sizeof(unsigned), 0);
tq->obj_move = obj_move; if (!tq->fifo_stream_index)
goto fail;
return tq; return tq;
fail: fail:
@ -132,23 +128,21 @@ int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
goto finish; goto finish;
} }
while (!(*finished & FINISHED_RECV) && !av_fifo_can_write(tq->fifo)) while (!(*finished & FINISHED_RECV) && !av_fifo_can_write(tq->fifo_stream_index))
pthread_cond_wait(&tq->cond, &tq->lock); pthread_cond_wait(&tq->cond, &tq->lock);
if (*finished & FINISHED_RECV) { if (*finished & FINISHED_RECV) {
ret = AVERROR_EOF; ret = AVERROR_EOF;
*finished |= FINISHED_SEND; *finished |= FINISHED_SEND;
} else { } else {
FifoElem elem = { .stream_idx = stream_idx }; ret = av_fifo_write(tq->fifo_stream_index, &stream_idx, 1);
ret = objpool_get(tq->obj_pool, &elem.obj);
if (ret < 0) if (ret < 0)
goto finish; goto finish;
tq->obj_move(elem.obj, data); ret = av_container_fifo_write(tq->fifo, data, 0);
if (ret < 0)
goto finish;
ret = av_fifo_write(tq->fifo, &elem, 1);
av_assert0(ret >= 0);
pthread_cond_broadcast(&tq->cond); pthread_cond_broadcast(&tq->cond);
} }
@ -161,18 +155,21 @@ finish:
static int receive_locked(ThreadQueue *tq, int *stream_idx, static int receive_locked(ThreadQueue *tq, int *stream_idx,
void *data) void *data)
{ {
FifoElem elem;
unsigned int nb_finished = 0; unsigned int nb_finished = 0;
while (av_fifo_read(tq->fifo, &elem, 1) >= 0) { while (av_container_fifo_read(tq->fifo, data, 0) >= 0) {
if (tq->finished[elem.stream_idx] & FINISHED_RECV) { unsigned idx;
objpool_release(tq->obj_pool, &elem.obj); int ret;
ret = av_fifo_read(tq->fifo_stream_index, &idx, 1);
av_assert0(ret >= 0);
if (tq->finished[idx] & FINISHED_RECV) {
(tq->type == THREAD_QUEUE_FRAMES) ?
av_frame_unref(data) : av_packet_unref(data);
continue; continue;
} }
tq->obj_move(data, elem.obj); *stream_idx = idx;
objpool_release(tq->obj_pool, &elem.obj);
*stream_idx = elem.stream_idx;
return 0; return 0;
} }
@ -202,12 +199,12 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
pthread_mutex_lock(&tq->lock); pthread_mutex_lock(&tq->lock);
while (1) { while (1) {
size_t can_read = av_fifo_can_read(tq->fifo); size_t can_read = av_container_fifo_can_read(tq->fifo);
ret = receive_locked(tq, stream_idx, data); ret = receive_locked(tq, stream_idx, data);
// signal other threads if the fifo state changed // signal other threads if the fifo state changed
if (can_read != av_fifo_can_read(tq->fifo)) if (can_read != av_container_fifo_can_read(tq->fifo))
pthread_cond_broadcast(&tq->cond); pthread_cond_broadcast(&tq->cond);
if (ret == AVERROR(EAGAIN)) { if (ret == AVERROR(EAGAIN)) {

View file

@ -21,7 +21,10 @@
#include <string.h> #include <string.h>
#include "objpool.h" enum ThreadQueueType {
THREAD_QUEUE_FRAMES,
THREAD_QUEUE_PACKETS,
};
typedef struct ThreadQueue ThreadQueue; typedef struct ThreadQueue ThreadQueue;
@ -32,12 +35,9 @@ typedef struct ThreadQueue ThreadQueue;
* maintained * maintained
* @param queue_size number of items that can be stored in the queue without * @param queue_size number of items that can be stored in the queue without
* blocking * blocking
* @param obj_pool object pool that will be used to allocate items stored in the
* queue; the pool becomes owned by the queue
* @param callback that moves the contents between two data pointers
*/ */
ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size, ThreadQueue *tq_alloc(unsigned int nb_streams, size_t queue_size,
ObjPool *obj_pool, void (*obj_move)(void *dst, void *src)); enum ThreadQueueType type);
void tq_free(ThreadQueue **tq); void tq_free(ThreadQueue **tq);
/** /**