blob: 915c00b5e79f02e77f3980922c17dab4bc9c5e6f [file] [log] [blame]
/*
* hardware/amlogic/audio/utils/aml_ringbuffer.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
*/
#define LOG_TAG "audio_hw_utils_ringbuf"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <cutils/log.h>
#include <aml_ringbuffer.h>
#include "tv_patch_format_parser.h"
#include "aml_malloc_debug.h"
/*************************************************
Function: get_write_space
Description: get the space can be written
Input: write_point: write pointer
read_point: read pointer
buffer_size: total buffer size
Output:
Return: the space can be written in byte
*************************************************/
static int get_write_space(unsigned char *write_point, unsigned char *read_point,
int buffer_size, int last_is_write)
{
int bytes = 0;
if (write_point > read_point) {
bytes = buffer_size + read_point - write_point;
} else if (write_point < read_point) {
bytes = read_point - write_point;
} else if (!last_is_write) {
bytes = buffer_size;
}
return bytes;
}
/*************************************************
Function: get_read_space
Description: get the date space can be read
Input: write_point: write pointer
read_point: read pointer
buffer_size: total buffer size
Output:
Return: the data space can be read in byte
*************************************************/
static size_t get_read_space(unsigned char *write_point, unsigned char *read_point,
int buffer_size, int last_is_write) {
int bytes = 0;
if (write_point > read_point) {
bytes = write_point - read_point;
} else if (write_point < read_point) {
bytes = buffer_size + write_point - read_point;
} else if (last_is_write) {
bytes = buffer_size;
}
return bytes;
}
/*************************************************
Function: write_to_buffer
Description: write data to ring buffer
Input: current_pointer: the current write pointer
data: data to be written
bytes: the space of data to be written
start_addr: dest buffer
total_size: dest buffer size
Output:
Return: 0 for success
*************************************************/
static int write_to_buffer(unsigned char *current_pointer, const unsigned char *data, int bytes,
unsigned char *start_addr, int total_size)
{
int left_bytes = start_addr + total_size - current_pointer;
if (left_bytes >= bytes) {
memcpy(current_pointer, data, bytes);
} else {
memcpy(current_pointer, data, left_bytes);
memcpy(start_addr, data + left_bytes, bytes - left_bytes);
}
return 0;
}
/*************************************************
Function: read_from_buffer
Description: read data to ring buffer
Input: current_pointer: the current read pointer
buffer: buffer for the data to be read
bytes: the space of data to be read
start_addr: dest buffer
total_size: dest buffer size
Output: read data
Return: 0 for success
*************************************************/
static int read_from_buffer(unsigned char *current_pointer, unsigned char *buffer, int bytes,
unsigned char *start_addr, int total_size)
{
int left_bytes = start_addr + total_size - current_pointer;
if (left_bytes >= bytes) {
memcpy(buffer, current_pointer, bytes);
} else {
memcpy(buffer, current_pointer, left_bytes);
memcpy(buffer + left_bytes, start_addr, bytes - left_bytes);
}
return 0;
}
/*************************************************
Function: update_pointer
Description: update read/write pointer of ring buffer
Input: current_pointer: the current read/write pointer
bytes: data space has been written/read
start_addr: ring buffer
total_size: ring buffer size
Output:
Return: the updated pointer
*************************************************/
static inline void* update_pointer(unsigned char *current_pointer, int bytes,
unsigned char *start_addr, int total_size)
{
current_pointer += bytes;
if (current_pointer >= start_addr + total_size) {
current_pointer -= total_size;
}
return current_pointer;
}
/*************************************************
Function: ring_buffer_write
Description: write data to ring buffer
Input: rbuffer: the dest ring buffer
data: data to be written
bytes: data space in byte
cover: whether or not to cover the data if over run
Output:
Return: data space has been written
*************************************************/
size_t ring_buffer_write(struct ring_buffer *rbuffer, const unsigned char* data, size_t bytes, int cover)
{
struct ring_buffer *buf = rbuffer;
size_t write_space, written_bytes;
pthread_mutex_lock(&buf->lock);
if (buf->start_addr == NULL || buf->rd == NULL || buf->wr == NULL || buf->size == 0) {
ALOGE("%s, Buffer malloc fail!\n", __FUNCTION__);
pthread_mutex_unlock(&buf->lock);
return 0;
}
write_space = get_write_space(buf->wr, buf->rd, buf->size, buf->last_is_write);
if (write_space < bytes) {
if (UNCOVER_WRITE == cover) {
written_bytes = write_space;
} else {
written_bytes = bytes;
}
} else {
written_bytes = bytes;
}
write_to_buffer(buf->wr, data, written_bytes, buf->start_addr, buf->size);
buf->wr = update_pointer(buf->wr, written_bytes, buf->start_addr, buf->size);
if (written_bytes)
buf->last_is_write = 1;
pthread_mutex_unlock(&buf->lock);
return written_bytes;
}
/*************************************************
Function: ring_buffer_read
Description: read data from ring buffer
Input: rbuffer: the source ring buffer
buffer: buffer for the read data
bytes: data space in byte
Output: data has been read
Return: data space has been read
*************************************************/
size_t ring_buffer_read(struct ring_buffer *rbuffer, unsigned char* buffer, size_t bytes)
{
struct ring_buffer *buf = rbuffer;
size_t readable_space, read_bytes;
pthread_mutex_lock(&buf->lock);
if (buf->start_addr == NULL || buf->rd == NULL || buf->wr == NULL
|| buf->size == 0) {
ALOGE("%s, Buffer malloc fail!\n", __FUNCTION__);
pthread_mutex_unlock(&buf->lock);
return 0;
}
readable_space = get_read_space(buf->wr, buf->rd, buf->size, buf->last_is_write);
if (readable_space < bytes) {
read_bytes = readable_space;
} else {
read_bytes = bytes;
}
read_from_buffer(buf->rd, buffer, read_bytes, buf->start_addr, buf->size);
buf->rd = update_pointer(buf->rd, read_bytes, buf->start_addr, buf->size);
if (read_bytes)
buf->last_is_write = 0;
pthread_mutex_unlock(&buf->lock);
return read_bytes;
}
/*************************************************
Function: ring_buffer_seek
Description: seek read or write pointer to add or
reduce audio latency
Input: rbuffer: the source ring buffer
bytes: seek space in byte,
bytes > 0, reduce audio latency, move read pointer
bytes < 0, add audio latency, move write pointer
Return: 0: do nothing for ringbuffer.
bytes: moved space in byte
*************************************************/
int ring_buffer_seek(struct ring_buffer *rbuffer, int bytes)
{
struct ring_buffer *buf = rbuffer;
int seek_bytes = 0;
pthread_mutex_lock(&buf->lock);
if (buf->start_addr == NULL || buf->rd == NULL || buf->wr == NULL
|| buf->size == 0 || bytes == 0) {
pthread_mutex_unlock(&buf->lock);
return 0;
}
if (bytes > 0) {
seek_bytes = get_read_space(buf->wr, buf->rd, buf->size, buf->last_is_write);
if (seek_bytes > bytes) {
seek_bytes = bytes;
}
if (seek_bytes > 0) {
buf->last_is_write = 0;
}
buf->rd = update_pointer(buf->rd, seek_bytes, buf->start_addr, buf->size);
} else {
seek_bytes = get_write_space(buf->wr, buf->rd, buf->size, buf->last_is_write);
bytes *= -1;
if (seek_bytes > bytes) {
seek_bytes = bytes;
}
buf->wr = update_pointer(buf->wr, seek_bytes, buf->start_addr, buf->size);
seek_bytes *= -1;
}
pthread_mutex_unlock(&buf->lock);
return seek_bytes;
}
/*************************************************
Function: ring_buffer_init
Description: initialize ring buffer
Input: rbuffer: the ring buffer to be initialized
buffer_size: total size of ring buffer
Output:
Return: 0 for success, otherwise fail
*************************************************/
int ring_buffer_init(struct ring_buffer *rbuffer, int buffer_size)
{
struct ring_buffer *buf = rbuffer;
pthread_mutex_lock(&buf->lock);
buf->size = buffer_size;
buf->start_addr = aml_audio_malloc(buffer_size * sizeof(unsigned char));
if (buf->start_addr == NULL) {
ALOGD("%s, Malloc android out buffer error!\n", __FUNCTION__);
pthread_mutex_unlock(&buf->lock);
return -1;
}
memset(buf->start_addr, 0, buffer_size);
buf->rd = buf->start_addr;
buf->wr = buf->start_addr;
buf->last_is_write = 0;
pthread_mutex_unlock(&buf->lock);
return 0;
}
/*************************************************
Function: ring_buffer_release
Description: release ring buffer
Input: rbuffer: the ring buffer to be released
Output:
Return: 0 for success, otherwise fail
*************************************************/
int ring_buffer_release(struct ring_buffer *rbuffer)
{
struct ring_buffer *buf = rbuffer;
pthread_mutex_lock(&buf->lock);
if (buf->start_addr != NULL) {
aml_audio_free(buf->start_addr);
buf->start_addr = NULL;
}
buf->rd = NULL;
buf->wr = NULL;
buf->size = 0;
buf->last_is_write = 0;
pthread_mutex_unlock(&buf->lock);
return 0;
}
/*************************************************
Function: ring_buffer_reset
Description: reset ring buffer
Input: rbuffer: the ring buffer to be reset
Output:
Return: 0 for success, otherwise fail
*************************************************/
int ring_buffer_reset(struct ring_buffer *rbuffer)
{
struct ring_buffer *buf = rbuffer;
pthread_mutex_lock(&buf->lock);
memset(buf->start_addr, 0, buf->size);
buf->rd = buf->wr = buf->start_addr;
buf->last_is_write = 0;
/*
* if (buf->rd >= (buf->start_addr + buf->size))
* buf->rd -= buf->size;
*/
pthread_mutex_unlock(&buf->lock);
return 0;
}
/*************************************************
Function: ring_buffer_clear
Description: ring clear
Input: rbuffer: the ring buffer to be clear
Output:
Return: 0 for success, otherwise fail
*************************************************/
int ring_buffer_clear(struct ring_buffer *rbuffer)
{
struct ring_buffer *buf = rbuffer;
pthread_mutex_lock(&buf->lock);
memset(buf->start_addr, 0, buf->size);
pthread_mutex_unlock(&buf->lock);
return 0;
}
/*************************************************
Function: ring_buffer_reset_size
Description: reset ring buffer and change the size
Input: rbuffer: the ring buffer to be reset
buffer_size: new size for ring buffer
Output:
Return: 0 for success, otherwise fail
*************************************************/
int ring_buffer_reset_size(struct ring_buffer *rbuffer, int buffer_size)
{
if (buffer_size > rbuffer->size) {
ALOGW("resized buffer size exceed largest buffer size, max %d, cur %d\n", \
rbuffer->size, buffer_size);
ring_buffer_release(rbuffer);
rbuffer->size = buffer_size;
return ring_buffer_init(rbuffer, buffer_size);
}
ALOGI("reset buffer size from %d to %d\n", rbuffer->size, buffer_size);
pthread_mutex_lock(&rbuffer->lock);
rbuffer->size = buffer_size;
memset(rbuffer->start_addr, 0, buffer_size);
rbuffer->rd = rbuffer->start_addr;
rbuffer->wr = rbuffer->start_addr;
pthread_mutex_unlock(&rbuffer->lock);
return 0;
}
/*************************************************
Function: get_buffer_read_space
Description: get the data space for reading in the buffer
Input: rbuffer: the ring buffer with data
Output:
Return: data space for success, otherwise < 0
*************************************************/
int get_buffer_read_space(struct ring_buffer *rbuffer)
{
size_t read_space = 0;
pthread_mutex_lock(&rbuffer->lock);
if (rbuffer->start_addr == NULL || rbuffer->wr == NULL || rbuffer->rd == NULL
|| rbuffer->size == 0) {
ALOGE("%s, Buffer malloc fail!\n", __FUNCTION__);
pthread_mutex_unlock(&rbuffer->lock);
return -1;
}
read_space = get_read_space(rbuffer->wr, rbuffer->rd, rbuffer->size, rbuffer->last_is_write);
pthread_mutex_unlock(&rbuffer->lock);
return read_space;
}
/*************************************************
Function: get_buffer_write_space
Description: get the space for writing in the buffer
Input: rbuffer: the ring buffer to be written
Output:
Return: data space for success, otherwise < 0
*************************************************/
int get_buffer_write_space(struct ring_buffer *rbuffer)
{
size_t write_space = 0;
pthread_mutex_lock(&rbuffer->lock);
if (rbuffer->start_addr == NULL || rbuffer->wr == NULL || rbuffer->wr == NULL
|| rbuffer->size == 0) {
ALOGE("%s, Buffer malloc fail!\n", __FUNCTION__);
pthread_mutex_unlock(&rbuffer->lock);
return -1;
}
write_space = get_write_space(rbuffer->wr, rbuffer->rd, rbuffer->size, rbuffer->last_is_write);
pthread_mutex_unlock(&rbuffer->lock);
return write_space;
}
// return value: The data size between sync word and read pointer of ring buffer
// if return value is -1, it means we can not find sync word in ring buffer
int find_61937_sync_word_position_in_ringbuffer(struct ring_buffer *rbuffer) {
int read_space = 0;
int pos_sync_word = -1;
int sync_word_size = 4;
unsigned char tmp_buf[4];
pthread_mutex_lock(&rbuffer->lock);
read_space = get_read_space(rbuffer->wr, rbuffer->rd, rbuffer->size, rbuffer->last_is_write);
if (read_space >= sync_word_size) {
// ring buffer |-------------|---------------|-------------|
// start_addr rd wr
if (rbuffer->rd < rbuffer->wr) {
pos_sync_word = find_61937_sync_word((char*)rbuffer->rd, read_space);
} else {
// ring buffer |----second_read_size----|---------------|---first_read_size---|
// start_addr wr rd
int first_read_size = rbuffer->size - (rbuffer->rd - rbuffer->start_addr);
int second_read_size = rbuffer->wr - rbuffer->start_addr;
//Check if the synchronization word is in first_read_size
if (first_read_size >= sync_word_size) {
pos_sync_word = find_61937_sync_word((char*)rbuffer->rd, first_read_size);
if (pos_sync_word != -1) {
goto find_exit;
}
}
//Check if the synchronization word is in second_read_size
if (second_read_size >= sync_word_size) {
pos_sync_word = find_61937_sync_word((char*)rbuffer->start_addr, second_read_size);
if (pos_sync_word != -1) {
pos_sync_word += first_read_size;
goto find_exit;
}
}
//Check if the synchronization word is in XXXX
// ring buffer |X------------|---------------|----------XXX|
// start_addr rd wr
if (first_read_size >=3 && second_read_size >=1)
{
tmp_buf[0]= rbuffer->start_addr[rbuffer->size - 3];
tmp_buf[1]= rbuffer->start_addr[rbuffer->size - 2];
tmp_buf[2]= rbuffer->start_addr[rbuffer->size - 1];
tmp_buf[3]= rbuffer->start_addr[0];
pos_sync_word = find_61937_sync_word((char*)tmp_buf, 4);
if (pos_sync_word != -1) {
pos_sync_word = first_read_size - 3;
goto find_exit;
}
}
//Check if the synchronization word is in XXXX
// ring buffer |XX-----------|---------------|-----------XX|
// start_addr rd wr
if (first_read_size >=2 && second_read_size >=2)
{
tmp_buf[0]= rbuffer->start_addr[rbuffer->size - 2];
tmp_buf[1]= rbuffer->start_addr[rbuffer->size - 1];
tmp_buf[2]= rbuffer->start_addr[0];
tmp_buf[3]= rbuffer->start_addr[1];
pos_sync_word = find_61937_sync_word((char*)tmp_buf, 4);
if (pos_sync_word != -1) {
pos_sync_word = first_read_size - 2;
goto find_exit;
}
}
//Check if the synchronization word is in XXXX
// ring buffer |XXX----------|---------------|------------X|
// start_addr rd wr
if (first_read_size >=1 && second_read_size >=3)
{
tmp_buf[0]= rbuffer->start_addr[rbuffer->size - 1];
tmp_buf[1]= rbuffer->start_addr[0];
tmp_buf[2]= rbuffer->start_addr[1];
tmp_buf[3]= rbuffer->start_addr[2];
pos_sync_word = find_61937_sync_word((char*)tmp_buf, 4);
if (pos_sync_word != -1) {
pos_sync_word = first_read_size - 1;
goto find_exit;
}
}
}
}
find_exit:
pthread_mutex_unlock(&rbuffer->lock);
/* coverity[integer_overflow]:The return value of find_61937_sync_word will not exceed the maximum value of int*/
return pos_sync_word;
}
/*************************************************
Function: ring_buffer_dump
Description: dump the ringbuffer status
Input: rbuffer: the ring buffer to be dumped
Output:
Return: NULL
*************************************************/
void ring_buffer_dump(struct ring_buffer *rbuffer)
{
ALOGI("-buffer_size:%d", rbuffer->size);
ALOGI("-buffer_avail:%d, buffer_space:%d", get_buffer_read_space(rbuffer), get_buffer_write_space(rbuffer));
}
#ifdef __cplusplus
}
#endif