/* * Copyright (C) 2017 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_primary" //#define LOG_NDEBUG 0 #include "aml_audio_log.h" #include #include #include #include "audio_hw.h" #include "audio_hw_utils.h" #include "aml_audio_stream.h" #include "tv_patch_avsync.h" #include "alsa_manager.h" #include "dolby_lib_api.h" #include "aml_alsa_mixer.h" #include "audio_hw_resource_mgr.h" #include "amlAudioMixer.h" #include "tv_patch_ctrl.h" #include "audio_port.h" #include "aml_audio_output.h" #include "tv_patch.h" #include "aml_audio_property.h" static int get_tvin_delay(struct aml_mixer_handle *mixer_handle); static int get_tvin_max_delay(struct aml_mixer_handle *mixer_handle); static int get_tvin_min_delay(struct aml_mixer_handle *mixer_handle); static int aml_dev_tune_video_path_latency(struct aml_mixer_handle *mixer_handle, unsigned int video_val); static int get_tvin_delay(struct aml_mixer_handle *mixer_handle) { int delay = 0; delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_DELAY); return delay; } static int get_tvin_max_delay(struct aml_mixer_handle *mixer_handle) { int delay = 0; delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_MAX_DELAY); return delay; } static int get_tvin_min_delay(struct aml_mixer_handle *mixer_handle) { int delay = 0; delay = aml_mixer_ctrl_get_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_MIN_DELAY); return delay; } static void check_skip_frames(struct aml_audio_patch *patch) { struct aml_audio_device *aml_dev; uint64_t frames = 0; uint64_t alsa_out_i2s_ltcy = -1; if (!patch) { AM_LOGE("patch is NULL"); return ; } aml_dev = (struct aml_audio_device *)patch->dev; patch->skip_frames = 0; /* for spk, check i2s device latency */ if (patch->need_do_avsync == true && patch->is_avsync_start == false && is_arc_connected(aml_dev) == 0) { frames = aml_audio_pcm_out_get_delayframe(aml_dev, false); if (frames > 0) { alsa_out_i2s_ltcy = frames / SAMPLE_RATE_MS; /* start to skip the output frames to reduce alsa out latency */ if (alsa_out_i2s_ltcy >= AVSYNC_ALSA_OUT_MAX_LATENCY) { patch->skip_frames = 1; } } } int alsa_out_spdif_ltcy = -1; /* for arc, check spdif device latency */ if (patch->need_do_avsync == true && patch->is_avsync_start == false && is_arc_connected(aml_dev) == 1) { if ( 0 == aml_alsa_output_getinfo(aml_dev->alsa_handle[DIGITAL_DEVICE], OUTPUT_INFO_DELAYFRAME, (alsa_output_info_t *)&alsa_out_spdif_ltcy)) { /* start to skip the output frames to reduce alsa out latency */ if (alsa_out_spdif_ltcy >= AVSYNC_ALSA_OUT_MAX_LATENCY_ARC) { patch->skip_frames = 1; } } } if (patch->skip_frames > 0) { ALOGI("spdif latency:%d i2s latency: %" PRIu64 " skip frame:%d", alsa_out_spdif_ltcy, alsa_out_i2s_ltcy, patch->skip_frames); } } int calc_frame_to_latency(int frames, audio_format_t format) { int latency = frames / SAMPLE_RATE_MS; if (format == AUDIO_FORMAT_E_AC3) { latency /= EAC3_MULTIPLIER; } else if (format == AUDIO_FORMAT_MAT || (format == AUDIO_FORMAT_DOLBY_TRUEHD)) { latency /= MAT_MULTIPLIER; } return latency; } static int calc_latency_to_frame(int latency, audio_format_t format) { int frames = 0; int coefficient = 1; if (format == AUDIO_FORMAT_E_AC3) { coefficient = EAC3_MULTIPLIER; } else if (format == AUDIO_FORMAT_MAT || (format == AUDIO_FORMAT_DOLBY_TRUEHD)) { coefficient = MAT_MULTIPLIER; } frames = latency * SAMPLE_RATE_MS * coefficient; return frames; } int clear_buffer_for_avsync(struct aml_audio_patch *patch) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); AM_LOGI("stream_out(%p) is_pcm(%d) is_game_mode(%d)", patch->stream_out, audio_is_linear_pcm(patch->aformat), is_low_latency_mode(aml_dev)); if (patch->stream_out) { patch->stream_out->flush(patch->stream_out); } if ((audio_is_linear_pcm(patch->aformat)) && is_low_latency_mode(aml_dev)) { ring_buffer_reset(&patch->aml_ringbuffer); aml_audio_pcm_out_flush(aml_dev); } return 0; } static int ringbuffer_seek(struct aml_audio_patch *patch, int tune_val) { int space = 0, seek_space = 0, frame_size = 0; frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT); space = calc_latency_to_frame(tune_val, patch->aformat) * frame_size; seek_space = ring_buffer_seek(&patch->aml_ringbuffer, space); if (seek_space == space) { ALOGV(" --tuning audio ringbuffer %dms successfully!\n", tune_val); } else { ALOGV(" --tuning audio ringbuffer require %d vs actual seek %d\n", space, seek_space); tune_val = calc_frame_to_latency(seek_space/frame_size, patch->aformat); } return tune_val; } int submix_ringbuffer_seek(struct aml_audio_patch *patch, int tune_val) { struct aml_audio_device *aml_dev; aml_dev = (struct aml_audio_device *)patch->dev; struct aml_stream_out *out = (struct aml_stream_out *)patch->stream_out; if (!out) { AM_LOGE("Error, Failed to get patch out stream, return!"); return 0; } if (!aml_dev->audio_mixer) { AM_LOGE("Error, Failed to get submix, return!"); return 0; } int space = 0, seek_space = 0, frame_size = 0; frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT); space = calc_latency_to_frame(tune_val, AUDIO_FORMAT_PCM_16_BIT) * frame_size; seek_space = mixer_seek_inport(aml_dev->audio_mixer, out->inputPortID, space); if (seek_space == space) { ALOGV(" --tuning audio smringbuffer %dms successfully!\n", tune_val); } else { ALOGV(" --tuning audio smringbuffer require %d vs actual seek %d\n", space, seek_space); tune_val = calc_frame_to_latency(seek_space/frame_size, AUDIO_FORMAT_PCM_16_BIT); } return tune_val; } static int hdmi_get_ms12_decoder_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_DECODER_LATENCY_PROPERTY; latency_ms = MS12_DECODER_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_ms12_pipeline_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_PIPELINE_LATENCY_PROPERTY; latency_ms = MS12_PIPELINE_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_ms12_dap_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_DAP_LATENCY_PROPERTY; latency_ms = MS12_DAP_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_ms12_encoder_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_ENCODER_LATENCY_PROPERTY; latency_ms = MS12_ENCODER_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_ms12_dd_ddp_buffer_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_DD_DDP_BUFFER_LATENCY_PROPERTY; latency_ms = MS12_DD_DDP_BUFFER_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_ms12_mat_buffer_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= MS12_MAT_BUFFER_LATENCY_PROPERTY; latency_ms = MS12_MAT_BUFFER_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_avr_pcm_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= AVR_LATENCY_PCM_PROPERTY; latency_ms = AVR_LATENCY_PCM; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_avr_dd_ddp_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= AVR_LATENCY_PROPERTY; latency_ms = AVR_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } static int hdmi_get_avr_raw_pcm_latency() { int latency_ms = 0; char *prop_name = NULL; prop_name= AVR_RAW_PCM_LATENCY_PROPERTY; latency_ms = AVR_RAW_PCM_LATENCY; if (prop_name) { latency_ms = aml_audio_property_get_int(prop_name, latency_ms); } ALOGV("%s latency ms =%d", __func__, latency_ms); return latency_ms; } int aml_dev_sample_audio_path_latency(struct aml_audio_patch *patch, char *latency_details) { struct aml_audio_device *aml_dev; int rbuf_ltcy = 0, spk_tuning_ltcy = 0, ms12_ltcy = 0, alsa_in_ltcy = 0, submix_in_ltcy = 0; int alsa_out_i2s_ltcy = 0, alsa_out_spdif_ltcy = 0, alsa_output_latency = 0; int whole_path_ltcy = 0, in_path_ltcy = 0, out_path_ltcy = 0, cap_ltcy = 0; uint64_t frames = 0; int alsa_frames = 0; int frame_size = 0; int rbuf_avail = 0; if (!patch) { return 0; } aml_dev = (struct aml_audio_device *)patch->dev; frame_size = CHANNEL_CNT * audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT); rbuf_avail = get_buffer_read_space(&patch->aml_ringbuffer); if (rbuf_avail == -1) { AM_LOGE("get_buffer_read_space err, need check!!"); } rbuf_ltcy = calc_frame_to_latency(rbuf_avail / frame_size, patch->aformat); patch->audio_latency.ringbuffer_latency = rbuf_ltcy; ALOGV(" audio ringbuf latency = %d", rbuf_ltcy); if (aml_dev->useSubMix && patch->stream_out) { struct aml_stream_out *out = (struct aml_stream_out *)patch->stream_out; submix_in_ltcy = mixer_get_inport_latency_frames(aml_dev->audio_mixer, out->inputPortID) / SAMPLE_RATE_MS; patch->audio_latency.submix_in_latency = submix_in_ltcy; } if (aml_dev->spk_tuning_lvl) { rbuf_avail = get_buffer_read_space(&aml_dev->spk_tuning_rbuf); if (rbuf_avail == -1) { AM_LOGE("get_buffer_read_space err, need check!!"); } spk_tuning_ltcy = rbuf_avail / frame_size / SAMPLE_RATE_MS; patch->audio_latency.user_tune_latency = spk_tuning_ltcy; ALOGV(" audio spk tuning latency = %d", spk_tuning_ltcy); } else { patch->audio_latency.user_tune_latency = 0; } if ((eDolbyMS12Lib == aml_dev->dolby_lib_type) && aml_dev->ms12.dolby_ms12_enable) { audio_format_t format = patch->aformat; int ms12_latency_decoder = hdmi_get_ms12_decoder_latency(); int ms12_latency_pipeline = hdmi_get_ms12_pipeline_latency(); int ms12_latency_dap = hdmi_get_ms12_dap_latency(); int ms12_latency_encoder = hdmi_get_ms12_encoder_latency(); int ms12_dd_ddp_buffer = hdmi_get_ms12_dd_ddp_buffer_latency(); int ms12_mat_buffer = hdmi_get_ms12_mat_buffer_latency(); ms12_ltcy += ms12_latency_pipeline; if (!audio_is_linear_pcm(format)) { if ((format == AUDIO_FORMAT_AC3) || (format == AUDIO_FORMAT_E_AC3)) ms12_ltcy += ms12_latency_decoder + ms12_dd_ddp_buffer; else if ((format == AUDIO_FORMAT_MAT) || (format == AUDIO_FORMAT_DOLBY_TRUEHD)) ms12_ltcy += ms12_latency_decoder + ms12_mat_buffer; } if (aml_dev->sink_format == AUDIO_FORMAT_PCM_16_BIT) { ms12_ltcy += ms12_latency_dap; } else if ((aml_dev->optical_format == AUDIO_FORMAT_AC3) || (aml_dev->optical_format == AUDIO_FORMAT_E_AC3)) { ms12_ltcy += ms12_latency_encoder; } /* if arc is connected and format setting is "Passthrough", bypass MS12 */ if (is_arc_connected(aml_dev) == 1 && aml_dev->hdmi_format == BYPASS) ms12_ltcy = 0; patch->audio_latency.ms12_latency = ms12_ltcy; ALOGV(" audio ms12 latency = %d, format = %x", ms12_ltcy, format); } else { patch->audio_latency.ms12_latency = 0; } if (aml_dev->alsa_handle[I2S_DEVICE]) { frames = aml_audio_pcm_out_get_delayframe(aml_dev, false); alsa_out_i2s_ltcy = frames / SAMPLE_RATE_MS; patch->audio_latency.alsa_i2s_out_latency = alsa_out_i2s_ltcy; ALOGV(" audio_hw_primary audio i2s latency = %d", alsa_out_i2s_ltcy); } else if (aml_dev->useSubMix) { alsa_out_i2s_ltcy = mixer_get_outport_latency_frames(aml_dev->audio_mixer) / SAMPLE_RATE_MS; patch->audio_latency.alsa_i2s_out_latency = alsa_out_i2s_ltcy; } else { alsa_out_i2s_ltcy = 40; patch->audio_latency.alsa_i2s_out_latency = 0; } if (aml_dev->alsa_handle[DIGITAL_DEVICE]) { aml_alsa_output_getinfo(aml_dev->alsa_handle[DIGITAL_DEVICE], OUTPUT_INFO_DELAYFRAME, (alsa_output_info_t *)&alsa_out_spdif_ltcy); patch->audio_latency.alsa_spdif_out_latency = alsa_out_spdif_ltcy; ALOGV(" audio spdif latency = %d", alsa_out_spdif_ltcy); } else { alsa_out_spdif_ltcy = 40; patch->audio_latency.alsa_spdif_out_latency = 0; } alsa_frames = audio_input_get_delay(patch->input_type); if (alsa_frames >= 0) { alsa_in_ltcy = calc_frame_to_latency(alsa_frames, patch->aformat); } patch->audio_latency.alsa_in_latency = alsa_in_ltcy; int avr_pcm_ltcy = hdmi_get_avr_pcm_latency(); int avr_dd_ddp_ltcy = hdmi_get_avr_dd_ddp_latency(); int avr_raw_pcm_ltcy = hdmi_get_avr_raw_pcm_latency(); if (aml_dev->sink_format == AUDIO_FORMAT_PCM_16_BIT) { out_path_ltcy = alsa_out_i2s_ltcy + spk_tuning_ltcy; alsa_output_latency = alsa_out_i2s_ltcy; /* In AVR case, for PCM only AVR or UI setting to PCM */ /* add more 10ms AVR latency */ if (aml_dev->active_outport == OUTPORT_HDMI_ARC) { alsa_output_latency += avr_pcm_ltcy; } } else if (aml_dev->sink_format == AUDIO_FORMAT_AC3 || aml_dev->sink_format == AUDIO_FORMAT_E_AC3 || aml_dev->sink_format == AUDIO_FORMAT_MAT) { if ((aml_dev->optical_format == AUDIO_FORMAT_AC3) || (aml_dev->optical_format == AUDIO_FORMAT_E_AC3)) { /* For dd/ddp output of AVR, add more 80ms latency */ out_path_ltcy = alsa_out_spdif_ltcy + avr_dd_ddp_ltcy; alsa_output_latency = alsa_out_spdif_ltcy; } else if (aml_dev->optical_format == AUDIO_FORMAT_MAT) { /* For mat output of AVR, add more 20ms latency */ out_path_ltcy = alsa_out_spdif_ltcy + avr_raw_pcm_ltcy; alsa_output_latency = alsa_out_spdif_ltcy; } else { out_path_ltcy = alsa_out_spdif_ltcy; alsa_output_latency = alsa_out_spdif_ltcy; } } // if audio_delay feature enable, add port delay to avsync if (aml_dev->max_audio_delay) { int audio_port_delay = 0; switch (aml_dev->active_outport) { case OUTPORT_HDMI_ARC: case OUTPORT_HDMI: case OUTPORT_SPDIF: audio_port_delay = aml_audio_get_port_delay("hdmi");//SPDIF/SPDIF_RAW/SPDIF_B_RAW's delay always is same. break; case OUTPORT_SPEAKER: audio_port_delay = aml_audio_get_port_delay("speaker"); break; case OUTPORT_HEADPHONE: audio_port_delay = aml_audio_get_port_delay("headphone"); break; default : break; } out_path_ltcy += audio_port_delay; AM_LOGI_IF(aml_dev->debug_flag, "active_outport:%d audio_delay:%d", aml_dev->active_outport, audio_port_delay); } if (aml_dev->effect_ctrl.vx_enable) { out_path_ltcy += VIRTUAL_X_LATENCY; AM_LOGI(" VX enable. Set default latency:%d", VIRTUAL_X_LATENCY); } #ifndef NO_AUDIO_CAP /*coverity[missing_lock]: adev->cap_buffer does not need to be lock*/ if (aml_dev->capture_handle) { cap_ltcy = aml_dev->cap_delay; } #endif /* calc whole path latency considering with format */ in_path_ltcy = alsa_in_ltcy + rbuf_ltcy + ms12_ltcy; out_path_ltcy += submix_in_ltcy; whole_path_ltcy = in_path_ltcy + out_path_ltcy + cap_ltcy; AM_LOGI_IF(aml_dev->debug_flag, "total: %d alsa in:%d rbuf:%d ms12:%d submix in:%d cap:%d", whole_path_ltcy, alsa_in_ltcy, rbuf_ltcy, ms12_ltcy, submix_in_ltcy, cap_ltcy); if (latency_details) { sprintf(latency_details, "alsa in:%d rbuf:%d ms12:%d alsa out:%d " "speak tuning rbuf:%d", alsa_in_ltcy, rbuf_ltcy, ms12_ltcy, alsa_output_latency, spk_tuning_ltcy); } patch->audio_latency.total_latency = whole_path_ltcy; return whole_path_ltcy; } int aml_dev_sample_video_path_latency(struct aml_audio_patch *patch) { struct aml_audio_device *aml_dev; int vltcy = 0; aml_dev = (struct aml_audio_device *)patch->dev; patch->max_video_latency = get_tvin_max_delay(&aml_dev->alsa_mixer); if (patch->max_video_latency <= 0) { return -1; } patch->min_video_latency = get_tvin_min_delay(&aml_dev->alsa_mixer); if (patch->min_video_latency <= 0) { return -1; } vltcy = get_tvin_delay(&aml_dev->alsa_mixer); return vltcy; } static inline int calc_diff(int altcy, int vltcy) { return altcy - vltcy; } int aml_dev_avsync_diff_in_path(struct aml_audio_patch *patch, int *Vltcy, int *Altcy, char *latency_details) { int altcy = 0, vltcy = 0; int src_diff_err = 0; int ret = 0; if (!patch || !Vltcy || !Altcy) { ret = -EINVAL; goto err; } check_skip_frames(patch); vltcy = aml_dev_sample_video_path_latency(patch); if (vltcy < 0) { ret = -EINVAL; goto err; } *Vltcy = vltcy; if (patch->input_device == AUDIO_DEVICE_IN_LINE) { src_diff_err = 30; } altcy = aml_dev_sample_audio_path_latency(patch, latency_details); *Altcy = altcy + src_diff_err; // FIXME: should be careful of overflow of latency_details int len = strlen(latency_details); sprintf(&latency_details[len], " video:%d", vltcy); ALOGV(" altcy: %dms, vltcy: %dms\n\n", altcy, vltcy); return 0; err: return ret; } static inline void aml_dev_accumulate_avsync_diff(struct aml_audio_patch *patch, int vltcy, int altcy) { patch->vltcy += vltcy; patch->altcy += altcy; patch->avsync_sample_accumulated++; patch->average_vltcy = patch->vltcy / patch->avsync_sample_accumulated; patch->average_altcy = patch->altcy / patch->avsync_sample_accumulated; AM_LOGI("latency status[%d]: average average_vltcy = %dms, average_altcy = %dms", patch->avsync_sample_accumulated, patch->average_vltcy, patch->average_altcy); } static int aml_dev_tune_video_path_latency(struct aml_mixer_handle *mixer_handle, unsigned int video_val) { int ret = 0; ret = aml_mixer_ctrl_set_int(mixer_handle, AML_MIXER_ID_TVIN_VIDEO_DELAY, video_val); return ret; } static inline void aml_dev_avsync_reset(struct aml_audio_patch *patch) { patch->avsync_sample_accumulated = 0; patch->vltcy = 0; patch->average_vltcy = 0; audio_patch_set_av_sync_status(patch, false); patch->is_avsync_start = false; patch->timeout_avsync_cnt = 0; patch->altcy = 0; patch->average_altcy = 0; patch->max_video_latency = 0; patch->min_video_latency = 0; } int aml_dev_try_avsync(struct aml_audio_patch *patch) { int vltcy = 0, altcy = 0; struct aml_audio_device *aml_dev; int ret = 0; if (!patch) { return -EINVAL; } aml_dev = (struct aml_audio_device *)patch->dev; char latency_details[256] = {0}; ret = aml_dev_avsync_diff_in_path(patch, &vltcy, &altcy, latency_details); if (ret < 0) { /* timeout to wait video stability, don't do avsync */ patch->timeout_avsync_cnt++; usleep(10*1000); if (patch->timeout_avsync_cnt > AVSYNC_TIMEOUT_CNT) { aml_dev_avsync_reset(patch); ALOGI(" timeout to tune avsync! error status = %d", ret); } return ret; } if (patch->is_avsync_start == false) { patch->is_avsync_start = true; /*flush the alsa input buffer*/ audio_input_flush(patch->input_type); ALOGV(" /* start calc the average latency of audio & video */"); } aml_dev_accumulate_avsync_diff(patch, vltcy, altcy); int tune_val = patch->average_altcy; int user_tune_val = 0; //aml_audio_get_src_tune_latency(aml_dev->patch_src); int seek_duration_ret = 0; int seek_duration = tune_val; int avDiff = 0; //int sm_tune_val = 0; if (patch->avsync_sample_accumulated >= AVSYNC_SAMPLE_MAX_CNT) { tune_val += user_tune_val; avDiff = calc_diff(tune_val, patch->min_video_latency); ALOGD("--vmax latency = [%dms], vmin latency = [%dms], vltcy = [%dms], altcy = [%dms], altcy - vmin = [%dms]", patch->max_video_latency, patch->min_video_latency, patch->average_vltcy, tune_val, avDiff); /* if min video latency is larger than audio latency, seek audio buffer to enlarge the audio delay */ if (avDiff < 0) { seek_duration_ret = ringbuffer_seek(patch, avDiff); } else if (patch->audio_latency.ringbuffer_latency > AVSYNC_RINGBUFFER_MIN_LATENCY) { /* if it need reduce audio latency, first do ringbuffer seek */ int valid_tune_space = patch->audio_latency.ringbuffer_latency - AVSYNC_RINGBUFFER_MIN_LATENCY; seek_duration = (avDiff < valid_tune_space) ? avDiff : valid_tune_space; seek_duration_ret = ringbuffer_seek(patch, seek_duration); } tune_val -= seek_duration_ret; #if 0 /* for submix ringbuffer tuning */ if (aml_dev->useSubMix) { sm_tune_val = avDiff - seek_duration_ret; if (sm_tune_val < 0) { seek_duration_ret = submix_ringbuffer_seek(patch, sm_tune_val); } else if (patch->audio_latency.smringbuffer_latency > AVSYNC_RINGBUFFER_MIN_LATENCY) { /* if it need reduce audio latency, first do submix ringbuffer seek*/ int valid_tune_space = patch->audio_latency.smringbuffer_latency - AVSYNC_RINGBUFFER_MIN_LATENCY; seek_duration = (sm_tune_val < valid_tune_space) ? sm_tune_val : valid_tune_space; seek_duration_ret = submix_ringbuffer_seek(patch, seek_duration); AM_LOGD("seek_duration_ret:%d, tune_val:%d", seek_duration_ret, tune_val); } tune_val -= seek_duration_ret; } #endif vltcy = aml_dev_sample_video_path_latency(patch); altcy = aml_dev_sample_audio_path_latency(patch, latency_details); if (tune_val > patch->max_video_latency) { tune_val = patch->max_video_latency; } else if (tune_val < patch->min_video_latency) { tune_val = patch->min_video_latency; } ret = aml_dev_tune_video_path_latency(&aml_dev->alsa_mixer, tune_val); ALOGD("--start avsync, tuning video total latency: value [%dms], real vltcy [%dms], real altcy [%dms]", tune_val, vltcy, altcy); aml_dev_avsync_reset(patch); } return 0; }