749 lines
28 KiB
C
749 lines
28 KiB
C
/*
|
|
* Copyright (C) 2010 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_hal_hwsync"
|
|
//#define LOG_NDEBUG 0
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include "aml_audio_log.h"
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/utsname.h>
|
|
#include "audio_hw_utils.h"
|
|
#include "audio_hwsync.h"
|
|
#include "audio_hw.h"
|
|
#include "dolby_lib_api.h"
|
|
#include "aml_audio_ms12_sync.h"
|
|
#include "aml_audio_timer.h"
|
|
#include "audio_hw_ms12_v2.h"
|
|
#include "aml_dump_debug.h"
|
|
#include "aml_audio_property.h"
|
|
|
|
#define MSYNC_CALLBACK_WAIT_TIMEOUT_US (4000*1000)
|
|
|
|
void audio_header_info_reset(audio_header_t *p_header)
|
|
{
|
|
AM_LOGI("p_header %p", p_header);
|
|
if (p_header == NULL) {
|
|
return;
|
|
}
|
|
p_header->hw_sync_state = HW_SYNC_STATE_HEADER;
|
|
p_header->hw_sync_header_cnt = 0;
|
|
p_header->hw_sync_frame_size = 0;
|
|
p_header->bvariable_frame_size = 0;
|
|
p_header->version_num = 0;
|
|
p_header->eos = false;
|
|
|
|
AM_LOGI("done");
|
|
return;
|
|
}
|
|
|
|
audio_header_t *audio_header_info_init(void)
|
|
{
|
|
audio_header_t *p_header = aml_audio_calloc(1, sizeof(audio_header_t));
|
|
if (NULL == p_header) {
|
|
AM_LOGE("malloc pheader failed, size(%zu)", sizeof(audio_header_t));
|
|
return NULL;
|
|
}
|
|
audio_header_info_reset(p_header);
|
|
return p_header;
|
|
}
|
|
|
|
//return bytes cost from input,
|
|
int audio_header_find_frame(audio_header_t *p_header,
|
|
const void *in_buffer, size_t in_bytes, uint64_t *cur_pts, int *outsize)
|
|
{
|
|
size_t remain = in_bytes;
|
|
uint8_t *p = (uint8_t *)in_buffer;
|
|
uint64_t time_diff = 0;
|
|
int pts_found = 0;
|
|
size_t v2_hwsync_header = HW_AVSYNC_HEADER_SIZE_V2;
|
|
int debug_enable = get_debug_value(AML_DEBUG_AUDIOHAL_HW_SYNC);
|
|
int header_sub_version = 0;
|
|
if (p_header == NULL || in_buffer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
while (remain > 0) {
|
|
if (p_header->hw_sync_state == HW_SYNC_STATE_HEADER) {
|
|
p_header->hw_sync_header[p_header->hw_sync_header_cnt++] = *p++;
|
|
remain--;
|
|
|
|
if (p_header->hw_sync_header_cnt == HW_SYNC_VERSION_SIZE) {
|
|
if (!hwsync_header_valid(&p_header->hw_sync_header[0])) {
|
|
p_header->hw_sync_state = HW_SYNC_STATE_HEADER;
|
|
memmove(p_header->hw_sync_header, p_header->hw_sync_header + 1, HW_SYNC_VERSION_SIZE - 1);
|
|
p_header->hw_sync_header_cnt--;
|
|
continue;
|
|
}
|
|
|
|
p_header->version_num = p_header->hw_sync_header[3];
|
|
if (p_header->version_num != 1 && p_header->version_num != 2 && p_header->version_num != 3) {
|
|
ALOGI("invalid pheader version num %d", p_header->version_num);
|
|
}
|
|
}
|
|
|
|
if ((p_header->version_num == 1 && p_header->hw_sync_header_cnt == HW_AVSYNC_HEADER_SIZE_V1) ||
|
|
(p_header->version_num == 2 && p_header->hw_sync_header_cnt == HW_AVSYNC_HEADER_SIZE_V2) ||
|
|
(p_header->version_num == 3 && p_header->hw_sync_header_cnt == HW_AVSYNC_HEADER_SIZE_V3)) {
|
|
|
|
if (p_header->version_num == 2 && p_header->hw_sync_header_cnt == HW_AVSYNC_HEADER_SIZE_V2) {
|
|
v2_hwsync_header = hwsync_header_get_offset(&p_header->hw_sync_header[0]);
|
|
if (v2_hwsync_header > HW_AVSYNC_MAX_HEADER_SIZE) {
|
|
ALOGE("buffer overwrite, check the header size %zu \n", v2_hwsync_header);
|
|
break;
|
|
}
|
|
if (v2_hwsync_header > p_header->hw_sync_header_cnt) {
|
|
ALOGV("need skip more sync header, %zu\n",v2_hwsync_header);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((3 == p_header->version_num) && (HW_AVSYNC_HEADER_SIZE_V3 == p_header->hw_sync_header_cnt)) {
|
|
p_header->clip_front = hwsync_header_get_clip_front(p_header->hw_sync_header);
|
|
p_header->clip_back = hwsync_header_get_clip_back(p_header->hw_sync_header);
|
|
AM_LOGI_IF(debug_enable, "clip_front:%"PRIu64", clip_back:%"PRIu64"", p_header->clip_front, p_header->clip_back);
|
|
}
|
|
|
|
if ((in_bytes - remain) > p_header->hw_sync_header_cnt) {
|
|
ALOGI("got the frame sync header cost %zu", in_bytes - remain);
|
|
}
|
|
|
|
p_header->hw_sync_body_cnt = hwsync_header_get_size(&p_header->hw_sync_header[0]);
|
|
if (p_header->hw_sync_body_cnt != 0) {
|
|
p_header->hw_sync_state = HW_SYNC_STATE_BODY;
|
|
}
|
|
|
|
if (p_header->hw_sync_body_cnt > HWSYNC_MAX_BODY_SIZE) {
|
|
ALOGE("body max size =%d hw_sync_body_cnt =%d", HWSYNC_MAX_BODY_SIZE, p_header->hw_sync_body_cnt);
|
|
p_header->hw_sync_body_cnt = HWSYNC_MAX_BODY_SIZE;
|
|
}
|
|
|
|
if (p_header->hw_sync_frame_size && p_header->hw_sync_body_cnt) {
|
|
if (p_header->hw_sync_frame_size != p_header->hw_sync_body_cnt) {
|
|
p_header->bvariable_frame_size = 1;
|
|
ALOGV("old frame size=%d new=%d", p_header->hw_sync_frame_size, p_header->hw_sync_body_cnt);
|
|
}
|
|
}
|
|
p_header->hw_sync_frame_size = p_header->hw_sync_body_cnt;
|
|
p_header->body_align_cnt = 0; // alisan zz
|
|
p_header->hw_sync_header_cnt = 0; //8.1
|
|
|
|
uint64_t pts = 0, pts_raw = 0;
|
|
pts_raw = pts = hwsync_header_get_pts(&p_header->hw_sync_header[0]);
|
|
if (pts == HWSYNC_PTS_EOS) {
|
|
p_header->eos = true;
|
|
ALOGI("%s: eos detected!", __FUNCTION__);
|
|
continue;
|
|
} else {
|
|
p_header->eos = false;
|
|
}
|
|
header_sub_version = p_header->hw_sync_header[2];
|
|
if (!header_sub_version)
|
|
pts = pts / 1000000 * 90 ;
|
|
time_diff = get_pts_gap(pts, p_header->last_apts_from_header) / 90;
|
|
if (debug_enable) {
|
|
ALOGI("pts 0x%"PRIx64",frame len %"PRIu32"\n", pts, p_header->hw_sync_body_cnt);
|
|
ALOGI("last pts 0x%"PRIx64",diff %"PRIu64" ms\n", p_header->last_apts_from_header, time_diff);
|
|
}
|
|
if (p_header->hw_sync_frame_size > HWSYNC_MAX_BODY_SIZE) {
|
|
ALOGE("pheader frame body %d bigger than pre-defined size %d, need check !!!!!\n",
|
|
p_header->hw_sync_frame_size,HWSYNC_MAX_BODY_SIZE);
|
|
p_header->hw_sync_state = HW_SYNC_STATE_HEADER;
|
|
return 0;
|
|
}
|
|
|
|
if (time_diff > 32) {
|
|
ALOGV("pts time gap %"PRIx64" ms,last %"PRIx64",cur %"PRIx64"\n", time_diff,
|
|
p_header->last_apts_from_header, pts);
|
|
}
|
|
p_header->last_apts_from_header = pts;
|
|
p_header->last_apts_from_header_raw = pts_raw;
|
|
*cur_pts = (pts_raw == HWSYNC_PTS_NA) ? HWSYNC_PTS_NA : pts;
|
|
pts_found = 1;
|
|
}
|
|
continue;
|
|
} else if (p_header->hw_sync_state == HW_SYNC_STATE_BODY) {
|
|
int m = (p_header->hw_sync_body_cnt < remain) ? p_header->hw_sync_body_cnt : remain;
|
|
// process m bytes body with an empty fragment for alignment
|
|
if (m > 0) {
|
|
memcpy(p_header->hw_sync_body_buf + p_header->hw_sync_frame_size - p_header->hw_sync_body_cnt, p, m);
|
|
p += m;
|
|
remain -= m;
|
|
p_header->hw_sync_body_cnt -= m;
|
|
if (p_header->hw_sync_body_cnt == 0) {
|
|
p_header->hw_sync_state = HW_SYNC_STATE_HEADER;
|
|
p_header->hw_sync_header_cnt = 0;
|
|
*outsize = p_header->hw_sync_frame_size;
|
|
/*
|
|
sometimes the audioflinger burst size is smaller than pheader payload
|
|
we need use the last found pts when got a complete pheader payload
|
|
*/
|
|
if (!pts_found) {
|
|
*cur_pts = (p_header->last_apts_from_header_raw == HWSYNC_PTS_NA) ?
|
|
HWSYNC_PTS_NA : p_header->last_apts_from_header;
|
|
|
|
}
|
|
if (debug_enable) {
|
|
ALOGV("we found the frame total body,yeah\n");
|
|
}
|
|
break;//continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return in_bytes - remain;
|
|
}
|
|
|
|
avsync_ctx_t *avsync_ctx_init(void)
|
|
{
|
|
AM_LOGI("<in>");
|
|
avsync_ctx_t *avsync_ctx = aml_audio_calloc(1, sizeof(avsync_ctx_t));
|
|
if (NULL == avsync_ctx)
|
|
{
|
|
AM_LOGI("calloc size:%zu, error!", sizeof(avsync_ctx_t));
|
|
return NULL;
|
|
}
|
|
|
|
avsync_ctx->mediasync_ctx = NULL;
|
|
avsync_ctx->msync_ctx = NULL;
|
|
avsync_ctx->last_lookup_apts = -1;
|
|
avsync_ctx->last_dec_out_frame = 0;
|
|
avsync_ctx->last_output_apts = -1;
|
|
avsync_ctx->get_tuning_latency = NULL;
|
|
avsync_ctx->payload_offset = 0;
|
|
pthread_mutex_init(&avsync_ctx->lock, NULL);
|
|
memset(avsync_ctx->pts_tab, 0, sizeof(apts_tab_t)*HWSYNC_APTS_NUM);
|
|
|
|
AM_LOGI("<out>");
|
|
return avsync_ctx;
|
|
}
|
|
|
|
void avsync_ctx_reset(avsync_ctx_t *avsync_ctx)
|
|
{
|
|
if (NULL == avsync_ctx) {
|
|
return;
|
|
}
|
|
pthread_mutex_lock(&(avsync_ctx->lock));
|
|
avsync_ctx->last_lookup_apts = -1;
|
|
avsync_ctx->last_dec_out_frame = 0;
|
|
avsync_ctx->last_output_apts = -1;
|
|
avsync_ctx->get_tuning_latency = NULL;
|
|
avsync_ctx->payload_offset = 0;
|
|
memset(avsync_ctx->pts_tab, 0, sizeof(apts_tab_t)*HWSYNC_APTS_NUM);
|
|
|
|
if (NULL != avsync_ctx->mediasync_ctx) {
|
|
audio_mediasync_t *mediasync_ctx = avsync_ctx->mediasync_ctx;
|
|
mediasync_ctx->mediasync_id = -1;
|
|
mediasync_ctx->cur_outapts = -1;
|
|
mediasync_ctx->out_start_apts = -1;
|
|
mediasync_ctx->in_apts = -1;
|
|
mediasync_ctx->apolicy.audiopolicy = MEDIASYNC_AUDIO_UNKNOWN;
|
|
}
|
|
|
|
if (NULL != avsync_ctx->msync_ctx) {
|
|
audio_msync_t *msync_ctx = avsync_ctx->msync_ctx;
|
|
/*coverity[missing_lock]: msync_ctx->msync_start does not need to be lock by msync_ctx->msync_mutex*/
|
|
msync_ctx->msync_start = false;
|
|
msync_ctx->msync_action = AV_SYNC_AA_RENDER;
|
|
msync_ctx->msync_action_delta = -1;
|
|
msync_ctx->msync_rendered_pts = -1;
|
|
msync_ctx->first_apts_flag = false;//flag to indicate set first apts
|
|
msync_ctx->msync_first_insert_flag = false;
|
|
msync_ctx->first_apts = -1; //record and print
|
|
}
|
|
pthread_mutex_unlock(&(avsync_ctx->lock));
|
|
return;
|
|
}
|
|
|
|
audio_mediasync_t *mediasync_ctx_init(void)
|
|
{
|
|
AM_LOGI("<in>");
|
|
audio_mediasync_t *mediasync_ctx = aml_audio_calloc(1, sizeof(audio_mediasync_t));
|
|
if (NULL == mediasync_ctx)
|
|
{
|
|
AM_LOGI("calloc size:%zu, error!", sizeof(audio_mediasync_t));
|
|
return NULL;
|
|
}
|
|
|
|
mediasync_ctx->handle = NULL;
|
|
mediasync_ctx->mediasync_id = -1;
|
|
mediasync_ctx->cur_outapts = -1;
|
|
mediasync_ctx->out_start_apts = -1;
|
|
mediasync_ctx->in_apts = -1;
|
|
mediasync_ctx->apolicy.audiopolicy = MEDIASYNC_AUDIO_UNKNOWN;
|
|
AM_LOGI("<out>");
|
|
return mediasync_ctx;
|
|
}
|
|
|
|
audio_msync_t *msync_ctx_init(void)
|
|
{
|
|
AM_LOGI("<in>");
|
|
audio_msync_t *msync_ctx = aml_audio_calloc(1, sizeof(audio_msync_t));
|
|
if (NULL == msync_ctx)
|
|
{
|
|
AM_LOGI("calloc size:%zu, error!", sizeof(audio_msync_t));
|
|
return NULL;
|
|
}
|
|
|
|
msync_ctx->msync_session = NULL;
|
|
msync_ctx->msync_start = false;
|
|
msync_ctx->msync_action = AV_SYNC_AA_RENDER;
|
|
msync_ctx->msync_action_delta = -1;
|
|
msync_ctx->msync_rendered_pts = -1;
|
|
msync_ctx->first_apts_flag = false;//flag to indicate set first apts
|
|
msync_ctx->msync_first_insert_flag = false;
|
|
msync_ctx->first_apts = -1; //record and print
|
|
pthread_mutex_init(&(msync_ctx->msync_mutex), NULL);
|
|
pthread_cond_init(&(msync_ctx->msync_cond), NULL);
|
|
|
|
AM_LOGI("<out>");
|
|
return msync_ctx;
|
|
}
|
|
|
|
void msync_unblock_start(audio_msync_t *msync_ctx)
|
|
{
|
|
pthread_mutex_lock(&(msync_ctx->msync_mutex));
|
|
msync_ctx->msync_start = true;
|
|
pthread_cond_signal(&(msync_ctx->msync_cond));
|
|
pthread_mutex_unlock(&(msync_ctx->msync_mutex));
|
|
}
|
|
|
|
static int msync_start_callback(void *priv, avs_ascb_reason reason)
|
|
{
|
|
ALOGI("msync_callback reason %d", reason);
|
|
msync_unblock_start((audio_msync_t *)priv);
|
|
return 0;
|
|
}
|
|
|
|
static inline uint32_t abs32(int32_t a)
|
|
{
|
|
return ((a) < 0 ? -(a) : (a));
|
|
}
|
|
|
|
#define pts_gap(a, b) abs32(((int)(a) - (int)(b)))
|
|
|
|
bool skip_check_when_gap(struct audio_stream_out *stream, size_t offset, uint64_t apts)
|
|
{
|
|
struct aml_stream_out *aout = (struct aml_stream_out *)stream;
|
|
struct aml_audio_device *adev = aout->dev;
|
|
|
|
if ((adev->gap_offset != 0)
|
|
&& (!adev->gap_ignore_pts)
|
|
&& ((int)(offset - (adev->gap_offset & 0xffffffff)) >= 0)) {
|
|
/* when PTS gap exists (Netflix specific), skip APTS reset between [adev->gap_pts, adev->gap_pts + 500ms] */
|
|
AM_LOGI("gap_pts = 0x%"PRIu64"", apts);
|
|
adev->gap_pts = apts;
|
|
adev->gap_ignore_pts = true;
|
|
set_ms12_main_audio_mute(&adev->ms12, true, 32);
|
|
}
|
|
|
|
if (adev->gap_ignore_pts) {
|
|
if (pts_gap(apts, adev->gap_pts) < 90 * 500) {
|
|
AM_LOGI("gap_pts = 0x%"PRIu64", adev->gap_pts:0x%x, skip", apts, adev->gap_pts);
|
|
return true;
|
|
} else {
|
|
AM_LOGI("gap process done, recovered APTS/PCR checking");
|
|
adev->gap_ignore_pts = false;
|
|
adev->gap_offset = 0;
|
|
adev->gap_passthrough_state = GAP_PASSTHROUGH_STATE_IDLE;
|
|
if (true != adev->decoder_mute) {
|
|
set_ms12_main_audio_mute(&adev->ms12, false, 32);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int msync_set_first_pts(audio_msync_t *msync_ctx, uint32_t pts)
|
|
{
|
|
if ((NULL == msync_ctx) || (NULL == msync_ctx->msync_session)) {
|
|
AM_LOGE("msync_ctx:%p error!", msync_ctx);
|
|
return -1;
|
|
}
|
|
|
|
msync_ctx->first_apts = pts;
|
|
|
|
if (msync_ctx->msync_session) {
|
|
int r = av_sync_audio_start(msync_ctx->msync_session, pts, 0,
|
|
msync_start_callback, msync_ctx);
|
|
if (r == AV_SYNC_ASTART_SYNC) {
|
|
AM_LOGI("MSYNC AV_SYNC_ASTART_SYNC");
|
|
msync_ctx->msync_action = AV_SYNC_AA_RENDER;
|
|
msync_ctx->first_apts_flag = true;
|
|
} else if (r == AV_SYNC_ASTART_ASYNC) {
|
|
struct timespec ts;
|
|
int rc = 0;
|
|
memset(&ts, 0, sizeof(ts));
|
|
ts_wait_time_us(&ts, MSYNC_CALLBACK_WAIT_TIMEOUT_US);
|
|
pthread_mutex_lock(&(msync_ctx->msync_mutex));
|
|
while (!msync_ctx->msync_start) {
|
|
AM_LOGI("wait %d ms", MSYNC_CALLBACK_WAIT_TIMEOUT_US/1000);
|
|
rc = pthread_cond_timedwait(&(msync_ctx->msync_cond), &(msync_ctx->msync_mutex), &ts);
|
|
if (ETIMEDOUT == rc) {
|
|
AM_LOGE("msync wait timeout, set start now!");
|
|
msync_ctx->msync_start = true;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&(msync_ctx->msync_mutex));
|
|
ALOGI("MSYNC AV_SYNC_ASTART_ASYNC");
|
|
msync_ctx->msync_action = AV_SYNC_AA_RENDER;
|
|
msync_ctx->first_apts_flag = true;
|
|
} else if (r == AV_SYNC_ASTART_AGAIN) {
|
|
AM_LOGI("MSYNC AV_SYNC_ASTART_AGAIN");
|
|
msync_ctx->msync_action = AV_SYNC_AA_DROP;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int msync_get_policy(struct audio_stream_out *stream, uint64_t apts)
|
|
{
|
|
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
|
|
avsync_ctx_t *avsync_ctx = NULL;
|
|
audio_msync_t *msync_ctx = NULL;
|
|
int force_action = 0;
|
|
|
|
if (NULL == aml_out) {
|
|
AM_LOGE("NULL == aml_out, need check!!");
|
|
return -1;
|
|
}
|
|
avsync_ctx = aml_out->avsync_ctx;
|
|
if ((NULL == avsync_ctx) || (NULL == avsync_ctx->msync_ctx)) {
|
|
AM_LOGE("avsync_type:%d, avsync_ctx:%p", aml_out->avsync_type, avsync_ctx);
|
|
return -1;
|
|
}
|
|
|
|
msync_ctx = avsync_ctx->msync_ctx;
|
|
do {
|
|
if (false == msync_ctx->first_apts_flag) {
|
|
pthread_mutex_lock(&(aml_out->avsync_ctx->lock));
|
|
msync_set_first_pts(msync_ctx, (uint32_t)apts);
|
|
pthread_mutex_unlock(&(aml_out->avsync_ctx->lock));
|
|
break;
|
|
}
|
|
|
|
if (true == aml_out->eos) {
|
|
pthread_mutex_lock(&(aml_out->avsync_ctx->lock));
|
|
msync_ctx->msync_action = AV_SYNC_AA_RENDER;
|
|
pthread_mutex_unlock(&(aml_out->avsync_ctx->lock));
|
|
break;
|
|
}
|
|
|
|
struct audio_policy policy;
|
|
memset(&policy, 0, sizeof(policy));
|
|
policy.action = msync_ctx->msync_action;
|
|
av_sync_audio_render(msync_ctx->msync_session, apts, &policy);
|
|
force_action = aml_audio_property_get_int("media.audiohal.action", 0);
|
|
/* 0: default, no force action
|
|
* 1: AA_RENDER
|
|
* 2: AA_DROP
|
|
* 3: AA_INSERT
|
|
*/
|
|
pthread_mutex_lock(&(aml_out->avsync_ctx->lock));
|
|
msync_ctx->msync_action_delta = policy.delta;
|
|
if (force_action) {
|
|
policy.action = force_action - 1;
|
|
if (policy.action == AV_SYNC_AA_INSERT) {
|
|
msync_ctx->msync_action_delta = -1;
|
|
} else if (policy.action == AV_SYNC_AA_DROP) {
|
|
msync_ctx->msync_action_delta = 1;
|
|
}
|
|
}
|
|
|
|
if (msync_ctx->msync_action != policy.action) {
|
|
/* msync_action_delta is > 0 for AV_SYNC_AA_DROP
|
|
* and MS12 "-sync <positive>" for insert
|
|
*/
|
|
if (policy.action == AV_SYNC_AA_INSERT) {
|
|
AM_LOGI("MSYNC action switched to AA_INSERT.");
|
|
} else if (policy.action == AV_SYNC_AA_DROP) {
|
|
AM_LOGI("MSYNC action switched to AA_DROP.");
|
|
} else {
|
|
AM_LOGI("MSYNC action switched to AA_RENDER.");
|
|
}
|
|
msync_ctx->msync_action = policy.action;
|
|
msync_ctx->msync_first_insert_flag = false;
|
|
}
|
|
pthread_mutex_unlock(&(aml_out->avsync_ctx->lock));
|
|
} while (0);
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************************
|
|
* Function: avsync_reset_apts_tbl
|
|
*
|
|
* Description: Reset the pts table and the payload offset
|
|
*
|
|
* Inputs: valid audio avsync_ctx_t pointer
|
|
*
|
|
* Outputs: NONE
|
|
*
|
|
* Return: NONE
|
|
*
|
|
* Notes: For dolby ms12 cleanup, the consumed payload offset by dolby ms12 SDK will reset too,
|
|
so, the pts table and the payload need reset correspond.
|
|
**************************************************************************************/
|
|
void avsync_reset_apts_tbl(avsync_ctx_t *avsync_ctx)
|
|
{
|
|
if (NULL == avsync_ctx) {
|
|
ALOGE("%s avsync_ctx null point", __func__);
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&avsync_ctx->lock);
|
|
ALOGI("%s Reset, payload_offset %zu", __func__,
|
|
avsync_ctx->payload_offset);
|
|
|
|
/* reset payload offset and pts table */
|
|
avsync_ctx->payload_offset = 0;
|
|
memset(avsync_ctx->pts_tab, 0, sizeof(apts_tab_t)*HWSYNC_APTS_NUM);
|
|
pthread_mutex_unlock(&avsync_ctx->lock);
|
|
return;
|
|
}
|
|
|
|
int avsync_checkin_apts(avsync_ctx_t *avsync_ctx, size_t offset, uint64_t apts)
|
|
{
|
|
int i = 0;
|
|
int ret = -1;
|
|
int debug_enable = get_debug_value(AML_DEBUG_AUDIOHAL_HW_SYNC);
|
|
apts_tab_t *pts_tab = NULL;
|
|
if (!avsync_ctx) {
|
|
ALOGE("%s null point", __func__);
|
|
return -1;
|
|
}
|
|
if (debug_enable) {
|
|
ALOGI("++ %s checkin ,offset %zu,apts 0x%"PRIx64"", __func__, offset, apts);
|
|
}
|
|
pthread_mutex_lock(&avsync_ctx->lock);
|
|
pts_tab = avsync_ctx->pts_tab;
|
|
for (i = 0; i < HWSYNC_APTS_NUM; i++) {
|
|
if (pts_tab[i].valid && (pts_tab[i].offset == offset)) {
|
|
/* for duplicated record, reuse previous entry.
|
|
* This may happen when msync returns ASYNC_AGAIN when first checkin
|
|
* record at offset 0 are overwritten after dropping data
|
|
*/
|
|
pts_tab[i].pts = apts;
|
|
if (debug_enable) {
|
|
ALOGI("%s:%d checkin done,offset %zu,apts 0x%"PRIx64"", __func__, __LINE__, offset, apts);
|
|
}
|
|
ret = 0;
|
|
break;
|
|
} else if (!pts_tab[i].valid) {
|
|
pts_tab[i].pts = apts;
|
|
pts_tab[i].offset = offset;
|
|
pts_tab[i].valid = 1;
|
|
if (debug_enable) {
|
|
ALOGI("%s:%d checkin done,offset %zu,apts 0x%"PRIx64"", __func__, __LINE__, offset, apts);
|
|
}
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&avsync_ctx->lock);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
for audio tunnel mode.the apts checkin align is based on
|
|
the audio payload size.
|
|
normally for Netflix case:
|
|
1)dd+ 768 byte per frame
|
|
2)LPCM from aac decoder, normally 4096 for LC AAC.8192 for SBR AAC
|
|
for the LPCM, the MS12 normally drain 6144 bytes per times which is
|
|
the each DD+ decoder output,we need align the size to 4096/8192 align
|
|
to checkout the apts.
|
|
*/
|
|
int avsync_lookup_apts(avsync_ctx_t *avsync_ctx, size_t offset, uint64_t *p_apts)
|
|
{
|
|
int i = 0;
|
|
size_t align = offset;
|
|
int ret = -1;
|
|
int debug_enable = 0;
|
|
apts_tab_t *pts_tab = NULL;
|
|
uint64_t nearest_pts = -1;
|
|
uint32_t nearest_offset = 0;
|
|
uint32_t min_offset = 0x7fffffff;
|
|
int match_index = -1;
|
|
debug_enable = get_debug_value(AML_DEBUG_AUDIOHAL_HW_SYNC);
|
|
|
|
// add protection to avoid NULL pointer.
|
|
if (!avsync_ctx) {
|
|
ALOGE("%s null point", __func__);
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&avsync_ctx->lock);
|
|
#if 0
|
|
/*the hw_sync_frame_size will be an issue if it is fixed one*/
|
|
//ALOGI("Adaptive steam =%d", p_header->bvariable_frame_size);
|
|
if (!p_header->bvariable_frame_size) {
|
|
if (p_header->hw_sync_frame_size) {
|
|
align = offset - offset % p_header->hw_sync_frame_size;
|
|
} else {
|
|
align = offset;
|
|
}
|
|
} else {
|
|
align = offset;
|
|
}
|
|
#endif
|
|
pts_tab = avsync_ctx->pts_tab;
|
|
for (i = 0; i < HWSYNC_APTS_NUM; i++) {
|
|
if (pts_tab[i].valid) {
|
|
if (pts_tab[i].offset == align) {
|
|
*p_apts = pts_tab[i].pts;
|
|
nearest_offset = pts_tab[i].offset;
|
|
ret = 0;
|
|
if (debug_enable) {
|
|
ALOGI("%s pts checkout done,offset %zu,align %zu,pts 0x%"PRIx64"",
|
|
__func__, offset, align, *p_apts);
|
|
}
|
|
break;
|
|
} else if (pts_tab[i].offset < align) {
|
|
/*find the nearest one*/
|
|
if ((align - pts_tab[i].offset) < min_offset) {
|
|
min_offset = align - pts_tab[i].offset;
|
|
match_index = i;
|
|
nearest_pts = pts_tab[i].pts;
|
|
nearest_offset = pts_tab[i].offset;
|
|
}
|
|
pts_tab[i].valid = 0;
|
|
}
|
|
}
|
|
}
|
|
if (i == HWSYNC_APTS_NUM) {
|
|
if (-1 != nearest_pts) {
|
|
ret = 0;
|
|
*p_apts = nearest_pts;
|
|
/*keep it as valid, it may be used for next lookup*/
|
|
pts_tab[match_index].valid = 1;
|
|
#if 0
|
|
/*sometimes, we can't get the correct ddp pts, but we have a nearest one
|
|
*we add one frame duration
|
|
*/
|
|
if (out->hal_internal_format == AUDIO_FORMAT_AC3 ||
|
|
out->hal_internal_format == AUDIO_FORMAT_E_AC3) {
|
|
*p_apts += (1536 * 1000) / out->hal_rate * 90;
|
|
ALOGI("correct nearest pts 0x%llx offset %u align %zu", *p_apts, nearest_offset, align);
|
|
}
|
|
#endif
|
|
if (debug_enable)
|
|
ALOGI("find nearest pts 0x%"PRIx64" offset %"PRIu32" align %zu", *p_apts, nearest_offset, align);
|
|
} else {
|
|
ALOGE("%s,apts lookup failed,align %zu,offset %zu", __func__, align, offset);
|
|
}
|
|
}
|
|
#if 0
|
|
if ((ret == 0) && audio_is_linear_pcm(out->hal_internal_format)) {
|
|
int diff = 0;
|
|
int pts_diff = 0;
|
|
uint32_t frame_size = out->hal_frame_size;
|
|
uint32_t sample_rate = out->hal_rate;
|
|
diff = (offset >= nearest_offset) ? (offset - nearest_offset) : 0;
|
|
if ((frame_size != 0) && (sample_rate != 0)) {
|
|
pts_diff = (diff * 1000/frame_size)/sample_rate;
|
|
}
|
|
*p_apts += pts_diff * 90;
|
|
if (debug_enable) {
|
|
ALOGI("data offset =%zu pts offset =%d diff =%" PRIuFAST16 " pts=0x%llx pts diff =%d", offset, nearest_offset, offset - nearest_offset, *p_apts, pts_diff);
|
|
}
|
|
}
|
|
#endif
|
|
pthread_mutex_unlock(&avsync_ctx->lock);
|
|
return ret;
|
|
}
|
|
|
|
av_sync_policy_e get_and_map_avsync_policy(avsync_ctx_t *avsync_ctx, int avsync_type)
|
|
{
|
|
av_sync_policy_e audio_sync_policy = AV_SYNC_AUDIO_UNKNOWN;
|
|
switch (avsync_type)
|
|
{
|
|
case AVSYNC_TYPE_MEDIASYNC:
|
|
{
|
|
audio_mediasync_t *mediasync_ctx = avsync_ctx->mediasync_ctx;
|
|
if (NULL == mediasync_ctx) {
|
|
AM_LOGE("mediasync_ctx is %p", mediasync_ctx);
|
|
return audio_sync_policy;
|
|
}
|
|
switch (mediasync_ctx->apolicy.audiopolicy)
|
|
{
|
|
case MEDIASYNC_AUDIO_DROP_PCM:
|
|
audio_sync_policy = AV_SYNC_AUDIO_DROP_PCM;
|
|
break;
|
|
case MEDIASYNC_AUDIO_INSERT:
|
|
audio_sync_policy = AV_SYNC_AUDIO_INSERT;
|
|
break;
|
|
case MEDIASYNC_AUDIO_NORMAL_OUTPUT:
|
|
audio_sync_policy = AV_SYNC_AUDIO_NORMAL_OUTPUT;
|
|
break;
|
|
case MEDIASYNC_AUDIO_ADJUST_CLOCK:
|
|
audio_sync_policy = AV_SYNC_AUDIO_ADJUST_CLOCK;
|
|
break;
|
|
case MEDIASYNC_AUDIO_RESAMPLE:
|
|
audio_sync_policy = AV_SYNC_AUDIO_RESAMPLE;
|
|
break;
|
|
case MEDIASYNC_AUDIO_MUTE:
|
|
audio_sync_policy = AV_SYNC_AUDIO_MUTE;
|
|
break;
|
|
default :
|
|
audio_sync_policy = AV_SYNC_AUDIO_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case AVSYNC_TYPE_MSYNC:
|
|
{
|
|
audio_msync_t *msync_ctx = avsync_ctx->msync_ctx;
|
|
if (NULL == msync_ctx) {
|
|
AM_LOGE("msync_ctx is %p", msync_ctx);
|
|
return audio_sync_policy;
|
|
}
|
|
switch (msync_ctx->msync_action)
|
|
{
|
|
case AV_SYNC_AA_DROP:
|
|
audio_sync_policy = AV_SYNC_AUDIO_DROP_PCM;
|
|
break;
|
|
case AV_SYNC_AA_INSERT:
|
|
audio_sync_policy = AV_SYNC_AUDIO_INSERT;
|
|
break;
|
|
case AV_SYNC_AA_RENDER:
|
|
audio_sync_policy = AV_SYNC_AUDIO_NORMAL_OUTPUT;
|
|
break;
|
|
default :
|
|
audio_sync_policy = AV_SYNC_AUDIO_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default :
|
|
audio_sync_policy = AV_SYNC_AUDIO_UNKNOWN;
|
|
break;
|
|
}
|
|
return audio_sync_policy;
|
|
}
|
|
|