572 lines
18 KiB
C
572 lines
18 KiB
C
/*
|
|
* 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 "aml_audio_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);
|
|
|
|
/*coverity[LOCK_EVASION]: There will be no competition here*/
|
|
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
|