Files
2026-08-24 10:03:24 +08:00

354 lines
12 KiB
C

/*
* Copyright (c) 2020 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.
* *
Description:
*/
#define LOG_TAG "audio_hw_hal_avsync"
//#define LOG_NDEBUG 0
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "aml_audio_log.h"
#include <inttypes.h>
#include "hw_avsync.h"
#include "audio_hwsync.h"
#include "aml_malloc_debug.h"
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
const uint8_t HW_AVSYNC_HEADER_V1[] = {0x55, 0x55, 0x00, 0x01};
const uint8_t HW_AVSYNC_HEADER_V2[] = {0x55, 0x55, 0x00, 0x02};
static uint32_t deserialize_bytes_to_int32(uint8_t *val_serial)
{
return (((uint32_t)val_serial[0]) << 24) |
(((uint32_t)val_serial[1]) << 16) |
(((uint32_t)val_serial[2]) << 8) |
((uint32_t)val_serial[3]);
}
static uint64_t deserialize_bytes_to_int64(uint8_t *val_serial)
{
return (((uint64_t)val_serial[0]) << 56) |
(((uint64_t)val_serial[1]) << 48) |
(((uint64_t)val_serial[2]) << 40) |
(((uint64_t)val_serial[3]) << 32) |
(((uint64_t)val_serial[4]) << 24) |
(((uint64_t)val_serial[5]) << 16) |
(((uint64_t)val_serial[6]) << 8) |
((uint64_t)val_serial[7]);
}
void hwsync_header_extract(struct hw_avsync_header *header)
{
uint32_t frame_size = deserialize_bytes_to_int32(&header->header[4]);
uint64_t pts = deserialize_bytes_to_int64(&header->header[8]);
int header_sub_version = header->header[2];
if (!header_sub_version)
pts = pts * 90 / 1000000;
hwsync_header_set_frame_size(header, frame_size);
hwsync_header_set_pts(header, pts);
}
static inline bool hwsync_header_validate(uint8_t *header)
{
return (header[0] == 0x55) &&
(header[1] == 0x55) &&
(header[2] == 0x00) &&
(header[3] == 0x02);
}
void serialize_int32_to_bytes(uint8_t *byte, uint32_t val)
{
int size = sizeof(uint32_t);
int i = 0;
for (i = 0; i < size; i++) {
byte[i] = (val >> ((size - 1 - i) * 8)) & 0xff;
}
}
void serialize_int64_to_bytes(uint8_t *byte, uint64_t val)
{
int size = sizeof(uint64_t);
int i = 0;
for (i = 0; i < size; i++) {
byte[i] = (val >> ((size - 1 - i) * 8)) & 0xff;
}
}
void hwsync_header_construct(struct hw_avsync_header *header)
{
size_t index = 0;
header->header[index++] = 0x55;
header->header[index++] = 0x55;
header->header[index++] = 0x00;
header->header[index++] = 0x02;
serialize_int32_to_bytes(&header->header[index], header->frame_size);
index += 4;
serialize_int64_to_bytes(&header->header[index], header->pts);
index += 8;
}
uint32_t hwsync_header_get_frame_size(struct hw_avsync_header *header)
{
if (header->is_complete)
return header->frame_size;
ALOGE("%s(), header not complete", __func__);
return 0;
}
static uint64_t hwsync_header_get_apts(struct hw_avsync_header *header)
{
if (header->is_complete)
return header->pts;
return 0;
}
void hwsync_header_set_frame_size(struct hw_avsync_header *header, uint32_t frame_size)
{
if (frame_size > HW_AVSYNC_FRAME_SIZE || frame_size == 0) {
ALOGE("%s(), invalid frame size (%d), 0 or exeeds %d",
__func__, frame_size, HW_AVSYNC_FRAME_SIZE);
}
header->frame_size = frame_size;
}
void hwsync_header_set_pts(struct hw_avsync_header *header, uint64_t pts)
{
header->pts = pts;
}
int hwsync_read_header_byte(struct hw_avsync_header *header, uint8_t *byte)
{
if (!header || !byte || header->bytes_read >= header->header_size)
return -EINVAL;
if (header->bytes_read == 0)
hwsync_header_construct(header);
*byte = header->header[header->bytes_read];
header->bytes_read++;
if (header->bytes_read >= header->header_size)
header->is_complete = true;
return 0;
}
int hwsync_write_header_byte(struct hw_avsync_header *header, uint8_t byte)
{
if (!header || (header->version_num > 0 && header->bytes_written >= header->header_size)) {
ALOGE("%s(), header null or inval written bytes", __func__);
return -EINVAL;
}
ALOGV("header->bytes_written:%zu byte:%0x",header->bytes_written,byte);
if (header->bytes_written < (HW_SYNC_VERSION_SIZE - 1) /*&&
byte == HW_AVSYNC_HEADER_V2[header->bytes_written]*/) {
header->header[header->bytes_written++] = byte;
} else if (header->bytes_written == (HW_SYNC_VERSION_SIZE - 1)) {
header->header[header->bytes_written++] = byte;
if (byte == 1) {
ALOGV("version_num %d ",byte);
header->version_num = byte;
header->header_size = HW_AVSYNC_HEADER_SIZE_V1;
} else if (byte == 2) {
ALOGV("version_num %d ",byte);
header->version_num = byte;
header->header_size = HW_AVSYNC_HEADER_SIZE_V2;
} else {
ALOGE("invalid version_num %d ",header->version_num);
}
} else if (header->bytes_written >= HW_SYNC_VERSION_SIZE ) {
header->header[header->bytes_written++] = byte;
if (header->bytes_written >= header->header_size &&
(header->version_num == 2 || header->version_num == 1)) {
header->is_complete = true;
hwsync_header_extract(header);
}
} else {
ALOGE("%s(), invalid data %d, bytes_wrtten %zu",
__func__, byte, header->bytes_written);
header->bytes_written = 0;
return -EINVAL;
}
return 0;
}
bool hwsync_is_header_complete(struct hw_avsync_header *header)
{
return header->is_complete;
}
int hwsync_header_reset(struct hw_avsync_header *header)
{
if (!header)
return -EINVAL;
ALOGV("%s()", __func__);
memset(header, 0, sizeof(struct hw_avsync_header));
header->bytes_read = 0;
header->bytes_written = 0;
header->frame_size = 0;
header->is_complete = false;
header->pts = 0;
return 0;
}
size_t hwsync_header_get_bytes_remaining(struct hw_avsync_header *header)
{
return header->header_size - header->bytes_written;
}
void extractor_consume_output(struct hw_avsync_header_extractor *header_extractor)
{
size_t written = 0;
while (header_extractor->data_size_bytes > 0) {
ALOGV("++%s, data size =%d", __func__, header_extractor->data_size_bytes);
written = header_extractor->consume_output_data(
header_extractor->cbk_cookie,
header_extractor->data,
header_extractor->data_size_bytes);
ALOGV("%s, header_extractor->data_size_bytes = %d, written =%zu", __func__,
header_extractor->data_size_bytes, written);
if (written <= header_extractor->data_size_bytes) {
header_extractor->sync_frame_written += written;
header_extractor->data_size_bytes -= written;
memmove(header_extractor->data,
header_extractor->data + written, header_extractor->data_size_bytes);
} else {
ALOGE("%s(), fatal error", __func__);
}
//header_extractor->sync_frame_written += written;
//header_extractor->data_size_bytes -= written;
ALOGV("--%s, data size =%d", __func__, header_extractor->data_size_bytes);
ALOGV("--%s, sync_frame_written =%zu", __func__, header_extractor->sync_frame_written);
}
}
static void extractor_reset(struct hw_avsync_header_extractor *header_extractor)
{
ALOGV("%s()", __func__);
header_extractor->is_reading_avsync_header = true;
header_extractor->data_size_bytes = 0;
header_extractor->sync_frame_written = 0;
hwsync_header_reset(&header_extractor->avsync_header);
}
ssize_t header_extractor_write(struct hw_avsync_header_extractor *header_extractor,
const void *buffer, size_t bytes)
{
size_t bytes_remaining = bytes;
const uint8_t *data = (const uint8_t *)buffer;
struct hw_avsync_header *sync_header = &header_extractor->avsync_header;
while (bytes_remaining > 0) {
if (header_extractor->is_reading_avsync_header) {
ALOGV("--%s() writing header byte val %#x", __func__, *data);
int ret = hwsync_write_header_byte(sync_header, *data);
if (ret < 0) {
ALOGE("%s(), invalid data!!, bytes_remaining %zu", __func__, bytes_remaining);
extractor_reset(header_extractor);
bytes_remaining--;
data++;
//continue;
return ret;
}
if (hwsync_is_header_complete(sync_header)) {
uint32_t frame_size = hwsync_header_get_frame_size(sync_header);
// means to start read body frames
header_extractor->is_reading_avsync_header = false;
// callback to consume the meta data
header_extractor->consume_meta_data(
header_extractor->cbk_cookie,
frame_size,
hwsync_header_get_apts(sync_header),
header_extractor->payload_offset);
// prepare the body data
header_extractor->data_size_bytes = 0;
memset(header_extractor->data, 0, HW_AVSYNC_FRAME_SIZE);
// accumulate the payload consumed
header_extractor->payload_offset += frame_size;
ALOGV("%s() filling header complete, framesize = %d, payload offset %" PRId64 "",
__func__, frame_size, header_extractor->payload_offset);
}
bytes_remaining--;
data++;
} else {
ALOGV("start dealing body read, bytes_remaing %zu, sync_frame_written %zu",
bytes_remaining, header_extractor->sync_frame_written);
size_t bytes_to_copy = min(bytes_remaining,
hwsync_header_get_frame_size(sync_header) - header_extractor->sync_frame_written);
ALOGV("%s() writing body_bytes= %zu,data_size_bytes= %d",
__func__, bytes_to_copy, header_extractor->data_size_bytes);
memcpy(header_extractor->data + header_extractor->data_size_bytes, data, bytes_to_copy);
header_extractor->data_size_bytes += bytes_to_copy;
bytes_remaining -= bytes_to_copy;
data += bytes_to_copy;
if (header_extractor->sync_frame_written + bytes_to_copy >= hwsync_header_get_frame_size(sync_header)) {
// prepare for next header
extractor_consume_output(header_extractor);
extractor_reset(header_extractor);
ALOGV("%s() reading body over, next to reading header, return bytes %zu",
__func__, bytes - bytes_remaining);
continue;
//return bytes - bytes_remaining;
}
}
}
if (header_extractor->data_size_bytes > 0) {
extractor_consume_output(header_extractor);
}
ALOGV("--%s()", __func__);
return bytes;
}
struct hw_avsync_header_extractor *
new_hw_avsync_header_extractor(
consume_meta_data_t consume_meta_data,
consume_output_data_t consume_output_data,
//int (*consume_meta_data)(void *cookie, uint32_t frame_size, int64_t pts, uint32_t offset),
//int (*consume_output_data)(void *cookie, const void *buffer, size_t bytes),
void *cookie)
{
struct hw_avsync_header_extractor *header_extractor = NULL;
header_extractor = (struct hw_avsync_header_extractor *)aml_audio_calloc(1, sizeof(*header_extractor));
if (!header_extractor) {
return NULL;
}
extractor_reset(header_extractor);
header_extractor->consume_meta_data = consume_meta_data;
header_extractor->consume_output_data = consume_output_data;
header_extractor->cbk_cookie = cookie;
return header_extractor;
}
void delete_hw_avsync_header_extractor(struct hw_avsync_header_extractor *header_extractor)
{
if (header_extractor) {
aml_audio_free(header_extractor);
header_extractor = NULL;
}
}
void flush_hw_avsync_header_extractor(struct hw_avsync_header_extractor *header_extractor)
{
if (header_extractor) {
extractor_reset(header_extractor);
header_extractor->payload_offset = 0;
}
}