Nils Hasenbanck 417d1b7de7 Color space conversion using frame based MT.
Using a ordered ring buffer and a thread pool, the color space
conversion is not multi-threaded based on frames. I tried
to implement slice based threading, but libswscale did produced
highly distorted pictures without obvious reason.

This approach introduces some more "lag" when decoding and skipping,
but shouldn't be affect the user negatively, since movie
watching is not lag sensitive, as long as the A/V is synchronized.

Change default to software decoding.

SW decoding is the most robust and fasted method of decoding right now.
Users should enable hw based decoding if their system requires it
and it's actually beneficial for them.

Fix deadlocks when seeking and decrease RAM usage.

Decrease memory allocation by reusing AVFrames.
2019-12-18 11:46:49 +01:00

186 lines
4.2 KiB
C

#include <libavformat/avformat.h>
#include <rthreads/rthreads.h>
#include "swsbuffer.h"
enum kbStatus
{
KB_OPEN,
KB_IN_PROGRESS,
KB_FINISHED
};
struct swsbuffer
{
sws_context_t *buffer;
enum kbStatus *status;
size_t size;
slock_t *lock;
int64_t head;
int64_t tail;
};
swsbuffer_t *swsbuffer_create(size_t num, int frame_size, int width, int height)
{
swsbuffer_t *b = malloc(sizeof (swsbuffer_t));
if (b == NULL)
return NULL;
b->status = malloc(sizeof(enum kbStatus) * num);
if (b->status == NULL)
return NULL;
for (int i = 0; i < num; i++)
b->status[i] = KB_OPEN;
b->lock = slock_new();
if (b->lock == NULL)
return NULL;
b->buffer = malloc(sizeof(sws_context_t) * num);
if (b->buffer == NULL)
return NULL;
for (int i = 0; i < num; i++)
{
b->buffer[i].index = i;
b->buffer[i].sws = sws_alloc_context();
b->buffer[i].source = av_frame_alloc();
#if LIBAVUTIL_VERSION_MAJOR > 55
b->buffer[i].hw_source = av_frame_alloc();
#endif
b->buffer[i].target = av_frame_alloc();
b->buffer[i].frame_buf = av_malloc(frame_size);
avpicture_fill((AVPicture*)b->buffer[i].target, (const uint8_t*)b->buffer[i].frame_buf,
PIX_FMT_RGB32, width, height);
}
b->size = num;
b->head = 0;
b->tail = 0;
return b;
}
void swsbuffer_destroy(swsbuffer_t *swsbuffer)
{
if (swsbuffer != NULL) {
slock_free(swsbuffer->lock);
free(swsbuffer->status);
for (int i = 0; i < swsbuffer->size; i++)
{
#if LIBAVUTIL_VERSION_MAJOR > 55
av_frame_free(&swsbuffer->buffer[i].hw_source);
#endif
av_frame_free(&swsbuffer->buffer[i].source);
av_frame_free(&swsbuffer->buffer[i].target);
av_freep(&swsbuffer->buffer[i].frame_buf);
sws_freeContext(swsbuffer->buffer[i].sws);
}
free(swsbuffer->buffer);
free(swsbuffer);
}
}
void swsbuffer_clear(swsbuffer_t *swsbuffer)
{
slock_lock(swsbuffer->lock);
swsbuffer->head = 0;
swsbuffer->tail = 0;
for (int i = 0; i < swsbuffer->size; i++)
swsbuffer->status[i] = KB_OPEN;
slock_unlock(swsbuffer->lock);
}
void swsbuffer_get_open_slot(swsbuffer_t *swsbuffer, sws_context_t **context)
{
slock_lock(swsbuffer->lock);
if (swsbuffer->status[swsbuffer->head] == KB_OPEN)
{
*context = &swsbuffer->buffer[swsbuffer->head];
swsbuffer->status[swsbuffer->head] = KB_IN_PROGRESS;
swsbuffer->head++;
swsbuffer->head %= swsbuffer->size;
}
slock_unlock(swsbuffer->lock);
}
void swsbuffer_return_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
{
slock_lock(swsbuffer->lock);
if (swsbuffer->status[context->index] == KB_IN_PROGRESS)
{
swsbuffer->status[context->index] = KB_OPEN;
swsbuffer->head--;
swsbuffer->head %= swsbuffer->size;
}
slock_unlock(swsbuffer->lock);
}
void swsbuffer_open_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
{
slock_lock(swsbuffer->lock);
if (swsbuffer->status[context->index] == KB_FINISHED)
{
swsbuffer->status[context->index] = KB_OPEN;
swsbuffer->tail++;
swsbuffer->tail %= (swsbuffer->size);
}
slock_unlock(swsbuffer->lock);
}
void swsbuffer_get_finished_slot(swsbuffer_t *swsbuffer, sws_context_t **context)
{
slock_lock(swsbuffer->lock);
if (swsbuffer->status[swsbuffer->tail] == KB_FINISHED)
*context = &swsbuffer->buffer[swsbuffer->tail];
slock_unlock(swsbuffer->lock);
}
void swsbuffer_finish_slot(swsbuffer_t *swsbuffer, sws_context_t *context)
{
slock_lock(swsbuffer->lock);
if (swsbuffer->status[context->index] == KB_IN_PROGRESS)
swsbuffer->status[context->index] = KB_FINISHED;
slock_unlock(swsbuffer->lock);
}
bool swsbuffer_has_open_slot(swsbuffer_t *swsbuffer)
{
bool ret = false;
slock_lock(swsbuffer->lock);
if (swsbuffer->status[swsbuffer->head] == KB_OPEN)
ret = true;
slock_unlock(swsbuffer->lock);
return ret;
}
bool swsbuffer_has_finished_slot(swsbuffer_t *swsbuffer)
{
bool ret = false;
slock_lock(swsbuffer->lock);
if (swsbuffer->status[swsbuffer->tail] == KB_FINISHED)
ret = true;
slock_unlock(swsbuffer->lock);
return ret;
}