710 lines
29 KiB
C
710 lines
29 KiB
C
/*
|
|
* Copyright (C) 2020 Amlogic Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#define LOG_TAG "audio_hw_decoder_spdif"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include "aml_audio_log.h"
|
|
#include "audio_hw.h"
|
|
#include "tv_patch_format_parser.h"
|
|
#include "aml_audio_spdifdec.h"
|
|
|
|
#define IEC61937_HEADER_PA_LITTLE 0xF872
|
|
#define IEC61937_HEADER_PB_LITTLE 0x4E1F
|
|
|
|
#define IEC61937_HEADER_PA_BIG 0x72F8
|
|
#define IEC61937_HEADER_PB_BIG 0x1F4E
|
|
|
|
|
|
#define IEC61937_DEFAULT_SIZE (6144)
|
|
#define IEC61937_HEADER_SIZE (8)
|
|
#define IEC61937_HEADER_COPY_SIZE (3) /*the header is 4 bytes, if sync failed, we need at least copy 3 bytes*/
|
|
#define IEC61937_PA_OFFSET (0)
|
|
#define IEC61937_PB_OFFSET (2)
|
|
#define IEC61937_PC_OFFSET (4)
|
|
#define IEC61937_PD_OFFSET (6)
|
|
#define IEC61937_HEADER_SYNC_PERIOD (8) /*in this period there is no IEC frame, increase this can accelerate the sync speed*/
|
|
#define IEC61937_DTSHD_HEAD_SIZE (12)
|
|
|
|
|
|
#define IEC61937_QUICK_SYNC
|
|
|
|
enum SPDIF_DEC_STATE {
|
|
SPDIF_DEC_SYNCING,
|
|
SPDIF_DEC_SYNCED,
|
|
SPDIF_DEC_LACK_DATA,
|
|
};
|
|
|
|
struct aml_spdif_decoder {
|
|
audio_format_t format;
|
|
void * buf;
|
|
int32_t buf_size;
|
|
int32_t buf_remain;
|
|
uint32_t status;
|
|
int32_t payload_size;
|
|
};
|
|
|
|
int aml_spdif_decoder_open(void **spdifdec_handle)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = NULL;
|
|
|
|
spdif_dec_handle = (struct aml_spdif_decoder *)aml_audio_calloc(1, sizeof(struct aml_spdif_decoder));
|
|
if (spdif_dec_handle == NULL) {
|
|
ALOGE("%s handle error", __func__);
|
|
goto ERROR;
|
|
}
|
|
|
|
spdif_dec_handle->buf_size = IEC61937_DEFAULT_SIZE;
|
|
spdif_dec_handle->buf = aml_audio_calloc(1, IEC61937_DEFAULT_SIZE);
|
|
if (spdif_dec_handle->buf == NULL) {
|
|
ALOGE("%s data buffer error", __func__);
|
|
goto ERROR;
|
|
}
|
|
spdif_dec_handle->format = AUDIO_FORMAT_INVALID;
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->payload_size = 0;
|
|
*spdifdec_handle = spdif_dec_handle;
|
|
ALOGI("%s exit =%p", __func__, spdif_dec_handle);
|
|
return 0;
|
|
ERROR:
|
|
if (spdif_dec_handle) {
|
|
if (spdif_dec_handle->buf) {
|
|
aml_audio_free(spdif_dec_handle->buf);
|
|
spdif_dec_handle->buf = NULL;
|
|
}
|
|
aml_audio_free(spdif_dec_handle);
|
|
spdif_dec_handle = NULL;
|
|
}
|
|
*spdifdec_handle = NULL;
|
|
ALOGE("%s error", __func__);
|
|
return -1;
|
|
}
|
|
int aml_spdif_decoder_close(void *phandle)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
|
|
if (spdif_dec_handle) {
|
|
if (spdif_dec_handle->buf) {
|
|
aml_audio_free(spdif_dec_handle->buf);
|
|
spdif_dec_handle->buf = NULL;
|
|
}
|
|
aml_audio_free(spdif_dec_handle);
|
|
spdif_dec_handle = NULL;
|
|
}
|
|
ALOGE("%s exit", __func__);
|
|
return 0;
|
|
}
|
|
|
|
int aml_spdif_decoder_reset(void *phandle)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
|
|
if (spdif_dec_handle) {
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->payload_size = 0;
|
|
}
|
|
ALOGE("%s exit", __func__);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*Find the position of 61937 sync word in the buffer, need PA/PB/PC/PD, 4*sizeof(short)
|
|
*/
|
|
static int seek_61937_sync_word(char *buffer, int size)
|
|
{
|
|
int i = -1;
|
|
if (size < 8) {
|
|
return i;
|
|
}
|
|
|
|
for (i = 0; i < (size - 3); i++) {
|
|
if (buffer[i + 0] == 0x72 && buffer[i + 1] == 0xf8 && buffer[i + 2] == 0x1f && buffer[i + 3] == 0x4e) {
|
|
return i;
|
|
}
|
|
if (buffer[i + 0] == 0xf8 && buffer[i + 1] == 0x72 && buffer[i + 2] == 0x4e && buffer[i + 3] == 0x1f) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int16_t swap_int16(int16_t value)
|
|
{
|
|
return ((value & 0x00FF) << 8) |
|
|
((value & 0xFF00) >> 8) ;
|
|
}
|
|
|
|
|
|
static int get_iec61937_info(void *phandle, void * buf, int32_t size, int32_t *package_size, int32_t *payload_size)
|
|
{
|
|
uint16_t pa = 0;
|
|
uint16_t pb = 0;
|
|
uint16_t pc = 0;
|
|
uint16_t pd = 0;
|
|
uint32_t big_endian = 0;
|
|
uint8_t data_type = 0;
|
|
uint32_t tmp = 0;
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
|
|
if (size < IEC61937_HEADER_SIZE) {
|
|
return -1;
|
|
}
|
|
pa = *(uint16_t*)((uint8_t*)buf + IEC61937_PA_OFFSET);
|
|
pb = *(uint16_t*)((uint8_t*)buf + IEC61937_PB_OFFSET);
|
|
pc = *(uint16_t*)((uint8_t*)buf + IEC61937_PC_OFFSET);
|
|
pd = *(uint16_t*)((uint8_t*)buf + IEC61937_PD_OFFSET);
|
|
|
|
if (pa == IEC61937_HEADER_PA_LITTLE && pb == IEC61937_HEADER_PB_LITTLE) {
|
|
big_endian = 0;
|
|
} else if (pa == IEC61937_HEADER_PA_BIG && pb == IEC61937_HEADER_PB_BIG) {
|
|
big_endian = 1;
|
|
pc = swap_int16(pc);
|
|
pd = swap_int16(pd);
|
|
} else {
|
|
ALOGE("It is not IEC Sync PA=0x%x PB=0x%x", pa, pb);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*Data type defined in PC bits 0-6 in IEC 61937-1*/
|
|
data_type = pc & 0x3f;
|
|
|
|
switch (data_type) {
|
|
case IEC61937_AC3:
|
|
{
|
|
*package_size = AC3_PERIOD_SIZE;
|
|
/*length code is in bits*/
|
|
*payload_size = pd >> 3;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_AC3;
|
|
break;
|
|
}
|
|
case IEC61937_EAC3:
|
|
{
|
|
*package_size = EAC3_PERIOD_SIZE;
|
|
/*length code is in bytes*/
|
|
*payload_size = pd;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_E_AC3;
|
|
break;
|
|
}
|
|
case IEC61937_DTS1:
|
|
{
|
|
*package_size = DTS1_PERIOD_SIZE;
|
|
/*length code is in bits*/
|
|
*payload_size = pd >> 3;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_DTS;
|
|
break;
|
|
}
|
|
case IEC61937_DTS2:
|
|
{
|
|
*package_size = DTS2_PERIOD_SIZE;
|
|
/*length code is in bits*/
|
|
*payload_size = pd >> 3;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_DTS;
|
|
break;
|
|
}
|
|
case IEC61937_DTS3:
|
|
{
|
|
*package_size = DTS3_PERIOD_SIZE;
|
|
/*length code is in bits*/
|
|
*payload_size = pd >> 3;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_DTS;
|
|
break;
|
|
}
|
|
case IEC61937_DTSHD:
|
|
{
|
|
/*Value of 8-12bit is framesize*/
|
|
tmp = (pc & 0x7ff) >> 8;
|
|
/*refer to IEC 61937-5 pdf, table 6*/
|
|
*package_size = DTSHD_PERIOD_SIZE << tmp ;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_DTS_HD;
|
|
break;
|
|
}
|
|
case IEC61937_MAT:
|
|
{
|
|
*package_size = MAT_PERIOD_SIZE;
|
|
/*length code is in bytes*/
|
|
*payload_size = pd;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_MAT;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
*package_size = 0;
|
|
spdif_dec_handle->format = AUDIO_FORMAT_INVALID;
|
|
ALOGE("unsupport iec61937 PC =0x%x PD=0x%x", pc, pd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*refer kodi how to add 12 bytes header for DTS HD
|
|
01 00 00 00 00 00 00 00 fe fe ** **, last 2 bytes for data size
|
|
*/
|
|
static int get_dtshd_info(void * buf, int32_t size, int32_t *payload_size, int32_t *dts_type4_subtype)
|
|
{
|
|
char *read_pointer = (char*)buf;
|
|
|
|
if (size < (IEC61937_HEADER_SIZE + IEC61937_DTSHD_HEAD_SIZE)) {
|
|
*payload_size = 0;
|
|
return -1;
|
|
}
|
|
|
|
*dts_type4_subtype = read_pointer[5] & 0x18; /* Pc[11]~Pc[12], according to IEC61937-5. */
|
|
read_pointer += IEC61937_HEADER_SIZE;
|
|
/*
|
|
Structure of the IEC Data Burst for DTS Type 4
|
|
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
|
| Filed name |Pa |Pb |Pc |Pd |Chunk Index 1|Chunk Index 2|Reserved|Status Register|Chunk ID|Chunk Size| audio data |
|
|
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
|
|Size in bytes| 2 | 2 | 2 | 2 | 1 | 1 | 5 | 1 | 2 | 2 | Chunk Size|
|
|
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
|
|
|
Chunk Index and Chunk ID Values
|
|
———————————————————————————————————————————————————————
|
|
|Chunk Index Value|Chunk ID| DESCRIPTION |
|
|
———————————————————————————————————————————————————————
|
|
| 0 | 0x0000 | No chunk index entry |
|
|
———————————————————————————————————————————————————————
|
|
| 1 | 0xFEFE |DTS Presentation audio chunk|
|
|
———————————————————————————————————————————————————————
|
|
| 5 | 0xFAFA | DTS Command chunk |
|
|
———————————————————————————————————————————————————————
|
|
|all other values | | Reserved |
|
|
———————————————————————————————————————————————————————
|
|
|
|
PS:Chunk Size is the audio data size
|
|
*/
|
|
if (DTS_TYPE_IV_SUB_TYPE_DTS_HD == *dts_type4_subtype) {
|
|
if (read_pointer[0] == 0x00 && read_pointer[1] == 0x01 && read_pointer[8] == 0xfe && read_pointer[9] == 0xfe) {
|
|
*payload_size = (read_pointer[10] | read_pointer[11] << 8);
|
|
} else if ((read_pointer[0] == 0x01 && read_pointer[1] == 0x00 && read_pointer[8] == 0xfe && read_pointer[9] == 0xfe)) {
|
|
*payload_size = (read_pointer[11] | read_pointer[10] << 8);
|
|
} else {
|
|
ALOGE("DTS HD error data\n");
|
|
*payload_size = 0;
|
|
return -1;
|
|
}
|
|
} else if (DTS_TYPE_IV_SUB_TYPE_DTS_UHD == *dts_type4_subtype) {
|
|
if ((read_pointer[0] == 0x01 || read_pointer[0] == 0x02) && read_pointer[8] == 0xfe && read_pointer[9] == 0xfe) {
|
|
*payload_size = (read_pointer[10] | read_pointer[11] << 8);
|
|
} else if (((read_pointer[1] == 0x01 || read_pointer[1] == 0x02) && read_pointer[9] == 0xfe && read_pointer[8] == 0xfe)) {
|
|
*payload_size = (read_pointer[11] | read_pointer[10] << 8);
|
|
} else {
|
|
// For DTS P2 stream, valid frames and empty frames appear alternately, we will drop the empty frame.
|
|
//empty frame like this:72 f8 1f 4e 11 0a 08 00 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
//For empty frame, we should not print error msg
|
|
if ((read_pointer[0] != 0x00 && read_pointer[0] != 0x00) || read_pointer[8] != 0x00 || read_pointer[9] != 0x00) {
|
|
ALOGE("DTS UHD error data\n");
|
|
}
|
|
*payload_size = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int aml_spdif_decoder_do_quick_sync
|
|
(void *phandle
|
|
, const void *inbuf
|
|
, int32_t n_bytes_inbuf
|
|
, int32_t *sync_word_offset
|
|
, int32_t *buf_left
|
|
, int32_t *buf_offset)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
uint8_t *spdifdec_buf = NULL;
|
|
bool is_quick_sync_suitable = false;
|
|
uint8_t *buffer = (uint8_t *)inbuf;
|
|
|
|
if (!spdif_dec_handle || !inbuf || !sync_word_offset || !buf_left || !buf_offset) {
|
|
ALOGE("%s line %d spdif_dec_handle %p inbuf %p sync_word_offset %p buf_left %p buf_offset %p\n",
|
|
__func__, __LINE__, spdif_dec_handle, inbuf, sync_word_offset, buf_left, buf_offset);
|
|
return -1;
|
|
}
|
|
|
|
spdifdec_buf = spdif_dec_handle->buf;
|
|
*buf_left = n_bytes_inbuf;
|
|
|
|
/*if we have enough data and we are doing syncing, we can sync the header quickly in original buf*/
|
|
is_quick_sync_suitable = ((spdif_dec_handle->buf_remain == 0) &&
|
|
(spdif_dec_handle->status == SPDIF_DEC_SYNCING) &&
|
|
(n_bytes_inbuf >= IEC61937_HEADER_SIZE));
|
|
|
|
if (is_quick_sync_suitable) {
|
|
*sync_word_offset = seek_61937_sync_word((char*)buffer, n_bytes_inbuf);
|
|
/*to avoid the case the sync word accross the input buf, we need copy the last 3 bytes*/
|
|
if (*sync_word_offset < 0) {
|
|
memcpy( spdifdec_buf, buffer + n_bytes_inbuf - IEC61937_HEADER_COPY_SIZE, IEC61937_HEADER_COPY_SIZE);
|
|
spdif_dec_handle->buf_remain += IEC61937_HEADER_COPY_SIZE;
|
|
return -1;
|
|
}
|
|
|
|
/*we have find the sync word, just copy the papb part*/
|
|
memcpy(spdifdec_buf , buffer + *sync_word_offset, IEC61937_HEADER_SIZE / 2);
|
|
spdif_dec_handle->buf_remain += IEC61937_HEADER_SIZE / 2;
|
|
*buf_offset += *sync_word_offset + IEC61937_HEADER_SIZE / 2;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
*sync_word_offset = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aml_spdif_decoder_find_syncword(void *phandle
|
|
, const void *inbuf
|
|
, int32_t n_bytes_inbuf
|
|
, int32_t *sync_word_offset
|
|
, int32_t *buf_left
|
|
, int32_t *buf_offset
|
|
, uint8_t **spdifdec_buf)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
int32_t loop_cnt = 0;
|
|
int32_t data_valid = 0;
|
|
int32_t need_size = 0;
|
|
uint8_t *buffer = (uint8_t *)inbuf;
|
|
|
|
if (!spdif_dec_handle || !inbuf || !sync_word_offset || !buf_left || !buf_offset) {
|
|
ALOGE("%s line %d spdif_dec_handle %p inbuf %p sync_word_offset %p buf_left %p buf_offset %p\n",
|
|
__func__, __LINE__, spdif_dec_handle, inbuf, sync_word_offset, buf_left, buf_offset);
|
|
return -1;
|
|
}
|
|
|
|
/*we need at least period bytes*/
|
|
if (spdif_dec_handle->buf_remain < IEC61937_HEADER_SYNC_PERIOD) {
|
|
need_size = IEC61937_HEADER_SYNC_PERIOD - spdif_dec_handle->buf_remain;
|
|
/*input data is not enough, just copy to internal buf*/
|
|
if (*buf_left < need_size) {
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, *buf_left);
|
|
spdif_dec_handle->buf_remain += *buf_left;
|
|
goto ERROR;
|
|
}
|
|
/*make sure the remain buf has period bytes*/
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, need_size);
|
|
spdif_dec_handle->buf_remain += need_size;
|
|
*buf_offset += need_size;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
|
|
}
|
|
|
|
if (spdif_dec_handle->status == SPDIF_DEC_SYNCING) {
|
|
*sync_word_offset = -1;
|
|
while (*sync_word_offset < 0) {
|
|
/*sync the header, we have at least period bytes*/
|
|
if (spdif_dec_handle->buf_remain < IEC61937_HEADER_SYNC_PERIOD) {
|
|
ALOGE("we should not get there");
|
|
goto DO_SYNC;
|
|
}
|
|
*sync_word_offset = seek_61937_sync_word((char*)*spdifdec_buf, spdif_dec_handle->buf_remain);
|
|
/*if we don't find the header in period bytes, move the last 3 bytes to header*/
|
|
if (*sync_word_offset < 0) {
|
|
memmove(*spdifdec_buf, *spdifdec_buf + spdif_dec_handle->buf_remain - IEC61937_HEADER_COPY_SIZE, IEC61937_HEADER_COPY_SIZE);
|
|
spdif_dec_handle->buf_remain = IEC61937_HEADER_COPY_SIZE;
|
|
need_size = IEC61937_HEADER_SYNC_PERIOD - spdif_dec_handle->buf_remain;
|
|
/*input data is not enough, just copy to internal buf*/
|
|
if (*buf_left < need_size) {
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, *buf_left);
|
|
spdif_dec_handle->buf_remain += *buf_left;
|
|
/*don't find the header, and there is no enough data*/
|
|
goto ERROR;
|
|
}
|
|
/*make the spdif dec buf has period bytes*/
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, need_size);
|
|
spdif_dec_handle->buf_remain += need_size;
|
|
*buf_offset += need_size;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
}
|
|
loop_cnt++;
|
|
}
|
|
/*got here means we find the sync word*/
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCED;
|
|
|
|
data_valid = spdif_dec_handle->buf_remain - *sync_word_offset;
|
|
/*move the header to the beginning of buf*/
|
|
if (*sync_word_offset != 0) {
|
|
memmove(*spdifdec_buf, *spdifdec_buf + *sync_word_offset, data_valid);
|
|
}
|
|
spdif_dec_handle->buf_remain = data_valid;
|
|
|
|
need_size = IEC61937_HEADER_SYNC_PERIOD - data_valid;
|
|
/*get some bytes to make sure it is at least period bytes*/
|
|
if (need_size > 0) {
|
|
/*check if input has enough data*/
|
|
if (*buf_left < need_size) {
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, *buf_left);
|
|
spdif_dec_handle->buf_remain += *buf_left;
|
|
goto ERROR;
|
|
}
|
|
/*make sure the remain buf has period bytes*/
|
|
memcpy(*spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset , need_size);
|
|
spdif_dec_handle->buf_remain += need_size;
|
|
*buf_offset += need_size;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
}
|
|
}
|
|
|
|
/*double check here*/
|
|
/*coverity[INTEGER_OVERFLOW]: buf_remain will not overflow, so it's ok.*/
|
|
*sync_word_offset = seek_61937_sync_word((char*)*spdifdec_buf, spdif_dec_handle->buf_remain);
|
|
if (*sync_word_offset != 0) {
|
|
ALOGE("we can't get here remain=%d,resync iec61937 header", spdif_dec_handle->buf_remain);
|
|
goto DO_SYNC;
|
|
}
|
|
|
|
return 0;
|
|
|
|
DO_SYNC:
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
|
|
ERROR:
|
|
return -1;
|
|
}
|
|
|
|
static int aml_spdif_decoder_addbytes
|
|
(void *phandle
|
|
, const void *inbuf
|
|
, int32_t n_bytes_inbuf
|
|
, uint8_t *spdifdec_buf
|
|
, int32_t *buf_left
|
|
, int32_t *buf_offset
|
|
, int32_t *used_size
|
|
, const void **output_buf
|
|
, int32_t *out_size)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
int32_t need_size = 0;
|
|
uint8_t *buffer = (uint8_t *)inbuf;
|
|
int32_t package_size = 0;
|
|
int32_t payload_size = 0;
|
|
int32_t new_buf_size = 0;
|
|
int32_t dtshd_header_len = 0;
|
|
int32_t dts_type4_subtype = 0;
|
|
int32_t dts_frame_size_is_odd = 0;
|
|
int ret = 0;
|
|
|
|
if (!spdif_dec_handle || !inbuf || !spdifdec_buf || !buf_left || !buf_offset || !used_size || !out_size) {
|
|
ALOGE("%s line %d spdif_dec_handle %p inbuf %p spdifdec_buf %p buf_left %p buf_offset %p used_size %p out_size %p\n",
|
|
__func__, __LINE__, spdif_dec_handle, inbuf, spdifdec_buf, buf_left, buf_offset, used_size, out_size);
|
|
return -1;
|
|
}
|
|
|
|
/* we got here means we find the IEC header and
|
|
* it is at the beginning of spedif dec buf and
|
|
* it has at least period bytes, we can get the pc/pd
|
|
*/
|
|
ret = get_iec61937_info(phandle, spdifdec_buf, IEC61937_HEADER_SIZE, &package_size, &payload_size);
|
|
if (ret != 0) {
|
|
goto DO_SYNC;
|
|
}
|
|
|
|
/*if it is DTS HD, we need more 12 bytes to calculate the payload size*/
|
|
if (spdif_dec_handle->format == AUDIO_FORMAT_DTS_HD) {
|
|
dtshd_header_len = IEC61937_DTSHD_HEAD_SIZE;
|
|
need_size = (IEC61937_HEADER_SIZE + IEC61937_DTSHD_HEAD_SIZE) - spdif_dec_handle->buf_remain;
|
|
if (need_size > 0) {
|
|
/*if there is no enough data, try to get more*/
|
|
if (*buf_left < need_size) {
|
|
memcpy(spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, *buf_left);
|
|
spdif_dec_handle->buf_remain += *buf_left;
|
|
spdif_dec_handle->status = SPDIF_DEC_LACK_DATA;
|
|
goto ERROR;
|
|
}
|
|
memcpy(spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, need_size);
|
|
spdif_dec_handle->buf_remain += need_size;
|
|
*buf_offset += need_size;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
}
|
|
|
|
ret = get_dtshd_info(spdifdec_buf, (IEC61937_HEADER_SIZE + IEC61937_DTSHD_HEAD_SIZE), &payload_size, &dts_type4_subtype);
|
|
if (ret != 0) {
|
|
goto DO_SYNC;
|
|
}
|
|
}
|
|
|
|
/*check whether the input data has a complete payload*/
|
|
if (payload_size == 0) {
|
|
ALOGE("%s wrong format=%d package size =%d payload =%d", __func__, spdif_dec_handle->format, package_size, payload_size);
|
|
goto DO_SYNC;
|
|
}
|
|
|
|
//ALOGI("payload_size:%d odd:%d format:%x", payload_size, payload_size%2, spdif_dec_handle->format);
|
|
if ((payload_size%2) && (spdif_dec_handle->format == AUDIO_FORMAT_DTS_HD || AUDIO_FORMAT_DTS == spdif_dec_handle->format)) {
|
|
dts_frame_size_is_odd = 1;
|
|
payload_size += 1;//for odd frame, the last audio byte comes from high address
|
|
//ALOGI("1.payload_size:%d dts_frame_size_is_odd:%d", payload_size, dts_frame_size_is_odd);
|
|
}
|
|
|
|
/*we have a complete payload*/
|
|
if ((spdif_dec_handle->buf_remain - IEC61937_HEADER_SIZE - dtshd_header_len + *buf_left) >= payload_size) {
|
|
need_size = payload_size - (spdif_dec_handle->buf_remain - IEC61937_HEADER_SIZE - dtshd_header_len);
|
|
if (need_size >= 0) {
|
|
new_buf_size = spdif_dec_handle->buf_remain + need_size;
|
|
if (new_buf_size > spdif_dec_handle->buf_size) {
|
|
spdif_dec_handle->buf = aml_audio_realloc(spdif_dec_handle->buf, new_buf_size);
|
|
if (spdif_dec_handle->buf == NULL) {
|
|
ALOGE("%s realloc buf failed =%d", __func__, new_buf_size);
|
|
goto DO_SYNC;
|
|
}
|
|
ALOGI("%s realloc buf =%d", __func__, new_buf_size);
|
|
spdifdec_buf = spdif_dec_handle->buf;
|
|
spdif_dec_handle->buf_size = new_buf_size;
|
|
}
|
|
|
|
memcpy(spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, need_size);
|
|
*buf_offset += need_size;
|
|
*buf_left = n_bytes_inbuf - *buf_offset;
|
|
/*we may have some stuffing data, we can skip it*/
|
|
if (*buf_left > 0) {
|
|
int32_t stuff_size = package_size - IEC61937_HEADER_SIZE - payload_size - dtshd_header_len;
|
|
if (stuff_size < 0) {
|
|
ALOGE("%s we can't get here suff size =%d", __func__, stuff_size);
|
|
goto DO_SYNC;
|
|
}
|
|
if (*buf_left >= stuff_size) {
|
|
*buf_offset += stuff_size;
|
|
} else {
|
|
*buf_offset += *buf_left;
|
|
}
|
|
}
|
|
*output_buf = (void*)(spdifdec_buf + IEC61937_HEADER_SIZE + dtshd_header_len);
|
|
if (dts_frame_size_is_odd) {
|
|
payload_size -= 1;
|
|
unsigned char *p = (unsigned char*)*output_buf;
|
|
/*ALOGI("2.payload_size:%d dts_frame_size_is_odd:%d (%x,%x,%x,%x)", payload_size, dts_frame_size_is_odd,
|
|
p[payload_size-3], p[payload_size-2], p[payload_size-1], p[payload_size]);*/
|
|
p[payload_size - 1] = p[payload_size];//for odd frame, the last audio byte comes from high address
|
|
}
|
|
*out_size = payload_size;
|
|
*used_size = *buf_offset;
|
|
ALOGV("OK type=0x%x payload size=%d used size=%d\n", spdif_dec_handle->format, payload_size, *buf_offset);
|
|
/*one frame has complete, need find next one*/
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
}else {
|
|
/*internal buf has more data than payload, we only need part of it*/
|
|
*output_buf = (void*)(spdifdec_buf + IEC61937_HEADER_SIZE + dtshd_header_len);
|
|
if (dts_frame_size_is_odd) {
|
|
payload_size -= 1;
|
|
unsigned char *p = (unsigned char*)*output_buf;
|
|
/*ALOGI("3.payload_size:%d dts_frame_size_is_odd:%d (%x,%x,%x,%x)", payload_size, dts_frame_size_is_odd,
|
|
p[payload_size-3], p[payload_size-2], p[payload_size-1], p[payload_size]);*/
|
|
p[payload_size - 1] = p[payload_size];//for odd frame, the last audio byte comes from high address
|
|
|
|
}
|
|
*out_size = payload_size;
|
|
/*move need_size bytes back to original buf*/
|
|
*used_size = *buf_offset + need_size;
|
|
ALOGV("wrap data type=0x%x payload size=%d used size=%d back size =%d\n", spdif_dec_handle->format, payload_size, *buf_offset, need_size);
|
|
if (*used_size <= 0) {
|
|
ALOGE("%s wrong used size =%d", __func__, *used_size);
|
|
goto DO_SYNC;
|
|
}
|
|
/*one frame has complete, need find next one*/
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
}
|
|
} else {
|
|
/*check whether the input buf size is big enough*/
|
|
new_buf_size = spdif_dec_handle->buf_remain + *buf_left;
|
|
if (new_buf_size > spdif_dec_handle->buf_size) {
|
|
spdif_dec_handle->buf = aml_audio_realloc(spdif_dec_handle->buf, new_buf_size);
|
|
if (spdif_dec_handle->buf == NULL) {
|
|
ALOGE("%s realloc buf failed =%d", __func__, new_buf_size);
|
|
goto DO_SYNC;
|
|
}
|
|
ALOGI("%s realloc buf =%d", __func__, new_buf_size);
|
|
spdifdec_buf = spdif_dec_handle->buf;
|
|
spdif_dec_handle->buf_size = new_buf_size;
|
|
}
|
|
memcpy(spdifdec_buf + spdif_dec_handle->buf_remain, buffer + *buf_offset, *buf_left);
|
|
spdif_dec_handle->buf_remain += *buf_left;
|
|
spdif_dec_handle->status = SPDIF_DEC_LACK_DATA;
|
|
goto ERROR;
|
|
}
|
|
if (spdif_dec_handle->payload_size != payload_size) {
|
|
spdif_dec_handle->payload_size = payload_size;
|
|
ALOGV("IEC61937 format =0x%x package size=%d payload_size=%d", spdif_dec_handle->format, package_size, payload_size);
|
|
}
|
|
return 0;
|
|
|
|
|
|
DO_SYNC:
|
|
spdif_dec_handle->buf_remain = 0;
|
|
spdif_dec_handle->status = SPDIF_DEC_SYNCING;
|
|
|
|
ERROR:
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int aml_spdif_decoder_process(void *phandle, const void *inbuf, int32_t n_bytes_inbuf, int32_t *used_size, const void **output_buf, int32_t *out_size)
|
|
{
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
uint8_t *spdifdec_buf = NULL;
|
|
int32_t sync_word_offset = -1;
|
|
int32_t buf_left = 0;
|
|
int32_t buf_offset = 0;
|
|
int32_t ret = 0;
|
|
|
|
if (!spdif_dec_handle || !inbuf) {
|
|
goto ERROR;
|
|
}
|
|
|
|
spdifdec_buf = spdif_dec_handle->buf;
|
|
buf_left = n_bytes_inbuf;
|
|
|
|
ALOGV("%s input buf size=%d status=%d", __func__, n_bytes_inbuf, spdif_dec_handle->status);
|
|
|
|
if (aml_spdif_decoder_do_quick_sync(phandle, inbuf, n_bytes_inbuf, &sync_word_offset, &buf_left, &buf_offset)) {
|
|
ALOGV("%s aml_spdif_decoder_do_quick_sync error return status=%d", __func__, spdif_dec_handle->status);
|
|
goto ERROR;
|
|
}
|
|
|
|
ret = aml_spdif_decoder_find_syncword(phandle, inbuf, n_bytes_inbuf, &sync_word_offset, &buf_left, &buf_offset, &spdifdec_buf);
|
|
if (ret) {
|
|
ALOGV("%s aml_spdif_decoder_find_syncword return %d status=%d", __func__, ret, spdif_dec_handle->status);
|
|
goto ERROR;
|
|
}
|
|
|
|
ret = aml_spdif_decoder_addbytes(phandle, inbuf, n_bytes_inbuf, spdifdec_buf, &buf_left, &buf_offset, used_size, output_buf, out_size);
|
|
ALOGV("%s aml_spdif_decoder_addbytes return %d status=%d", __func__, ret, spdif_dec_handle->status);
|
|
if (ret == 0) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
ERROR:
|
|
*output_buf = NULL;
|
|
*out_size = 0;
|
|
*used_size = n_bytes_inbuf;
|
|
return 0;
|
|
}
|
|
|
|
int aml_spdif_decoder_getformat(void *phandle) {
|
|
struct aml_spdif_decoder *spdif_dec_handle = (struct aml_spdif_decoder *)phandle;
|
|
if (spdif_dec_handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return (int)spdif_dec_handle->format;
|
|
}
|