audio: add injection and capture for roku [1/1]

PD#SWPL-187494

Problem:
new feature

Solution:
add capture output and package into a module

Verify:
yocto

Change-Id: I7eaa3cdc394b5c77495d69d04f5bfd2e09fb0311
Signed-off-by: wei.du <wei.du@amlogic.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a63acc9..09839b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -201,6 +201,7 @@
 	audio_hal/hal_clipmeta.c
 	audio_hal/aml_audio_scaletempo.c
 	audio_hal/karaoke_manager.c
+	audio_hal/aml_capture_output.c
 	utils/cJSON/cJSON.c
 	utils/aml_hw_mixer.c
 	utils/alsa_device_parser.c
diff --git a/audio_hal/amlAudioMixer.c b/audio_hal/amlAudioMixer.c
index e67d71f..2d8831d 100644
--- a/audio_hal/amlAudioMixer.c
+++ b/audio_hal/amlAudioMixer.c
@@ -484,59 +484,13 @@
         int count = 3;
 
         while (out_port->bytes_avail > 0) {
-            // out_write_callbacks();
-            if (adev->active_outport == OUTPORT_A2DP) {
-                if (out_port->cfg.channelCnt == 1) {
-                    in_data_config.channel_mask = AUDIO_CHANNEL_OUT_MONO;
-                } else if (out_port->cfg.channelCnt == 2) {
-                    in_data_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-                } else {
-                    AM_LOGW("not supported channel:%d", out_port->cfg.channelCnt);
-                    release_cur_outport_lock(audio_mixer, port_index);
-                    return out_port->bytes_avail;
-                }
-                in_data_config.sample_rate = out_port->cfg.sampleRate;
-                in_data_config.format = out_port->cfg.format;
-#if 0
-                if (is_TV(adev)) {
-                    float volume = aml_audio_get_s_gain_by_src(adev, get_dev_patch_src(adev));
-
-                    volume *= adev->sink_gain[OUTPORT_A2DP];
-                    apply_volume(volume, out_port->data_buf, sizeof(uint16_t),
-                        out_port->bytes_avail);
-                }
-
-                int ret = a2dp_out_write(adev, &in_data_config, out_port->data_buf, out_port->bytes_avail);
-                if (ret == 0 && count-- > 0) {
-                     continue;
-                }
-#endif
-            } else if (is_sco_port(adev->active_outport)) {
-                if (out_port->cfg.channelCnt == 1) {
-                    in_data_config.channel_mask = AUDIO_CHANNEL_OUT_MONO;
-                } else if (out_port->cfg.channelCnt == 2) {
-                    in_data_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-                } else {
-                    AM_LOGW("not supported channel:%d", out_port->cfg.channelCnt);
-                    release_cur_outport_lock(audio_mixer, port_index);
-                    return out_port->bytes_avail;
-                }
-                in_data_config.sample_rate = out_port->cfg.sampleRate;
-                in_data_config.format = out_port->cfg.format;
-                if (is_TV(adev))
-                    apply_volume(adev->sink_gain[OUTPORT_BT_SCO], out_port->data_buf, sizeof(uint16_t),
-                        out_port->bytes_avail);
-
-                write_to_sco(adev, &in_data_config, out_port->data_buf, out_port->bytes_avail);
-            } else {
-                if (audio_mixer->submix_standby) {
-                    pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]);
-                    mixer_output_startup(audio_mixer);
-                    pthread_mutex_lock(&audio_mixer->outport_locks[port_index]);
-                }
-
-                out_port->write(out_port, out_port->data_buf, out_port->bytes_avail);
+            if (audio_mixer->submix_standby) {
+                pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]);
+                mixer_output_startup(audio_mixer);
+                pthread_mutex_lock(&audio_mixer->outport_locks[port_index]);
             }
+
+            out_port->write(out_port, out_port->data_buf, out_port->bytes_avail);
             set_outport_data_avail(out_port, 0);
         };
         release_cur_outport_lock(audio_mixer, port_index);
