mpv/core/mp_ring.c

156 lines
4.2 KiB
C

/*
* This file is part of mpv.
* Copyright (c) 2012 wm4
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
#include <libavutil/common.h>
#include <assert.h>
#include "talloc.h"
#include "core/mp_memory_barrier.h"
#include "core/mp_ring.h"
struct mp_ring {
uint8_t *buffer;
/* Positions of thes first readable/writeable chunks. Do not read this
* fields but use the atomic private accessors `mp_ring_get_wpos`
* and `mp_ring_get_rpos`. */
uint32_t rpos, wpos;
};
static uint32_t mp_ring_get_wpos(struct mp_ring *buffer)
{
mp_memory_barrier();
return buffer->wpos;
}
static uint32_t mp_ring_get_rpos(struct mp_ring *buffer)
{
mp_memory_barrier();
return buffer->rpos;
}
struct mp_ring *mp_ring_new(void *talloc_ctx, int size)
{
struct mp_ring *ringbuffer =
talloc_zero(talloc_ctx, struct mp_ring);
*ringbuffer = (struct mp_ring) {
.buffer = talloc_size(talloc_ctx, size),
};
return ringbuffer;
}
int mp_ring_drain(struct mp_ring *buffer, int len)
{
int buffered = mp_ring_buffered(buffer);
int drain_len = FFMIN(len, buffered);
mp_atomic_add_and_fetch(&buffer->rpos, drain_len);
mp_memory_barrier();
return drain_len;
}
int mp_ring_read(struct mp_ring *buffer, unsigned char *dest, int len)
{
if (!dest) return mp_ring_drain(buffer, len);
int size = mp_ring_size(buffer);
int buffered = mp_ring_buffered(buffer);
int read_len = FFMIN(len, buffered);
int read_ptr = mp_ring_get_rpos(buffer) % size;
int len1 = FFMIN(size - read_ptr, read_len);
int len2 = read_len - len1;
memcpy(dest, buffer->buffer + read_ptr, len1);
memcpy(dest + len1, buffer->buffer, len2);
mp_atomic_add_and_fetch(&buffer->rpos, read_len);
mp_memory_barrier();
return read_len;
}
int mp_ring_read_cb(struct mp_ring *buffer, void *ctx, int len,
void (*func)(void*, void*, int))
{
// The point of this function is defining custom read behaviour, assume
// it's a programmers error if func is null.
assert(func);
int size = mp_ring_size(buffer);
int buffered = mp_ring_buffered(buffer);
int read_len = FFMIN(len, buffered);
int read_ptr = mp_ring_get_rpos(buffer) % size;
func(ctx, buffer->buffer + read_ptr, len);
return mp_ring_drain(buffer, read_len);
}
int mp_ring_write(struct mp_ring *buffer, unsigned char *src, int len)
{
int size = mp_ring_size(buffer);
int free = mp_ring_available(buffer);
int write_len = FFMIN(len, free);
int write_ptr = mp_ring_get_wpos(buffer) % size;
int len1 = FFMIN(size - write_ptr, write_len);
int len2 = write_len - len1;
memcpy(buffer->buffer + write_ptr, src, len1);
memcpy(buffer->buffer, src + len1, len2);
mp_atomic_add_and_fetch(&buffer->wpos, write_len);
mp_memory_barrier();
return write_len;
}
void mp_ring_reset(struct mp_ring *buffer)
{
buffer->wpos = buffer->rpos = 0;
mp_memory_barrier();
}
int mp_ring_available(struct mp_ring *buffer)
{
return mp_ring_size(buffer) - mp_ring_buffered(buffer);
}
int mp_ring_size(struct mp_ring *buffer)
{
return talloc_get_size(buffer->buffer);
}
int mp_ring_buffered(struct mp_ring *buffer)
{
return (mp_ring_get_wpos(buffer) - mp_ring_get_rpos(buffer));
}
char *mp_ring_repr(struct mp_ring *buffer, void *talloc_ctx)
{
return talloc_asprintf(
talloc_ctx,
"Ringbuffer { .size = %dB, .buffered = %dB, .available = %dB }",
mp_ring_size(buffer),
mp_ring_buffered(buffer),
mp_ring_available(buffer));
}