Wrap g_hash_table_remove() with ms_node_remove_from_table()
[platform/core/api/mediastreamer.git] / src / media_streamer_priv.c
1 /*
2  * Copyright (c) 2015 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 #include <mm_resource_manager.h>
18 #include "media_streamer_priv.h"
19 #include "media_streamer_util.h"
20 #include "media_streamer_node.h"
21 #include "media_streamer_gst.h"
22
23 #define GST_TIME_TO_MSEC(t) (t == GST_CLOCK_TIME_NONE ? t : (int)(((GstClockTime)(t)) / GST_MSECOND))
24
25 static int __ms_change_dpm_policy_state(media_streamer_s *ms_streamer, media_streamer_state_e state);
26 static gboolean __ms_resource_node_find(gpointer key, gpointer value, gpointer user_data);
27 static int __ms_resource_release_cb(mm_resource_manager_h rm,
28                 mm_resource_manager_res_h resource_h, void *user_data);
29 static int __ms_acquire_resources(media_streamer_s *ms_streamer);
30 static int __ms_release_resources(media_streamer_s *ms_streamer);
31
32 int ms_set_state(media_streamer_s *ms_streamer, media_streamer_state_e state)
33 {
34         int ret = MEDIA_STREAMER_ERROR_NONE;
35
36         ms_debug_fenter();
37
38         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
39         ms_retvm_if(ms_streamer->state == state, MEDIA_STREAMER_ERROR_NONE, "Media streamer already in this state");
40
41         if (!ms_streamer->is_interrupted) {
42                 ret = __ms_change_dpm_policy_state(ms_streamer, state);
43                 if (MEDIA_STREAMER_ERROR_NONE != ret) {
44                         ms_error("Failed to change DPM policy state for streamer %p", ms_streamer);
45                         return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
46                 }
47         }
48
49         switch (state) {
50         case MEDIA_STREAMER_STATE_NONE:
51                 /* Media streamer must be in IDLE state
52                  * Unlink and destroy all bins and elements. */
53                 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_NULL);
54                 ms_streamer->pend_state = MEDIA_STREAMER_STATE_NONE;
55                 ms_streamer->state = state;
56                 break;
57         case MEDIA_STREAMER_STATE_IDLE: {
58                 media_streamer_state_e prev_pend_state = ms_streamer->pend_state;
59                 /* Unlink all gst_elements, set pipeline into state NULL. */
60                 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_NULL);
61                 ms_bin_foreach_elements(GST_BIN(ms_streamer->sink_bin), ms_element_unlock_state, ms_streamer);
62
63                 /* Release resources, use ms_pipeline_get_state() to ensure the state change finish. */
64                 ms_pipeline_get_state(ms_streamer);
65                 if (!ms_streamer->is_interrupted &&
66                         (prev_pend_state == MEDIA_STREAMER_STATE_READY ||
67                         prev_pend_state == MEDIA_STREAMER_STATE_PAUSED ||
68                         prev_pend_state == MEDIA_STREAMER_STATE_PLAYING)) {
69                         ret = __ms_release_resources(ms_streamer);
70                 }
71                 ms_streamer->pend_state = MEDIA_STREAMER_STATE_IDLE;
72                 ms_streamer->state = state;
73                 break;
74         }
75         case MEDIA_STREAMER_STATE_READY:
76                 /* Acquire resources */
77                 if (!ms_streamer->is_interrupted &&
78                         ms_streamer->pend_state == MEDIA_STREAMER_STATE_IDLE) {
79                         ret = __ms_acquire_resources(ms_streamer);
80                         if (MEDIA_STREAMER_ERROR_NONE != ret) {
81                                 ms_error("Failed to change resources state for streamer %p", ms_streamer);
82                                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
83                         }
84                 }
85
86                 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_PAUSED);
87                 ms_streamer->pend_state = MEDIA_STREAMER_STATE_READY;
88                 break;
89         case MEDIA_STREAMER_STATE_PLAYING:
90                 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_PLAYING);
91                 ms_bin_foreach_elements(GST_BIN(ms_streamer->sink_bin), ms_element_unlock_state, ms_streamer);
92                 ms_streamer->pend_state = MEDIA_STREAMER_STATE_PLAYING;
93                 break;
94         case MEDIA_STREAMER_STATE_PAUSED:
95                 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_PAUSED);
96                 ms_streamer->pend_state = MEDIA_STREAMER_STATE_PAUSED;
97                 break;
98         case MEDIA_STREAMER_STATE_SEEKING:
99         default:{
100                         ms_info("Error: invalid state [%d]", state);
101                         return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
102                 }
103         }
104
105         ms_debug_fleave();
106
107         return ret;
108 }
109
110 int ms_create(media_streamer_s *ms_streamer)
111 {
112         int ret = MEDIA_STREAMER_ERROR_NONE;
113
114         ms_debug_fenter();
115
116         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
117
118         ms_load_ini_settings(&ms_streamer->ini);
119
120         ms_streamer->nodes_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, ms_node_remove_from_table_cb);
121         ms_retvm_if(ms_streamer->nodes_table == NULL, MEDIA_STREAMER_ERROR_INVALID_OPERATION, "Error creating hash table");
122
123         ret = ms_pipeline_create(ms_streamer);
124
125         if (MM_RESOURCE_MANAGER_ERROR_NONE != mm_resource_manager_create(
126                 MM_RESOURCE_MANAGER_APP_CLASS_MEDIA,
127                 __ms_resource_release_cb,
128                 ms_streamer,
129                 &ms_streamer->resource_manager)) {
130                 ms_error("Failed to init resource manager for media streamer");
131                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
132         }
133
134         ms_debug_fleave();
135
136         return ret;
137 }
138
139 int ms_get_position(media_streamer_s *ms_streamer, int *time)
140 {
141         gint64 current = GST_CLOCK_TIME_NONE;
142
143         ms_debug_fenter();
144
145         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
146         ms_retvm_if(time == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Return value is NULL");
147
148         if (!gst_element_query_position(ms_streamer->pipeline, GST_FORMAT_TIME, &current)) {
149                 ms_error("Could not query current position.");
150                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
151         } else {
152                 *time = GST_TIME_TO_MSEC(current);
153                 ms_info("Media streamer queried position at [%d] msec successfully.", *time);
154         }
155
156         ms_debug_fleave();
157
158         return MEDIA_STREAMER_ERROR_NONE;
159 }
160
161 int ms_get_duration(media_streamer_s *ms_streamer, int *time)
162 {
163         gint64 duration = GST_CLOCK_TIME_NONE;
164
165         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
166         ms_retvm_if(time == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Return value is NULL");
167
168         if (!gst_element_query_duration(ms_streamer->pipeline, GST_FORMAT_TIME, &duration)) {
169                 ms_error("Could not query current duration.");
170                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
171         } else {
172                 *time = GST_TIME_TO_MSEC(duration);
173                 ms_info("Media streamer queried duration [%d] msec successfully.", *time);
174         }
175
176         ms_debug_fleave();
177
178         return MEDIA_STREAMER_ERROR_NONE;
179 }
180
181 int ms_seek(media_streamer_s *ms_streamer, int g_time, bool flag)
182 {
183         GstSeekFlags seek_flag;
184
185         ms_debug_fenter();
186
187         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
188
189         if (flag)
190                 seek_flag = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE;
191         else
192                 seek_flag = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT;
193
194         if (!ms_gst_seek(ms_streamer->pipeline, (gint64) g_time * GST_MSECOND, seek_flag)) {
195                 ms_error("Error while seeking media streamer to [%d]", g_time);
196                 return MEDIA_STREAMER_ERROR_SEEK_FAILED;
197         } else {
198                 ms_streamer->is_seeking = TRUE;
199         }
200
201         ms_info("Media streamer pipeline seeked successfully to [%d] position", g_time);
202
203         ms_debug_fleave();
204
205         return MEDIA_STREAMER_ERROR_NONE;
206 }
207
208 int ms_destroy(media_streamer_s *ms_streamer)
209 {
210         int ret = MEDIA_STREAMER_ERROR_NONE;
211
212         ms_debug_fenter();
213
214         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
215
216         g_mutex_lock(&ms_streamer->mutex_lock);
217
218         ret = ms_set_state(ms_streamer, MEDIA_STREAMER_STATE_NONE);
219         MS_TABLE_SAFE_UNREF(ms_streamer->nodes_table);
220
221         MS_SAFE_UNREF(ms_streamer->bus);
222         MS_SAFE_UNREF(ms_streamer->pipeline);
223
224         /* Clean up exclude elements list */
225         if (ms_streamer->ini.exclude_elem_names)
226                 g_strfreev(ms_streamer->ini.exclude_elem_names);
227
228         /* Clean up resource required elements list */
229         if (ms_streamer->ini.resource_required_elem_names)
230                 g_strfreev(ms_streamer->ini.resource_required_elem_names);
231
232         if (MM_RESOURCE_MANAGER_ERROR_NONE !=
233                 mm_resource_manager_destroy(ms_streamer->resource_manager)) {
234                 ms_error("Failed to deinit resource manager for media streamer");
235                 g_mutex_unlock(&ms_streamer->mutex_lock);
236                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
237         }
238
239         g_mutex_unlock(&ms_streamer->mutex_lock);
240         g_mutex_clear(&ms_streamer->mutex_lock);
241         MS_SAFE_FREE(ms_streamer);
242
243         ms_debug_fleave();
244
245         return ret;
246 }
247
248 static int __ms_acquire_resources(media_streamer_s *ms_streamer)
249 {
250         int ret = MEDIA_STREAMER_ERROR_NONE;
251
252         ms_debug_fenter();
253
254         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
255
256         /* Acquire resources for src bin */
257         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->src_bin),
258                 ms_node_resources_acquire_iter, ms_streamer);
259         if (MEDIA_STREAMER_ERROR_NONE != ret) {
260                 ms_error("Failed to acquire resources for src bin");
261                 return ret;
262         }
263
264         /* Acquire resources for transform bin. If user doesn't
265            add nodes explicitly they will be acquired later.
266                  For example when decodebin is used resources are quired
267                  when 'autoplug-select' signal is triggered for new GstElement. */
268         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->transform_bin),
269                 ms_node_resources_acquire_iter, ms_streamer);
270         if (MEDIA_STREAMER_ERROR_NONE != ret) {
271                 ms_error("Failed to acquire resources for transform bin");
272                 return ret;
273         }
274
275         /* Acquire resources for src bin */
276         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->sink_bin),
277                 ms_node_resources_acquire_iter, ms_streamer);
278         if (MEDIA_STREAMER_ERROR_NONE != ret) {
279                 ms_error("Failed to acquire resources for sink bin");
280                 return ret;
281         }
282
283         ms_debug_fleave();
284
285         return MEDIA_STREAMER_ERROR_NONE;
286 }
287
288 static int __ms_release_resources(media_streamer_s *ms_streamer)
289 {
290         int ret = MEDIA_STREAMER_ERROR_NONE;
291
292         ms_debug_fenter();
293
294         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
295
296         /* Release resources for src bin */
297         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->src_bin),
298                 ms_node_resources_release_iter, ms_streamer);
299         if (MEDIA_STREAMER_ERROR_NONE != ret) {
300                 ms_error("Failed to release resources for src bin");
301                 return ret;
302         }
303
304         /* Release resources for transform bin. Here we also consider
305                  nodes that were not added by user explicitly (e.g. decodebin
306                  was used). */
307         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->transform_bin),
308                 ms_node_resources_release_iter, ms_streamer);
309         if (MEDIA_STREAMER_ERROR_NONE != ret) {
310                 ms_error("Failed to release resources for transform bin");
311                 return ret;
312         }
313
314         /* Release resources for src bin */
315         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->sink_bin),
316                 ms_node_resources_release_iter, ms_streamer);
317         if (MEDIA_STREAMER_ERROR_NONE != ret) {
318                 ms_error("Failed to release resources for sink bin");
319                 return ret;
320         }
321
322         ms_debug_fleave();
323
324         return MEDIA_STREAMER_ERROR_NONE;
325 }
326
327 //LCOV_EXCL_START
328 static gboolean __ms_resource_node_find(gpointer key, gpointer value, gpointer user_data)
329 {
330         ms_debug_fenter();
331
332         ms_retvm_if(key == NULL, FALSE, "key is NULL");
333         ms_retvm_if(value == NULL, FALSE, "value is NULL");
334
335         ms_debug_fleave();
336
337         return ((media_streamer_node_s *)value)->resource ==
338                         (mm_resource_manager_res_h)user_data;
339 }
340
341 static int __ms_resource_release_cb(mm_resource_manager_h rm,
342                 mm_resource_manager_res_h resource_h, void *user_data)
343 {
344         media_streamer_s *streamer = (media_streamer_s *) user_data;
345         media_streamer_node_s *node;
346
347         ms_debug_fenter();
348
349         ms_retvm_if(resource_h == NULL, 0, "resource_h is NULL");
350         ms_retvm_if(streamer == NULL, 0, "user_data is NULL");
351
352         ms_info("Received release_cb for streamer %p", streamer);
353
354         /* Here we perform action to release resources that
355                  were previously acquired (dynamically created case).
356                  Basically what we need to do is to unprepare media streamer
357                  without calling release function on every acquired resource. */
358         g_mutex_lock(&streamer->mutex_lock);
359
360         /* Clear handle of released resource */
361         if (resource_h == streamer->video_decoder_resource) {
362                 streamer->video_decoder_resource = NULL;
363         } else {
364                 node = (media_streamer_node_s *)g_hash_table_find(streamer->nodes_table,
365                                 (GHRFunc)__ms_resource_node_find, resource_h);
366                 if (node == NULL)
367                         ms_error("Failed to find released resource");
368                 else
369                         node->resource = NULL;
370         }
371
372         streamer->is_interrupted = TRUE;
373         if (MEDIA_STREAMER_ERROR_NONE !=
374                 ms_pipeline_unprepare(streamer)) {
375                 ms_error("Failed to unprepare streamer");
376                 streamer->is_interrupted = FALSE;
377                 g_mutex_unlock(&streamer->mutex_lock);
378                 return 0;
379         }
380         streamer->is_interrupted = FALSE;
381
382         /* Call interrupted_cb with appropriate code */
383         media_streamer_interrupted_cb interrupted_cb =
384                 (media_streamer_interrupted_cb) streamer->interrupted_cb.callback;
385         if (interrupted_cb) {
386                 interrupted_cb(MEDIA_STREAMER_INTERRUPTED_BY_RESOURCE_CONFLICT,
387                         streamer->interrupted_cb.user_data);
388         } else {
389                 ms_info("Interruption will not be handled because interrupted_cb is NULL");
390         }
391         g_mutex_unlock(&streamer->mutex_lock);
392
393         ms_debug_fleave();
394
395         return 0;
396 }
397 //LCOV_EXCL_STOP
398
399 static int __ms_check_dpm_policy(media_streamer_s *ms_streamer)
400 {
401         int ret = MEDIA_STREAMER_ERROR_NONE;
402
403         ms_debug_fenter();
404
405         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
406
407         ret = ms_bin_foreach_elements(GST_BIN(ms_streamer->src_bin),
408                 ms_node_dpm_policy_check_iter, ms_streamer);
409         if (MEDIA_STREAMER_ERROR_NONE != ret) {
410                 ms_error("Failed to check DPM policies for src bin");
411                 return ret;
412         }
413
414         ms_debug_fleave();
415
416         return MEDIA_STREAMER_ERROR_NONE;
417 }
418
419 static int __ms_change_dpm_policy_state(media_streamer_s *ms_streamer, media_streamer_state_e state)
420 {
421         int ret = MEDIA_STREAMER_ERROR_NONE;
422
423         ms_debug_fenter();
424
425         ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
426
427         /* According to state graph we need to handle only this state
428          * and different transitions into them */
429         if (state == MEDIA_STREAMER_STATE_READY) {
430                 switch (ms_streamer->pend_state) {
431                 case MEDIA_STREAMER_STATE_IDLE:
432                         /* After prepare function */
433                         ret = __ms_check_dpm_policy(ms_streamer);
434                         break;
435                 case MEDIA_STREAMER_STATE_PAUSED:
436                 case MEDIA_STREAMER_STATE_PLAYING:
437                 case MEDIA_STREAMER_STATE_NONE:
438                 case MEDIA_STREAMER_STATE_SEEKING:
439                 default:
440                         break;
441                 }
442         } else {
443                 ms_debug("Ignoring state for policy managements");
444         }
445
446         ms_debug_fleave();
447
448         return ret;
449 }