diff --git a/audio_hal/aml_audio_output.c b/audio_hal/aml_audio_output.c
index 705a28b..b5e022f 100644
--- a/audio_hal/aml_audio_output.c
+++ b/audio_hal/aml_audio_output.c
@@ -32,9 +32,7 @@
 #include "audio_hw_resource_mgr.h"
 #include "audio_hw_ms12_v2.h"
 #include "aml_audio_timer.h"
-#ifndef NO_AUDIO_CAP
-    #include <IpcBuffer/IpcBuffer_c.h>
-#endif
+#include "aml_capture_output.h"
 #include "a2dp_hal.h"
 #include "audio_bt_sco.h"
 #include "dolby_lib_api.h"
@@ -178,11 +176,13 @@
 
     pthread_mutex_lock(&adev->alsa_lock[I2S_DEVICE]);
     alsa_handle = adev->alsa_handle[I2S_DEVICE];
-    if (NULL == alsa_handle || NULL == alsa_status) {
-        AM_LOGE("alsa_handle[I2S_DEVICE] or alsa_status is NULL!");
-        goto error;
-    } else {
-        ret = aml_alsa_output_get_alsa_status(alsa_handle, alsa_status);
+    if (!adev->injection_enable) {
+        if (NULL == alsa_handle || NULL == alsa_status) {
+            AM_LOGE("alsa_handle[I2S_DEVICE] or alsa_status is NULL!");
+            goto error;
+        } else {
+            ret = aml_alsa_output_get_alsa_status(alsa_handle, alsa_status);
+        }
     }
 error:
     pthread_mutex_unlock(&adev->alsa_lock[I2S_DEVICE]);
@@ -217,14 +217,16 @@
 
     pthread_mutex_lock(&adev->alsa_lock[I2S_DEVICE]);
     alsa_handle = adev->alsa_handle[I2S_DEVICE];
-    if (NULL == alsa_handle) {
-        AM_LOGE("alsa_handle[I2S_DEVICE] is NULL!");
-        struct pcm_config config;
-        get_hardware_config_parameters(&config, AUDIO_FORMAT_PCM, adev->default_alsa_ch, 48000, false,
-                    false, is_low_latency_mode(adev));
-        delay_frames = config.start_threshold;
-    } else {
-        delay_frames = aml_alsa_output_get_delay_frame(alsa_handle, ignore_default);
+    if (!adev->injection_enable) {
+        if (NULL == alsa_handle) {
+            AM_LOGE("alsa_handle[I2S_DEVICE] is NULL!");
+            struct pcm_config config;
+            get_hardware_config_parameters(&config, AUDIO_FORMAT_PCM, adev->default_alsa_ch, 48000, false,
+                        false, is_low_latency_mode(adev));
+            delay_frames = config.start_threshold;
+        } else {
+            delay_frames = aml_alsa_output_get_delay_frame(alsa_handle, ignore_default);
+        }
     }
 error:
     pthread_mutex_unlock(&adev->alsa_lock[I2S_DEVICE]);
@@ -396,11 +398,11 @@
 
 #ifndef NO_AUDIO_CAP
         /* 2ch downmix capture for nonetv platform*/
-        pthread_mutex_lock(&adev->cap_buffer_lock);
-        if (adev->cap_buffer) {
-            IpcBuffer_write(adev->cap_buffer, (const unsigned char *)buffer, (int) bytes);
+        pthread_mutex_lock(&adev->cap_handle_lock);
+        if (adev->capture_handle) {
+            aml_audio_capture_out_write(adev->capture_handle, buffer, bytes);
         }
-        pthread_mutex_unlock(&adev->cap_buffer_lock);
+        pthread_mutex_unlock(&adev->cap_handle_lock);
 #endif
 
         if (is_TV(adev)) {
@@ -616,29 +618,35 @@
             buffer, bytes, output_format);
     }
 
-    if (!adev->alsa_handle[I2S_DEVICE] && !adev->injection_enable) {
-        AM_LOGI("pcm is null, open device");
-        if (-1 == aml_audio_pcm_out_open(adev)) {
-            AM_LOGE("aml_audio_pcm_out_open fail, hw write fail");
-            return -1;
+    if (!adev->injection_enable) {
+        if (!adev->alsa_handle[I2S_DEVICE]) {
+            AM_LOGI("pcm is null, open device");
+            if (-1 == aml_audio_pcm_out_open(adev)) {
+                AM_LOGE("aml_audio_pcm_out_open fail, hw write fail");
+                return -1;
+            }
+        }
+
+        if (adev->alsa_handle[I2S_DEVICE] || adev->a2dp_hal || is_sco_port(adev->active_outport)) {
+//#ifdef ENABLE_BT_A2DP
+#ifndef BUILD_LINUX
+            if (adev->active_outport == OUTPORT_A2DP) {
+                ret = a2dp_out_write(adev, &in_data_config, buffer, bytes);
+            } else
+#endif
+            if (is_sco_port(adev->active_outport)) {
+                ret = write_to_sco(adev, &in_data_config, buffer, bytes);
+            } else {
+                ret = aml_audio_pcm_out_write(adev, buffer, bytes);
+            }
+            if (ret < 0) {
+                AM_LOGE("ALSA out write fail");
+            }
         }
     }
-
-    if (adev->alsa_handle[I2S_DEVICE] || adev->a2dp_hal || is_sco_port(adev->active_outport)) {
-        //#ifdef ENABLE_BT_A2DP
-        #ifndef BUILD_LINUX
-        if (adev->active_outport == OUTPORT_A2DP) {
-            ret = a2dp_out_write(adev, &in_data_config, buffer, bytes);
-        } else
-        #endif
-        if (is_sco_port(adev->active_outport)) {
-            ret = write_to_sco(adev, &in_data_config, buffer, bytes);
-        } else {
-            ret = aml_audio_pcm_out_write(adev, buffer, bytes);
-        }
-        if (ret < 0) {
-            AM_LOGE("ALSA out write fail");
-        }
+    else if (!adev->capture_handle){
+        AM_LOGE("injection should enable together with capture, if not, sleep 5ms to avoid ms12 overrun!!");
+        aml_audio_sleep(5*1000);
     }
 
     if (adev->debug_flag > 1) {
diff --git a/audio_hal/aml_capture_output.c b/audio_hal/aml_capture_output.c
new file mode 100644
index 0000000..ae79015
--- /dev/null
+++ b/audio_hal/aml_capture_output.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 224 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_capture_output"
+//#define LOG_NDEBUG 0
+
+
+#include <system/audio.h>
+#include <hardware/audio.h>
+#include <sys/timerfd.h>
+#ifndef NO_AUDIO_CAP
+#include <IpcBuffer/IpcBuffer_c.h>
+#endif
+#include "aml_audio_log.h"
+#include "audio_hw.h"
+#include "aml_capture_output.h"
+#include "aml_malloc_debug.h"
+#include "audio_hw_utils.h"
+#include "aml_dump_debug.h"
+#include "aml_audio_timer.h"
+
+#define IPC_BUFFER_CREATE_MAX_RETRY_ATTEMPTS        (10)
+#define IPC_BUFFER_CREATE_RETRY_SLEEP_MS            (5)
+#define CAPTURE_TIMER_INTERVAL_MS                   (10)
+#define CAPTURE_BUFFER_MIN_SIZE                     (4*48*32)
+
+typedef enum capture_location {
+    CAP_MIXER_OUTPUT = 0,
+    CAP_MIXER_INPUT,
+    CAP_LOCATION_MAX
+} capture_location_e;
+
+typedef struct aml_capture_handle {
+    void *cap_buffer;
+    enum capture_location cap_location;
+    int timer_fd;
+    pthread_t cap_process_threadID;
+    pthread_mutex_t cap_lock;
+    bool cap_process_exit;
+    bool is_cap_init;
+    bool cap_mute;
+    bool cap_start_detect;
+    ring_buffer_t cap_ringbuffer;
+    void *cap_ipcbuffer;
+    int cap_mm_delay;
+    int cap_dtv_delay;
+    int buffer_size;
+    void *cap_interleave_buffer;
+    void *cap_stereo_buffer;
+} aml_capture_handle_t;
+
+static int aml_start_timer_fd(int fd, uint32_t interval_ms)
+{
+    struct itimerspec i_timer_spec;
+    uint64_t interval_ns = (uint64_t)interval_ms*(uint64_t)1000000;
+
+    i_timer_spec.it_value.tv_sec = interval_ns/1000000000;
+    i_timer_spec.it_value.tv_nsec = interval_ns%1000000000;
+    i_timer_spec.it_interval.tv_sec = i_timer_spec.it_value.tv_sec;
+    i_timer_spec.it_interval.tv_nsec = i_timer_spec.it_value.tv_nsec;
+
+    return timerfd_settime(fd, 0, &i_timer_spec, NULL);
+}
+
+void *aml_audio_capture_process_threadloop(void *data)
+{
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)data;
+    struct timespec ts;
+    int avail_bytes = 0;
+    size_t read_bytes = 0;
+    char *read_buffer = NULL;
+    int expect_read_bytes = CAPTURE_TIMER_INTERVAL_MS * 48 * 4;
+    bool is_start_play = false;
+    unsigned int count = 0;
+    uint64_t events = 0;
+    ssize_t read_ret = 0;
+
+    AM_LOGI("in");
+    read_buffer = aml_audio_calloc(1, expect_read_bytes);
+    if (!read_buffer) {
+        AM_LOGE("buffer calloc failed");
+        return NULL;
+    }
+
+    aml_set_thread_priority("audio capture thread", p_capture->cap_process_threadID, 30);
+
+    while (!p_capture->cap_process_exit) {
+        read_ret = read(p_capture->timer_fd, &events, sizeof(events));
+        while (events > 0) {
+            if (p_capture->is_cap_init) {
+                avail_bytes = get_buffer_read_space(&p_capture->cap_ringbuffer);
+                if (avail_bytes >= expect_read_bytes * 3 && !is_start_play) {
+                    count = 0;
+                    is_start_play = true;
+                    AM_LOGI("Cap start play, threshold:%d ms", CAPTURE_TIMER_INTERVAL_MS * 3);
+                }
+
+                if (is_start_play) {
+                    if (avail_bytes > expect_read_bytes) {
+                        count = 0;
+                        read_bytes = ring_buffer_read(&p_capture->cap_ringbuffer, read_buffer, expect_read_bytes);
+                    } else {
+                        if (++count >= 3) {
+                            is_start_play = false;
+                        }
+                        read_bytes = expect_read_bytes;
+                        memset(read_buffer, 0, read_bytes);
+                        AM_LOGI("Cap ringbuffer underrun, conut:%d", count);
+                    }
+                } else {
+                    read_bytes = expect_read_bytes;
+                    memset(read_buffer, 0, read_bytes);
+                }
+
+                if (aml_audio_property_get_bool("vendor.media.audiohal.mixer.capture", 0)) {
+                    aml_dump_audio_bitstreams("capture.pcm", read_buffer, read_bytes);
+                }
+
+                pthread_mutex_lock(&p_capture->cap_lock);
+                IpcBuffer_write(p_capture->cap_ipcbuffer, (const unsigned char *)read_buffer, (int)read_bytes);
+                pthread_mutex_unlock(&p_capture->cap_lock);
+            }
+            --events;
+        }
+    }
+
+    aml_audio_free(read_buffer);
+    AM_LOGI("exit");
+    return NULL;
+}
+
+
+int aml_audio_capture_out_open(void ** phandle, char *name, int size)
+{
+    int ret = 0;
+    int signal_timer_id = -1;
+    int retry_count = 0;
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)aml_audio_calloc(1, sizeof(aml_capture_handle_t));
+    if (p_capture == NULL) {
+        AM_LOGE("malloc aml_capture_handle_t failed\n");
+        return -1;
+    }
+    p_capture->buffer_size = size;
+    if (p_capture->buffer_size < CAPTURE_BUFFER_MIN_SIZE) {
+        AM_LOGI("capture buffer size(%d) is too small!!!", size);
+    }
+    if (!strcmp(name, "cap_mix_output")) {
+        p_capture->cap_location = CAP_MIXER_OUTPUT;
+    } else if (!strcmp(name, "cap_mix_input")) {
+        //to do
+        p_capture->cap_location = CAP_MIXER_INPUT;
+        p_capture->cap_interleave_buffer = (char *)aml_audio_calloc(1, size);
+        if (!p_capture->cap_interleave_buffer) {
+            AM_LOGE("Interleave buffer calloc size %d failed", size);
+            goto calloc_interleave_buffer_error;
+        }
+
+        p_capture->cap_stereo_buffer = (char *)aml_audio_calloc(1, size);
+        if (!p_capture->cap_stereo_buffer) {
+            AM_LOGE("Stereo buffer calloc size %d failed", size);
+            goto calloc_stereo_buffer_error;
+        }
+    } else {
+        AM_LOGE("Unsupported capture location:%s", name);
+        goto capture_location_error;
+    }
+
+    pthread_mutex_lock(&p_capture->cap_lock);
+    while (retry_count < IPC_BUFFER_CREATE_MAX_RETRY_ATTEMPTS) {
+        p_capture->cap_ipcbuffer = IpcBuffer_create(name, size);
+        if (!p_capture->cap_ipcbuffer) {
+            AM_LOGE("Create IPC buffer failed (attempt %d)", retry_count +1);
+            retry_count++;
+            usleep(IPC_BUFFER_CREATE_RETRY_SLEEP_MS * 1000); // Sleep for 5 milliseconds
+        } else {
+            AM_LOGI("Create IPC buffer succeeded, point[%p]:(%s, %d)", p_capture->cap_ipcbuffer, name, size);
+            break; // Successfully created IPC buffer, exit loop
+        }
+    }
+    pthread_mutex_unlock(&p_capture->cap_lock);
+
+    if (retry_count == IPC_BUFFER_CREATE_MAX_RETRY_ATTEMPTS) {
+        AM_LOGE("Create IPC buffer failed after %d attempts", IPC_BUFFER_CREATE_MAX_RETRY_ATTEMPTS);
+        goto calloc_stereo_buffer_error;
+    }
+
+    ret = ring_buffer_init(&p_capture->cap_ringbuffer, size);
+    if (ret != 0) {
+        AM_LOGE("Init ring buffer failed");
+        goto init_ring_buffer_error;
+    }
+
+    p_capture->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+    if (p_capture->timer_fd < 0) {
+        AM_LOGE("Create timerfd failed");
+        goto create_timer_fd_error;
+    }
+
+    ret = aml_start_timer_fd(p_capture->timer_fd, CAPTURE_TIMER_INTERVAL_MS);
+    if (ret < 0) {
+        AM_LOGE("Start timerfd failed");
+        goto start_timer_fd_error;
+    }
+    p_capture->cap_process_exit = false;
+    ret = pthread_create(&p_capture->cap_process_threadID, NULL, &aml_audio_capture_process_threadloop, p_capture);
+    if (ret != 0) {
+        AM_LOGE("Can't create thread: %s", strerror(ret));
+        goto create_pthread_error;
+    }
+
+    p_capture->is_cap_init = true;
+    *phandle = (void *)p_capture;
+
+    return 0;
+
+create_pthread_error:
+start_timer_fd_error:
+    close(p_capture->timer_fd);
+create_timer_fd_error:
+    ring_buffer_release(&p_capture->cap_ringbuffer);
+init_ring_buffer_error:
+    pthread_mutex_lock(&p_capture->cap_lock);
+    if (p_capture->cap_ipcbuffer) {
+        IpcBuffer_destroy(p_capture->cap_ipcbuffer);
+        p_capture->cap_ipcbuffer = NULL;
+    }
+    pthread_mutex_unlock(&p_capture->cap_lock);
+calloc_stereo_buffer_error:
+    if (p_capture->cap_interleave_buffer) {
+        aml_audio_free(p_capture->cap_interleave_buffer);
+        p_capture->cap_interleave_buffer = NULL;
+    }
+calloc_interleave_buffer_error:
+capture_location_error:
+    aml_audio_free(p_capture);
+    return -1;
+
+}
+int aml_audio_capture_out_close(void * handle)
+{
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)handle;
+    int ret = 0;
+
+    p_capture->is_cap_init = false;
+    if (p_capture->cap_location == CAP_MIXER_INPUT) {
+        //to do
+    }
+
+    if (p_capture->cap_interleave_buffer) {
+        aml_audio_free(p_capture->cap_interleave_buffer);
+        p_capture->cap_interleave_buffer = NULL;
+    }
+
+    if (p_capture->cap_stereo_buffer) {
+        aml_audio_free(p_capture->cap_stereo_buffer);
+        p_capture->cap_stereo_buffer = NULL;
+    }
+
+    p_capture->cap_process_exit = true;
+    ret = pthread_join(p_capture->cap_process_threadID, NULL);
+    if (ret != 0) {
+        AM_LOGE("pthread join failed");
+    }
+    close(p_capture->timer_fd);
+
+    pthread_mutex_lock(&p_capture->cap_lock);
+    if (p_capture->cap_ipcbuffer) {
+        IpcBuffer_destroy(p_capture->cap_ipcbuffer);
+        p_capture->cap_ipcbuffer = NULL;
+    }
+    pthread_mutex_unlock(&p_capture->cap_lock);
+
+    ring_buffer_release(&p_capture->cap_ringbuffer);
+
+    aml_audio_free(handle);
+}
+int aml_audio_capture_out_write(void * handle, const void *buffer, size_t bytes)
+{
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)handle;
+    int ret = 0;
+    int retry = false;
+    int avail_bytes = 0;
+    void *audio_cap_buf = (void *)buffer;
+    size_t audio_cap_used_size = bytes;
+
+    if (p_capture->is_cap_init && p_capture->cap_location == CAP_MIXER_OUTPUT) {
+        do {
+            avail_bytes = get_buffer_write_space(&p_capture->cap_ringbuffer);
+            if (avail_bytes >= audio_cap_used_size) {
+                retry = false;
+                ret = ring_buffer_write(&p_capture->cap_ringbuffer, audio_cap_buf, audio_cap_used_size, UNCOVER_WRITE);
+                if (ret != audio_cap_used_size) {
+                    AM_LOGE("Write buffer fails!");
+                }
+            } else if (avail_bytes > 0) {
+                retry = true;
+                ret = ring_buffer_write(&p_capture->cap_ringbuffer, audio_cap_buf, avail_bytes, UNCOVER_WRITE);
+                if (ret != avail_bytes) {
+                    AM_LOGE("Write buffer fails!");
+                }
+                audio_cap_used_size = audio_cap_used_size - avail_bytes;
+                memmove(audio_cap_buf, audio_cap_buf + avail_bytes, audio_cap_used_size);
+            } else {
+                retry = true;
+                aml_audio_sleep(CAPTURE_TIMER_INTERVAL_MS * 1000);
+            }
+        } while (retry && p_capture->is_cap_init);
+    }
+
+    return ret;
+}
+int aml_audio_capture_out_reset(void * handle)
+{
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)handle;
+    if (p_capture->is_cap_init) {
+        AM_LOGI("reset capture buffer");
+        ring_buffer_reset(&p_capture->cap_ringbuffer);
+    }
+}
+
+int aml_audio_capture_out_dump(void * handle, int fd) {
+    aml_capture_handle_t * p_capture = (aml_capture_handle_t *)handle;
+    if (p_capture->is_cap_init) {
+        dprintf(fd, "[AML_HAL] cap_location : %d, timer fd: %d\n", p_capture->cap_location, p_capture->timer_fd);
+        dprintf(fd, "[AML_HAL] mute     : %d\n", p_capture->cap_mute);
+        dprintf(fd, "[AML_HAL] buffer size : %d, avail size : %d\n", p_capture->buffer_size, get_buffer_read_space(&p_capture->cap_ringbuffer));
+    }
+}
diff --git a/audio_hal/aml_capture_output.h b/audio_hal/aml_capture_output.h
new file mode 100644
index 0000000..854d671
--- /dev/null
+++ b/audio_hal/aml_capture_output.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef _AML_CAPTURE_OUTPUT_H_
+#define _AML_CAPTURE_OUTPUT_H_
+
+int aml_audio_capture_out_open(void ** phandle, char *name, int size);
+int aml_audio_capture_out_close(void * handle);
+int aml_audio_capture_out_write(void * handle, const void *buffer, size_t bytes);
+int aml_audio_capture_out_reset(void * handle);
+int aml_audio_capture_out_dump(void * handle, int fd);
+#endif // _AML_CAPTURE_OUTPUT_H_
diff --git a/audio_hal/audio_hw.c b/audio_hal/audio_hw.c
index d6b6a3c..536f6f6 100644
--- a/audio_hal/audio_hw.c
+++ b/audio_hal/audio_hw.c
@@ -58,9 +58,9 @@
 #include <aml_android_utils.h>
 #include <aml_alsa_mixer.h>
 #ifndef NO_AUDIO_CAP
-    #include <IpcBuffer/IpcBuffer_c.h>
+#include <IpcBuffer/IpcBuffer_c.h>
 #endif
-
+#include "aml_capture_output.h"
 #include "tv_patch_format_parser.h"
 #include "SPDIFEncoderAD.h"
 #include "aml_volume_utils.h"
@@ -4762,22 +4762,23 @@
         if (name) {
             size = atoi(name + strlen(name) + 1);
             if (size > 0) {
-                pthread_mutex_lock(&adev->cap_buffer_lock);
-                if (adev->cap_buffer) {
-                    IpcBuffer_destroy(adev->cap_buffer);
+                pthread_mutex_lock(&adev->cap_handle_lock);
+                if (adev->capture_handle) {
+                    aml_audio_capture_out_close(adev->capture_handle);
+                    adev->capture_handle = NULL;
                 }
-                adev->cap_buffer = IpcBuffer_create(name, size);
-                AM_LOGI("IpcBuffer_created %p (%s, %d)", adev->cap_buffer, name, size);
-                pthread_mutex_unlock(&adev->cap_buffer_lock);
+                aml_audio_capture_out_open(&adev->capture_handle, name, size);
+                AM_LOGI("IpcBuffer_created %p (%s, %d)", adev->capture_handle, name, size);
+                pthread_mutex_unlock(&adev->cap_handle_lock);
                 ret = 0;
                 goto exit;
             } else if (size == 0) {
-                pthread_mutex_lock(&adev->cap_buffer_lock);
-                if (adev->cap_buffer) {
-                    IpcBuffer_destroy(adev->cap_buffer);
-                    adev->cap_buffer = NULL;
+                pthread_mutex_lock(&adev->cap_handle_lock);
+                if (adev->capture_handle) {
+                    aml_audio_capture_out_close(adev->capture_handle);
+                    adev->capture_handle = NULL;
                 }
-                pthread_mutex_unlock(&adev->cap_buffer_lock);
+                pthread_mutex_unlock(&adev->cap_handle_lock);
                 ret = 0;
                 goto exit;
             }
@@ -4785,10 +4786,18 @@
         ret = -EINVAL;
         goto exit;
     }
-    ret = str_parms_get_str(parms, "cap_delay", value, sizeof(value));
+
+    ret = str_parms_get_str(parms, "cap_mm_delay", value, sizeof(value));
     if (ret >= 0) {
-        adev->cap_delay = atoi(value);
-        ret = 0;
+        adev->cap_mm_delay = atoi(value);
+        AM_LOGI("cap_delay=%d",adev->cap_mm_delay);
+        goto exit;
+    }
+
+    ret = str_parms_get_str(parms, "cap_dtv_delay", value, sizeof(value));
+    if (ret >= 0) {
+        adev->cap_dtv_delay = atoi(value);
+        AM_LOGI("cap_dtv_delay=%d",adev->cap_dtv_delay);
         goto exit;
     }
 #endif
@@ -5050,10 +5059,23 @@
     ret = str_parms_get_int(parms, "audioinject", &val);
     if (ret >= 0) {
         if (val == 1) {
+            adev->injection_enable = 1;
+            // injection must be used with capture together
+            pthread_mutex_lock(&adev->cap_handle_lock);
+            if (!adev->capture_handle) {
+                AM_LOGE("injection must be used with capture together!!! please check !!!");
+            }
+            pthread_mutex_unlock(&adev->cap_handle_lock);
+            //set PCM output to close spdif handle
+            set_hdmi_format(adev, PCM);
             AM_LOGI("close pcm out");
             aml_audio_pcm_out_close(adev);
-            adev->injection_enable = 1;
         } else {
+            pthread_mutex_lock(&adev->cap_handle_lock);
+            if (adev->capture_handle) {
+                aml_audio_capture_out_reset(adev->capture_handle);
+            }
+            pthread_mutex_unlock(&adev->cap_handle_lock);
             adev->injection_enable = 0;
         }
         goto exit;
@@ -7741,6 +7763,14 @@
     dprintf(fd, "\n");
     dprintf(fd, "[AML_HAL]      hdmi_format     : %10d |  active_outport    :    %s | active_inport: %s\n",
         aml_dev->hdmi_format, outputPort2Str(aml_dev->active_outport), inputPort2Str(get_active_inport(aml_dev)));
+    dprintf(fd, "[AML_HAL]      injection   : %d\n", aml_dev->injection_enable);
+    pthread_mutex_lock(&aml_dev->cap_handle_lock);
+    dprintf(fd, "[AML_HAL]      capture enable   : %d\n", aml_dev->capture_handle ? 1 :0);
+    if (aml_dev->capture_handle) {
+        aml_audio_capture_out_dump(aml_dev->capture_handle, fd);
+    }
+    pthread_mutex_unlock(&aml_dev->cap_handle_lock);
+
     dprintf(fd, "[AML_HAL]  audio_effect_enable : %10d | stream_write_data  :    %d\n",
         aml_dev->last_audio_effect_enable, aml_dev->stream_write_data);
     dprintf(fd, "[AML_HAL]      A2DP gain       : %10f \n",
@@ -8586,7 +8616,7 @@
     aml_audio_debug_open(aml_get_jason_string_value("DUMP_DEFAULT_PATH"));
 
 #ifndef NO_AUDIO_CAP
-    pthread_mutex_init(&adev->cap_buffer_lock, NULL);
+    pthread_mutex_init(&adev->cap_handle_lock, NULL);
 #endif
 
     //set DRC default as RF
diff --git a/audio_hal/audio_hw.h b/audio_hal/audio_hw.h
index 68eda67..52a5cef 100644
--- a/audio_hal/audio_hw.h
+++ b/audio_hal/audio_hw.h
@@ -563,9 +563,10 @@
     float record_volume_before_mute;//Record the master volume before mute
 #ifndef NO_AUDIO_CAP
     /* CapTure IpcBuffer */
-    pthread_mutex_t cap_buffer_lock;
-    void *cap_buffer;
-    int cap_delay;
+    void * capture_handle;
+    pthread_mutex_t cap_handle_lock;
+    int cap_mm_delay;
+    int cap_dtv_delay;
 #endif
     int synctype; //tsplayer TS mode input set
     int injection_enable;
diff --git a/input/dtv_patch_hal_avsync.c b/input/dtv_patch_hal_avsync.c
index d88d03c..2e8c8d9 100644
--- a/input/dtv_patch_hal_avsync.c
+++ b/input/dtv_patch_hal_avsync.c
@@ -773,8 +773,8 @@
     }
 #ifndef NO_AUDIO_CAP
     /*coverity[missing_lock]: aml_dev->cap_buffer does not need to be lock*/
-    if (aml_dev->cap_buffer) {
-        tuning_ms += aml_dev->cap_delay;
+    if (aml_dev->capture_handle) {
+        tuning_ms += aml_dev->cap_dtv_delay;
     } else {
         tuning_ms += aml_audio_property_get_int(PROPERTY_LOCAL_PASSTHROUGH_LATENCY, 0);
     }