1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 #ifdef HAAS_AUDIO_DEMO
5 #include <posix/pthread.h>
6 #else
7 #include <pthread.h>
8 #endif
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <stdbool.h>
14 #include <aos/kernel.h>
15 #include <aos/list.h>
16 #include <errno.h>
17 #include "ulog/ulog.h"
18 #include "audio_rtos.h"
19 
20 #define LOG_TAG    "[pb_task]"
21 #define PB_HIGH_STACKSIZE            8192
22 #define PB_DEFAULT_PRIORITY          33
23 #define PB_TIMER_INTERVAL_MS         20    // minimum interval 20ms
24 #define PB_TIMER_MAX_IDLE_TIME_MS    2000  // max idle time 2s
25 #define PB_DATA_BUFFER_ENTRY_NUM_MAX 10
26 
27 typedef struct {
28     struct dlist_s list;
29     char *data;
30     unsigned int size;
31     unsigned int wOffset;
32     void *dev;
33 } pb_data_buf_t;
34 
35 static pthread_cond_t  pb_cond;
36 static pthread_mutex_t  pb_mutex;
37 static pthread_t pb_thread;
38 static bool bCreatePbThreadFlag = false;
39 static bool bPbStartFlag = false;
40 static aos_hdl_t flagMutex;
41 AOS_DLIST_HEAD(pb_data_list);
42 
pb_thread_loop(void * arg)43 static void pb_thread_loop(void *arg)
44 {
45     pb_data_buf_t *data_buf = NULL;
46     native_pcm_device_t *pcm_dev = NULL;
47     dlist_t *temp = NULL;
48     unsigned int frame_size, period_bytes, left_bytes;
49     unsigned int sleepCntUs;
50     long long now_ts = 0;
51     static int totalIdleTime = 0;
52     bool copyStartFlag = false;
53     LOGD(LOG_TAG, "%s:%d, entry!! bPbStartFlag %d", __func__, __LINE__, bPbStartFlag);
54     while(1) {
55         now_ts = aos_calendar_localtime_get();
56         aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
57         copyStartFlag = bPbStartFlag;
58         aos_mutex_unlock(&flagMutex);
59         if(true == copyStartFlag) {
60             sleepCntUs = PB_TIMER_INTERVAL_MS * 1000;
61             if(dlist_empty(&pb_data_list)) {
62                 totalIdleTime ++;
63                 if(totalIdleTime > PB_TIMER_MAX_IDLE_TIME_MS / PB_TIMER_INTERVAL_MS) {
64                     sleepCntUs = PB_TIMER_INTERVAL_MS * 100 * 1000;
65                 }
66                 LOGE(LOG_TAG, "%s:%d, data list empty, underflow!!", __func__, __LINE__);
67             } else {
68                 data_buf = dlist_first_entry(&pb_data_list, pb_data_buf_t, list);
69                 if(data_buf && data_buf->data && data_buf->dev) {
70                     pcm_dev = (native_pcm_device_t *)data_buf->dev;
71                     frame_size = pcm_dev->params.channels * pcm_dev->params.sample_bits / 8;
72                     period_bytes = frame_size * PB_TIMER_INTERVAL_MS * pcm_dev->params.rate / 1000;
73                     left_bytes = data_buf->size - data_buf->wOffset;
74                     if(left_bytes > period_bytes) {
75                         pcm_dev->ops->write(pcm_dev->hdl, data_buf->data + data_buf->wOffset, period_bytes);
76                         data_buf->wOffset += period_bytes;
77                         //LOGE(LOG_TAG, "%s:%d, fetch %d bytes from buffer", __func__, __LINE__, period_bytes);
78                     } else {
79                         pcm_dev->ops->write(pcm_dev->hdl, data_buf->data + data_buf->wOffset, left_bytes);
80                         data_buf->wOffset += left_bytes;
81                         sleepCntUs = left_bytes * PB_TIMER_INTERVAL_MS * 1000 / period_bytes;
82                         //LOGE(LOG_TAG, "%s:%d, fetch %d bytes from buffer", __func__, __LINE__, left_bytes);
83 
84                         /* free data_buf */
85                         dlist_del(&data_buf->list);
86                         free(data_buf->data);
87                         free(data_buf);
88                     }
89                 }
90             }
91             now_ts = (aos_calendar_localtime_get() - now_ts) * 1000;
92             if(sleepCntUs > now_ts) {
93                 usleep(sleepCntUs - now_ts);
94             }
95         } else {
96             LOGD(LOG_TAG, "%s:%d, wait for pb_cond", __func__, __LINE__);
97             dlist_for_each_entry_safe(&pb_data_list, temp, data_buf, pb_data_buf_t, list) {
98                 if(data_buf) {
99                     dlist_del(&data_buf->list);
100                     free(data_buf->data);
101                     free(data_buf);
102                 }
103             }
104             pthread_cond_wait(&pb_cond, &pb_mutex);
105         }
106     }
107 
108     return 0;
109 }
110 
create_pb_task(void)111 static int create_pb_task(void)
112 {
113     if(bCreatePbThreadFlag == true) {
114         LOGD(LOG_TAG, "%s:%d, pbthread is running, skip ...", __func__, __LINE__);
115         return -1;
116     }
117     LOGD(LOG_TAG, "%s:%d, -->>", __func__, __LINE__);
118     pthread_attr_t attr;
119     struct sched_param sched;
120 
121     aos_mutex_new(&flagMutex);
122     pthread_cond_init(&pb_cond, NULL);
123     pthread_mutex_init(&pb_mutex, NULL);
124     pthread_attr_init(&attr);
125     pthread_attr_setstacksize(&attr, PB_HIGH_STACKSIZE);
126     sched.sched_priority = PB_DEFAULT_PRIORITY;
127     pthread_attr_setschedparam(&attr, &sched);
128     pthread_create(&pb_thread, &attr, pb_thread_loop, NULL);
129     pthread_setname_np(pb_thread, "pbthread");
130     pthread_attr_destroy(&attr);
131     bCreatePbThreadFlag = true;
132     return 0;
133 }
134 
playback_task_start()135 void playback_task_start()
136 {
137     LOGE(LOG_TAG, "%s:%d", __func__, __LINE__);
138     aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
139     bPbStartFlag = true;
140     aos_mutex_unlock(&flagMutex);
141     create_pb_task();
142     pthread_cond_signal(&pb_cond);
143 }
144 
playback_task_stop()145 void playback_task_stop()
146 {
147     LOGE(LOG_TAG, "%s:%d", __func__, __LINE__);
148     aos_mutex_lock(&flagMutex, AOS_WAIT_FOREVER);
149     bPbStartFlag = false;
150     aos_mutex_unlock(&flagMutex);
151 }
152 
playback_task_drain()153 int playback_task_drain()
154 {
155     while(!dlist_empty(&pb_data_list)) {
156         aos_msleep(PB_TIMER_INTERVAL_MS);
157     }
158     return 0;
159 }
160 
playback_push_data(void * dev,const char * buf,unsigned int len,int blockMode)161 int playback_push_data(void *dev, const char *buf, unsigned int len, int blockMode)
162 {
163     if(1 == blockMode) {
164         while(dlist_entry_number(&pb_data_list) >= PB_DATA_BUFFER_ENTRY_NUM_MAX) {
165             usleep(PB_TIMER_INTERVAL_MS * 1000);
166         }
167     }
168     if(dlist_entry_number(&pb_data_list) >= PB_DATA_BUFFER_ENTRY_NUM_MAX) {
169         return 0;
170     }
171     pb_data_buf_t *databuf = malloc(sizeof(pb_data_buf_t));
172 
173     if(!databuf) {
174         LOGE(LOG_TAG, "%s:%d, malloc pb_data_buf_t failed", __func__, __LINE__);
175         return -1;
176     }
177     databuf->data = malloc(len);
178     if(!databuf->data) {
179         LOGE(LOG_TAG, "%s:%d, malloc %d bytes failed", __func__, __LINE__, len);
180         return -1;
181     }
182     memset(databuf->data, 0, len);
183     databuf->size = len;
184     memcpy(databuf->data, buf, len);
185     databuf->wOffset = 0;
186     databuf->dev = dev;
187     dlist_init(&databuf->list);
188     dlist_add_tail(&databuf->list, &pb_data_list);
189     //LOGE(LOG_TAG, "%s:%d: push %d bytes to buffer", __func__, __LINE__, len);
190     return len;
191 }