From e38bd3eb862bc738a82183fb5195512a05e2cbe6 Mon Sep 17 00:00:00 2001 From: Jian Xu Date: Fri, 10 Mar 2017 18:52:24 +0000 Subject: Merge "PD#140145: fix A/V sync problem after short seek" into n-amlogic --- diff --git a/audio_hw.c b/audio_hw.c index 3bcfce6..4ec165c 100644 --- a/audio_hw.c +++ b/audio_hw.c @@ -1594,7 +1594,7 @@ if (!(adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) { // here we use acutal audio frame gap,not use the differece of caculated current apts with the current frame pts, //as there is a offset of audio latency from alsa. // handle audio gap 0.5~5 s - uint64_t two_frame_gap = pts_abs(hw_sync->last_apts_from_header, pts); + uint64_t two_frame_gap = get_pts_gap(hw_sync->last_apts_from_header, pts); if (two_frame_gap > APTS_DISCONTINUE_THRESHOLD_MIN && two_frame_gap < APTS_DISCONTINUE_THRESHOLD_MAX) { /* if (abs(pts -apts) > APTS_DISCONTINUE_THRESHOLD_MIN && abs(pts -apts) < APTS_DISCONTINUE_THRESHOLD_MAX) { */ ALOGI("HW sync PTS discontinue, 0x%"PRIx64"->0x%"PRIx64"(from header) diff %"PRIx64",last apts %"PRIx64"(from header)", @@ -1669,10 +1669,12 @@ if (!(adev->out_device & AUDIO_DEVICE_OUT_ALL_SCO)) { } #endif } else { - int pcr = 0; - if (get_sysfs_int16(TSYNC_PCRSCR, &pcr) == 0) { + uint pcr = 0; + if (get_sysfs_uint(TSYNC_PCRSCR, &pcr) == 0) { + uint apts_gap = 0; int32_t apts_cal = apts & 0xffffffff; - if (abs(pcr - apts_cal) < SYSTIME_CORRECTION_THRESHOLD) { + apts_gap = get_pts_gap(pcr, apts); + if (apts_gap < SYSTIME_CORRECTION_THRESHOLD) { // do nothing } else { sprintf(buf, "0x%x", apts_cal); @@ -2038,6 +2040,55 @@ exit: return bytes; } +// insert bytes of zero data to pcm which makes A/V synchronization +static int insert_output_bytes(struct aml_stream_out *out, size_t size) +{ + int ret = 0; + size_t insert_size = size; + size_t once_write_size = 0; + char *insert_buf = (char*)malloc(8192); + + if (insert_buf == NULL) { + ALOGE("malloc size failed \n"); + return -ENOMEM; + } + + memset(insert_buf, 0, 8192); + while (insert_size > 0) { + once_write_size = insert_size > 8192 ? 8192 : insert_size; + ret = pcm_write(out->pcm, (void *)insert_buf, once_write_size); + if (ret != 0) { + ALOGE("pcm write failed\n"); + goto exit; + } + insert_size -= once_write_size; + } + +exit: + free(insert_buf); + return 0; +} + +enum hwsync_status { + CONTINUATION, // good sync condition + ADJUSTMENT, // can be adjusted by discarding or padding data + RESYNC, // pts need resync +}; + +enum hwsync_status check_hwsync_status(uint apts_gap) +{ + enum hwsync_status sync_status; + + if (apts_gap < APTS_DISCONTINUE_THRESHOLD_MIN) + sync_status = CONTINUATION; + else if (apts_gap > APTS_DISCONTINUE_THRESHOLD_MAX) + sync_status = RESYNC; + else + sync_status = ADJUSTMENT; + + return sync_status; +} + static ssize_t out_write_direct(struct audio_stream_out *stream, const void* buffer, size_t bytes) { @@ -2082,7 +2133,7 @@ static ssize_t out_write_direct(struct audio_stream_out *stream, const void* buf out->spdif_enc_init_frame_write_sum = out->frame_write_sum; } // todo: check timestamp header PTS discontinue for new sync point after seek - aml_audio_hwsync_clear_status(out); + aml_audio_hwsync_init(&out->hwsync); out->spdif_enc_init_frame_write_sum = out->frame_write_sum; } if (out->standby) { @@ -2105,7 +2156,10 @@ static ssize_t out_write_direct(struct audio_stream_out *stream, const void* buf int outsize = 0; char tempbuf[128]; ALOGV("before aml_audio_hwsync_find_frame bytes %zu\n", bytes); - hwsync_cost_bytes = aml_audio_hwsync_find_frame(out, buffer, bytes, &cur_pts, &outsize); + hwsync_cost_bytes = aml_audio_hwsync_find_frame(&out->hwsync, buffer, bytes, &cur_pts, &outsize); + if (cur_pts > 0xffffffff) { + ALOGE("APTS exeed the max 32bit value"); + } ALOGV("after aml_audio_hwsync_find_frame bytes remain %zu,cost %zu,outsize %d,pts %"PRIx64"\n", bytes - hwsync_cost_bytes, hwsync_cost_bytes, outsize, cur_pts); //TODO,skip 3 frames after flush, to tmp fix seek pts discontinue issue.need dig more @@ -2122,89 +2176,63 @@ static ssize_t out_write_direct(struct audio_stream_out *stream, const void* buf //we take this frame pts as the first apts. //this can fix the seek discontinue,we got a fake frame,which maybe cached before the seek if (hw_sync->first_apts_flag == false) { - hw_sync->first_apts_flag = true; - hw_sync->first_apts = cur_pts; - sprintf(tempbuf, "AUDIO_START:0x%"PRIx64"", cur_pts & 0xffffffff); - ALOGI("tsync -> %s,frame size %d", tempbuf, outsize); - if (sysfs_set_sysfs_str(TSYNC_EVENT, tempbuf) == -1) { - ALOGE("set AUDIO_START failed \n"); - } + aml_audio_hwsync_set_first_pts(&out->hwsync, cur_pts); } else { uint64_t apts; + uint32_t apts32; + uint pcr = 0; + uint apts_gap = 0; uint64_t latency = out_get_latency(stream) * 90; // check PTS discontinue, which may happen when audio track switching // discontinue means PTS calculated based on first_apts and frame_write_sum // does not match the timestamp of next audio samples - if (cur_pts > latency) { + if (cur_pts > latency) { apts = cur_pts - latency; } else { apts = 0; } - if (0) { //abs(cur_pts -apts) > APTS_DISCONTINUE_THRESHOLD) { - ALOGI("HW sync PTS discontinue, 0x%"PRIx64"->0x%"PRIx64"(from header) diff %"PRIx64",last apts %"PRIx64"(from header)", - apts, cur_pts, pts_abs(cur_pts, apts), hw_sync->last_apts_from_header); - hw_sync->first_apts = cur_pts; - sprintf(tempbuf, "AUDIO_TSTAMP_DISCONTINUITY:0x%"PRIx64"", cur_pts); - if (sysfs_set_sysfs_str(TSYNC_EVENT, tempbuf) == -1) { - ALOGE("unable to open file %s,err: %s", TSYNC_EVENT, strerror(errno)); - } - } else { - int pcr = 0; - if (get_sysfs_int16(TSYNC_PCRSCR, &pcr) == 0) { - uint32_t apts_cal = apts & 0xffffffff; - if (pts_abs(pcr, apts) < SYSTIME_CORRECTION_THRESHOLD) { - // do nothing - } - // limit the gap handle to 0.5~5 s. - else if ((apts - pcr) > APTS_DISCONTINUE_THRESHOLD_MIN && (apts - pcr) < APTS_DISCONTINUE_THRESHOLD_MAX) { + + apts32 = apts & 0xffffffff; + + if (get_sysfs_uint(TSYNC_PCRSCR, &pcr) == 0) { + enum hwsync_status sync_status = CONTINUATION; + apts_gap = get_pts_gap(pcr, apts32); + sync_status = check_hwsync_status(apts_gap); + + // limit the gap handle to 0.5~5 s. + if (sync_status == ADJUSTMENT) { + // two cases: apts leading or pcr leading + // apts leading needs inserting frame and pcr leading neads discarding frame + if (apts32 > pcr) { int insert_size = 0; - int once_write_size = 0; if (out->codec_type == TYPE_EAC3) { - insert_size = pts_abs(apts, pcr) / 90 * 48 * 4 * 4; + insert_size = apts_gap / 90 * 48 * 4 * 4; } else { - insert_size = pts_abs(apts, pcr) / 90 * 48 * 4; + insert_size = apts_gap / 90 * 48 * 4; } insert_size = insert_size & (~63); - ALOGI("audio gap %"PRIx64" ms ,need insert data %d\n", pts_abs(apts, pcr) / 90, insert_size); - char *insert_buf = (char*)malloc(8192); - if (insert_buf == NULL) { - ALOGE("malloc size failed \n"); - pthread_mutex_unlock(&adev->lock); - goto exit; - } - memset(insert_buf, 0, 8192); - while (insert_size > 0) { - once_write_size = insert_size > 8192 ? 8192 : insert_size; - ret = pcm_write(out->pcm, (void *) insert_buf, once_write_size); - if (ret != 0) { - ALOGE("pcm write failed\n"); - free(insert_buf); - pthread_mutex_unlock(&adev->lock); - goto exit; - } - insert_size -= once_write_size; - } - free(insert_buf); - } - //audio pts smaller than pcr,need skip frame. - else if ((pcr - apts) > APTS_DISCONTINUE_THRESHOLD_MIN && (pcr - apts) < APTS_DISCONTINUE_THRESHOLD_MAX) { + ALOGI("audio gap 0x%"PRIx32" ms ,need insert data %d\n", apts_gap / 90, insert_size); + ret = insert_output_bytes(out, insert_size); + } else { + //audio pts smaller than pcr,need skip frame. //we assume one frame duration is 32 ms for DD+(6 blocks X 1536 frames,48K sample rate) if (out->codec_type == TYPE_EAC3 && outsize > 0) { - ALOGI("audio slow 0x%"PRIx64",skip frame @pts 0x%"PRIx64",pcr 0x%x,cur apts 0x%"PRIx64"\n", (pcr - apts), cur_pts, pcr, apts); + ALOGI("audio slow 0x%x,skip frame @pts 0x%"PRIx64",pcr 0x%x,cur apts 0x%x\n", + apts_gap, cur_pts, pcr, apts32); out->frame_skip_sum += 1536; - bytes = outsize; + bytes = outsize; pthread_mutex_unlock(&adev->lock); goto exit; } - } else { - sprintf(tempbuf, "0x%"PRIx64"", apts); - ALOGI("tsync -> reset pcrscr 0x%d -> 0x%"PRIx64", %s big,diff %"PRIx64" ms", pcr, apts, apts > (uint64_t)pcr ? "apts" : "pcr", pts_abs(apts, pcr) / 90); -#if 0 - int ret_val = sysfs_set_sysfs_str(TSYNC_APTS, tempbuf); - if (ret_val == -1) { - ALOGE("unable to open file %s,err: %s", TSYNC_APTS, strerror(errno)); - } -#endif + } + } else if (sync_status == RESYNC){ + sprintf(tempbuf, "0x%x", apts32); + ALOGI("tsync -> reset pcrscr 0x%x -> 0x%x, %s big,diff %"PRIx64" ms", + pcr, apts32, apts32 > pcr ? "apts" : "pcr", get_pts_gap(apts, pcr) / 90); + + int ret_val = sysfs_set_sysfs_str(TSYNC_APTS, tempbuf); + if (ret_val == -1) { + ALOGE("unable to open file %s,err: %s", TSYNC_APTS, strerror(errno)); } } } @@ -3237,7 +3265,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->standby = true; out->frame_write_sum = 0; out->hw_sync_mode = false; - out->hwsync.first_apts_flag = false; + aml_audio_hwsync_init(&out->hwsync); //out->hal_rate = out->config.rate; if (0/*flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC*/) { out->hw_sync_mode = true; diff --git a/audio_hw_utils.c b/audio_hw_utils.c index 5dc53ca..39441af 100644 --- a/audio_hw_utils.c +++ b/audio_hw_utils.c @@ -30,11 +30,11 @@ #else #define LOGFUNC(...) (ALOGD(__VA_ARGS__)) #endif -int get_sysfs_int16(const char *path, int *value) +int get_sysfs_uint(const char *path, uint *value) { int fd; char valstr[64]; - int val = 0; + uint val = 0; fd = open(path, O_RDONLY); if (fd >= 0) { memset(valstr, 0, 64); diff --git a/audio_hw_utils.h b/audio_hw_utils.h index 9bcdc9c..2a2a836 100644 --- a/audio_hw_utils.h +++ b/audio_hw_utils.h @@ -1,6 +1,6 @@ #ifndef _AUDIO_HW_UTILS_H_ #define _AUDIO_HW_UTILS_H_ -int get_sysfs_int16(const char *path, int *value); +int get_sysfs_uint(const char *path, uint *value); int sysfs_set_sysfs_str(const char *path, const char *val); int get_sysfs_int (const char *path); int mystrstr(char *mystr,char *substr) ; diff --git a/audio_hwsync.c b/audio_hwsync.c index 1116fab..2fd1e89 100644 --- a/audio_hwsync.c +++ b/audio_hwsync.c @@ -1,63 +1,34 @@ #define LOG_TAG "audio_hwsync" -#include -#include + #include #include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include "audio_hwsync.h" #include "audio_hw_utils.h" -#include "audio_hw.h" +#include "audio_hwsync.h" -static uint32_t aml_hwsync_out_get_latency(const struct audio_stream_out *stream) +void aml_audio_hwsync_init(audio_hwsync_t *p_hwsync) { - struct aml_stream_out *out = (struct aml_stream_out *)stream; - uint32_t whole_latency; - int ret; - snd_pcm_sframes_t frames = 0; - whole_latency = (out->config.period_size * out->config.period_count * 1000) / out->config.rate; - if (!out->pcm || !pcm_is_ready(out->pcm)) { - return whole_latency; - } - ret = pcm_ioctl(out->pcm, SNDRV_PCM_IOCTL_DELAY, &frames); - if (ret < 0) { - return whole_latency; - } - if (out->hal_format == AUDIO_FORMAT_E_AC3) { - frames /= 4; - } - return (uint32_t)((frames * 1000) / out->config.rate); -} + if (p_hwsync == NULL) + return; -void aml_audio_hwsync_clear_status(struct aml_stream_out *out) -{ - audio_hwsync_t *p_hwsync = &out->hwsync; - p_hwsync->first_apts_flag = 0; + p_hwsync->first_apts_flag = false; p_hwsync->hw_sync_state = HW_SYNC_STATE_HEADER; p_hwsync->hw_sync_header_cnt = 0; return; } //return bytes cost from input, -int aml_audio_hwsync_find_frame(struct aml_stream_out *out, const void *in_buffer, size_t in_bytes, uint64_t *cur_pts, int *outsize) +int aml_audio_hwsync_find_frame(audio_hwsync_t *p_hwsync, + 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; - audio_hwsync_t *p_hwsync = &out->hwsync; uint64_t time_diff = 0; + + if (p_hwsync == NULL || in_buffer == NULL) + return 0; + //ALOGI(" --- out_write %d, cache cnt = %d, body = %d, hw_sync_state = %d", out_frames * frame_size, out->body_align_cnt, out->hw_sync_body_cnt, out->hw_sync_state); while (remain > 0) { //if (p_hwsync->hw_sync_state == HW_SYNC_STATE_RESYNC) { @@ -85,7 +56,7 @@ int aml_audio_hwsync_find_frame(struct aml_stream_out *out, const void *in_buff //memcpy(write_buf+write_pos,&p_hwsync->hw_sync_header[0],16); //write_pos += 16; pts = pts * 90 / 1000000; - time_diff = pts_abs(pts, p_hwsync->last_apts_from_header) / 90; + time_diff = get_pts_gap(pts, p_hwsync->last_apts_from_header) / 90; ALOGV("pts %"PRIx64",frame len %zu\n", pts, p_hwsync->hw_sync_body_cnt); ALOGV("last pts %"PRIx64",diff %"PRIx64" ms\n", p_hwsync->last_apts_from_header, time_diff); @@ -121,3 +92,29 @@ int aml_audio_hwsync_find_frame(struct aml_stream_out *out, const void *in_buff } return in_bytes - remain; } + +int aml_audio_hwsync_set_first_pts(audio_hwsync_t *p_hwsync, uint64_t pts) +{ + uint32_t pts32; + char tempbuf[128]; + + if (p_hwsync == NULL) + return -1; + + if (pts > 0xffffffff) { + ALOGE("APTS exeed the 32bit range!"); + return -1; + } + + pts32 = (uint32_t)pts; + p_hwsync->first_apts_flag = true; + p_hwsync->first_apts = pts; + sprintf(tempbuf, "AUDIO_START:0x%x", pts32); + ALOGI("hwsync set tsync -> %s", tempbuf); + if (sysfs_set_sysfs_str(TSYNC_EVENT, tempbuf) == -1) { + ALOGE("set AUDIO_START failed \n"); + return -1; + } + + return 0; +} diff --git a/audio_hwsync.h b/audio_hwsync.h index 99bf8ae..9330c6b 100644 --- a/audio_hwsync.h +++ b/audio_hwsync.h @@ -1,6 +1,8 @@ #ifndef _AUDIO_HWSYNC_H_ #define _AUDIO_HWSYNC_H_ +#include + #define TSYNC_FIRSTAPTS "/sys/class/tsync/firstapts" #define TSYNC_PCRSCR "/sys/class/tsync/pts_pcrscr" #define TSYNC_EVENT "/sys/class/tsync/event" @@ -55,7 +57,7 @@ static inline uint32_t hwsync_header_get_size(uint8_t *header) ((uint32_t)header[7]); } -static inline uint64_t pts_abs(uint64_t a, uint64_t b) +static inline uint64_t get_pts_gap(uint64_t a, uint64_t b) { if (a >= b) return (a - b); @@ -63,9 +65,8 @@ static inline uint64_t pts_abs(uint64_t a, uint64_t b) return (b - a); } -struct aml_stream_out; - -void aml_audio_hwsync_clear_status(struct aml_stream_out *out); -int aml_audio_hwsync_find_frame(struct aml_stream_out *out, +void aml_audio_hwsync_init(audio_hwsync_t *p_hwsync); +int aml_audio_hwsync_find_frame(audio_hwsync_t *p_hwsync, const void *in_buffer, size_t in_bytes, uint64_t *cur_pts, int *outsize); +int aml_audio_hwsync_set_first_pts(audio_hwsync_t *p_hwsync, uint64_t pts); #endif -- cgit