Modify stream stop API to prevent segfault
[platform/core/api/sound-pool.git] / src / priority.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * @file priority.c
19  * @brief This file include implementation of prioritization of streams in the
20  * SoundPool.
21  */
22
23 #include "internal/priority.h"
24 #include "internal/stream_cb_manager.h"
25
26 static gint __sound_stream_priority_cmp(gconstpointer a, gconstpointer b);
27
28 static gint __sound_stream_priority_cmp(gconstpointer a, gconstpointer b)
29 {
30         SP_DEBUG_FENTER();
31         SP_RETVM_IF(!a, 0, "Firs pointer is NULL.");
32         SP_RETVM_IF(!b, 0, "Second pointer is NULL.");
33         sound_stream_t *stream_a = (sound_stream_t *)a;
34         sound_stream_t *stream_b = (sound_stream_t *)b;
35
36         SP_DEBUG_FLEAVE();
37         if (stream_a->priority != stream_b->priority)
38                 return (stream_b->priority - stream_a->priority);
39         return (stream_a->id - stream_b->id);
40 }
41
42 sound_pool_error_e _sound_stream_priority_create(sound_pool_t *pool,
43                 stream_priority_manager_t **mgr)
44 {
45         SP_DEBUG_FENTER();
46         SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Stream priority "
47                         "manager can't be created for NULL sound pool");
48         SP_RETVM_IF(!mgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create "
49                         "stream priority manager. Manager pointer is NULL");
50
51         stream_priority_manager_t *_mgr = NULL;
52         SP_RETVM_IF(!(_mgr = g_try_malloc0(sizeof(*_mgr))),
53                         SOUND_POOL_ERROR_OUT_OF_MEMORY,
54                         "Memory alloc failure. Can't create stream priority manager.");
55
56         _mgr->priority_queue = NULL;
57         _mgr->pool = pool;
58         pool->mgr_priority = _mgr;
59
60         *mgr = _mgr;
61
62         SP_DEBUG_FLEAVE();
63         return SOUND_POOL_ERROR_NONE;
64 }
65
66 sound_pool_error_e _sound_stream_priority_destroy(stream_priority_manager_t *mgr)
67 {
68         SP_DEBUG_FENTER();
69         SP_RETVM_IF(!mgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle priority "
70                         "manager, it is NULL.");
71
72         if (mgr->priority_queue) {
73                 g_list_free(mgr->priority_queue);
74                 mgr->priority_queue = NULL;
75         }
76
77         mgr->pool = NULL;
78
79         SP_SAFE_GFREE(mgr);
80
81         SP_DEBUG_FLEAVE();
82         return SOUND_POOL_ERROR_NONE;
83 }
84
85 sound_pool_error_e _sound_stream_priority_add_stream(
86                 stream_priority_manager_t *mgr, sound_stream_t *stream)
87 {
88         SP_DEBUG_FENTER();
89         SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER);
90         SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
91
92         mgr->priority_queue = g_list_append(mgr->priority_queue, (gpointer)stream);
93         _sound_stream_priority_update_playback(mgr);
94
95         SP_DEBUG_FLEAVE();
96         return SOUND_POOL_ERROR_NONE;
97 }
98
99 sound_pool_error_e _sound_stream_priority_remove_stream(
100                 stream_priority_manager_t *mgr, sound_stream_t *stream)
101 {
102         SP_DEBUG_FENTER();
103         SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER);
104         SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
105         GList* to_delete;
106
107         to_delete = g_list_find(mgr->priority_queue, (gpointer)stream);
108         SP_RETVM_IF(NULL == to_delete, SOUND_POOL_ERROR_INVALID_PARAMETER,
109                         "Can't find to deleting link to stream [id:%d] from priority queue.",
110                         stream->id);
111         mgr->priority_queue = g_list_delete_link(mgr->priority_queue, to_delete);
112         _sound_stream_priority_update_playback(mgr);
113
114         SP_DEBUG_FLEAVE();
115         return SOUND_POOL_ERROR_NONE;
116 }
117
118 /*
119  * Checks selected stream for availability of playing. It depends
120  * on its position in priority queue and current state of other streams.
121  * The queue is analyzed downwards to lower priorities skipping all paused
122  * streams until first suspended stream is found.
123 */
124 sound_pool_error_e _sound_stream_priority_check(stream_priority_manager_t *mgr,
125                 sound_stream_t *stream, gboolean *out)
126 {
127         SP_DEBUG_FENTER();
128         SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER);
129         SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
130         SP_INST_CHECK(out, SOUND_POOL_ERROR_INVALID_PARAMETER);
131         SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
132         SP_INST_CHECK(mgr->pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
133         SP_INST_CHECK(mgr->pool->sources, SOUND_POOL_ERROR_INVALID_PARAMETER);
134         SP_INST_CHECK(g_hash_table_lookup(mgr->pool->sources,
135                         stream->parent_source->tag_name), SOUND_POOL_ERROR_KEY_NOT_AVAILABLE);
136         SP_RETVM_IF(NULL == mgr->priority_queue, SOUND_POOL_ERROR_INVALID_PARAMETER,
137                         "Can't perform checking stream [%s] due to priority queue is NULL.",
138                         stream->parent_source->tag_name);
139         *out = FALSE;
140
141         sound_stream_t *ref_stream = (sound_stream_t*)(mgr->priority_queue->data);
142         gboolean flag_target_priority = FALSE;
143         gboolean flag_terminator = FALSE;
144         unsigned rank = ref_stream->priority;
145
146         if (rank == stream->priority) {
147                 *out = TRUE;
148         } else {
149                 GList *iter = mgr->priority_queue;
150                 for (; iter != NULL && !flag_terminator; iter = iter->next) {
151                         sound_stream_t *str = (sound_stream_t *)iter->data;
152                         if ((ref_stream->priority > str->priority)) {
153                                 if (flag_target_priority) {
154                                         flag_terminator = TRUE;
155                                         continue;
156                                 }
157                                 rank = ref_stream->priority;
158                                 ref_stream = str;
159                         }
160
161                         if (SOUND_POOL_STREAM_STATE_PLAYING == str->state ||
162                                 SOUND_POOL_STREAM_STATE_SUSPENDED == str->state) {
163                                 rank = str->priority;
164                                 flag_target_priority = TRUE;
165                         }
166                 }
167
168                 if (rank == stream->priority)
169                         *out = TRUE;
170         }
171
172         SP_DEBUG_FLEAVE();
173         return SOUND_POOL_ERROR_NONE;
174 }
175
176 void _sound_stream_priority_update_playback(stream_priority_manager_t *mgr)
177 {
178         SP_DEBUG_FENTER();
179         SP_RETM_IF(!mgr, "Priority manager pointer is NULL.");
180         SP_RETM_IF(!mgr->pool, "Pool pointer is NULL.");
181         SP_RETM_IF(!mgr->pool->cbmgr, "Callback manager pointer is NULL.");
182         gboolean can_run = FALSE;
183
184         if (mgr->pool->state == SOUND_POOL_STATE_INACTIVE) {
185                 SP_INFO("No need to update streams state based on priority, "
186                                 "because of Sound pool is INACTIVE.");
187                 return;
188         }
189
190         mgr->priority_queue = g_list_sort(mgr->priority_queue,
191                         __sound_stream_priority_cmp);
192
193         GList *iter = mgr->priority_queue;
194         for (; iter != NULL; iter = iter->next) {
195                 sound_stream_t *stream = (sound_stream_t *)iter->data;
196
197                 if (SOUND_POOL_ERROR_NONE != _sound_stream_priority_check(mgr, stream,
198                                 &can_run))
199                         continue;
200
201                 if (can_run) {
202                         switch (stream->state) {
203                         case SOUND_POOL_STREAM_STATE_PLAYING:
204                                 if (SOUND_POOL_STREAM_PRIORITY_POLICY_MUTE == stream->priority_policy && stream->mute) {
205                                         SP_INFO("stream[%d] with priority[%d] was Playing with mute, now unmute",
206                                                         stream->id, stream->priority);
207                                         _sound_stream_set_mute(stream, FALSE);
208                                 }
209                                 break;
210                         case SOUND_POOL_STREAM_STATE_NONE:
211                         case SOUND_POOL_STREAM_STATE_SUSPENDED:
212                                 SP_INFO("stream[%d] with priority[%d] was Suspended(pool deactivate) or initial created, now play",
213                                                 stream->id, stream->priority);
214                                 _sound_stream_play(stream);
215                                 break;
216                         default:
217                                 break;
218                         }
219                 } else { /* Can't run */
220                         if (SOUND_POOL_STREAM_PRIORITY_POLICY_MUTE == stream->priority_policy) {
221                                 /* mute policy */
222                                 switch (stream->state) {
223                                 case SOUND_POOL_STREAM_STATE_PLAYING:
224                                         if (!stream->mute) {
225                                                 SP_INFO("stream[%d] with priority[%d] was Playing, now mute",
226                                                                 stream->id, stream->priority);
227                                                 _sound_stream_set_mute(stream, TRUE);
228                                         }
229                                         break;
230                                 case SOUND_POOL_STREAM_STATE_NONE:
231                                 case SOUND_POOL_STREAM_STATE_SUSPENDED:
232                                         SP_INFO("stream[%d] with priority[%d] was Suspended(pool deactivate) or initial created, now play with mute",
233                                                         stream->id, stream->priority);
234                                         _sound_stream_set_mute(stream, TRUE);
235                                         _sound_stream_play(stream);
236                                         break;
237                                 default:
238                                         /* nothing to do */
239                                         break;
240                                 }
241                         } else {
242                                 /* suspended policy */
243                                 switch (stream->state) {
244                                 case SOUND_POOL_STREAM_STATE_PLAYING:
245                                         SP_INFO("stream[%d] with priority[%d] was Playing, now Suspend",
246                                                         stream->id, stream->priority);
247                                         _sound_stream_suspend(stream);
248                                         break;
249                                 case SOUND_POOL_STREAM_STATE_NONE:
250                                         /* This case handles newly created streams in active pool, but with
251                                                 too low priority for transition to playing state: */
252                                         _sound_stream_send_event(stream, SOUND_POOL_STREAM_STATE_SUSPENDED);
253                                         break;
254                                 default:
255                                         /* nothing to do */
256                                         break;
257                                 }
258                         }
259                 }
260
261         }
262
263         SP_DEBUG_FLEAVE();
264 }
265