2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
23 #define GST_TIME_TO_MSEC(t) (t == GST_CLOCK_TIME_NONE ? t : (int)(((GstClockTime)(t)) / GST_MSECOND))
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);
32 int ms_set_state(media_streamer_s *ms_streamer, media_streamer_state_e state)
34 int ret = MEDIA_STREAMER_ERROR_NONE;
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");
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;
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;
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);
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);
71 ms_streamer->pend_state = MEDIA_STREAMER_STATE_IDLE;
72 ms_streamer->state = state;
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;
86 ret = ms_element_set_state(ms_streamer->pipeline, GST_STATE_PAUSED);
87 ms_streamer->pend_state = MEDIA_STREAMER_STATE_READY;
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;
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;
98 case MEDIA_STREAMER_STATE_SEEKING:
100 ms_info("Error: invalid state [%d]", state);
101 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
110 int ms_create(media_streamer_s *ms_streamer)
112 int ret = MEDIA_STREAMER_ERROR_NONE;
116 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
118 ms_load_ini_settings(&ms_streamer->ini);
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");
123 ret = ms_pipeline_create(ms_streamer);
125 if (MM_RESOURCE_MANAGER_ERROR_NONE != mm_resource_manager_create(
126 MM_RESOURCE_MANAGER_APP_CLASS_MEDIA,
127 __ms_resource_release_cb,
129 &ms_streamer->resource_manager)) {
130 ms_error("Failed to init resource manager for media streamer");
131 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
139 int ms_get_position(media_streamer_s *ms_streamer, int *time)
141 gint64 current = GST_CLOCK_TIME_NONE;
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");
148 if (!gst_element_query_position(ms_streamer->pipeline, GST_FORMAT_TIME, ¤t)) {
149 ms_error("Could not query current position.");
150 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
152 *time = GST_TIME_TO_MSEC(current);
153 ms_info("Media streamer queried position at [%d] msec successfully.", *time);
158 return MEDIA_STREAMER_ERROR_NONE;
161 int ms_get_duration(media_streamer_s *ms_streamer, int *time)
163 gint64 duration = GST_CLOCK_TIME_NONE;
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");
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;
172 *time = GST_TIME_TO_MSEC(duration);
173 ms_info("Media streamer queried duration [%d] msec successfully.", *time);
178 return MEDIA_STREAMER_ERROR_NONE;
181 int ms_seek(media_streamer_s *ms_streamer, int g_time, bool flag)
183 GstSeekFlags seek_flag;
187 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
190 seek_flag = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE;
192 seek_flag = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT;
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;
198 ms_streamer->is_seeking = TRUE;
201 ms_info("Media streamer pipeline seeked successfully to [%d] position", g_time);
205 return MEDIA_STREAMER_ERROR_NONE;
208 int ms_destroy(media_streamer_s *ms_streamer)
210 int ret = MEDIA_STREAMER_ERROR_NONE;
214 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
216 g_mutex_lock(&ms_streamer->mutex_lock);
218 ret = ms_set_state(ms_streamer, MEDIA_STREAMER_STATE_NONE);
219 MS_TABLE_SAFE_UNREF(ms_streamer->nodes_table);
221 MS_SAFE_UNREF(ms_streamer->bus);
222 MS_SAFE_UNREF(ms_streamer->pipeline);
224 /* Clean up exclude elements list */
225 if (ms_streamer->ini.exclude_elem_names)
226 g_strfreev(ms_streamer->ini.exclude_elem_names);
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);
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;
239 g_mutex_unlock(&ms_streamer->mutex_lock);
240 g_mutex_clear(&ms_streamer->mutex_lock);
241 MS_SAFE_FREE(ms_streamer);
248 static int __ms_acquire_resources(media_streamer_s *ms_streamer)
250 int ret = MEDIA_STREAMER_ERROR_NONE;
254 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
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");
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");
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");
285 return MEDIA_STREAMER_ERROR_NONE;
288 static int __ms_release_resources(media_streamer_s *ms_streamer)
290 int ret = MEDIA_STREAMER_ERROR_NONE;
294 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
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");
304 /* Release resources for transform bin. Here we also consider
305 nodes that were not added by user explicitly (e.g. decodebin
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");
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");
324 return MEDIA_STREAMER_ERROR_NONE;
328 static gboolean __ms_resource_node_find(gpointer key, gpointer value, gpointer user_data)
332 ms_retvm_if(key == NULL, FALSE, "key is NULL");
333 ms_retvm_if(value == NULL, FALSE, "value is NULL");
337 return ((media_streamer_node_s *)value)->resource ==
338 (mm_resource_manager_res_h)user_data;
341 static int __ms_resource_release_cb(mm_resource_manager_h rm,
342 mm_resource_manager_res_h resource_h, void *user_data)
344 media_streamer_s *streamer = (media_streamer_s *) user_data;
345 media_streamer_node_s *node;
349 ms_retvm_if(resource_h == NULL, 0, "resource_h is NULL");
350 ms_retvm_if(streamer == NULL, 0, "user_data is NULL");
352 ms_info("Received release_cb for streamer %p", streamer);
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);
360 /* Clear handle of released resource */
361 if (resource_h == streamer->video_decoder_resource) {
362 streamer->video_decoder_resource = NULL;
364 node = (media_streamer_node_s *)g_hash_table_find(streamer->nodes_table,
365 (GHRFunc)__ms_resource_node_find, resource_h);
367 ms_error("Failed to find released resource");
369 node->resource = NULL;
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);
380 streamer->is_interrupted = FALSE;
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);
389 ms_info("Interruption will not be handled because interrupted_cb is NULL");
391 g_mutex_unlock(&streamer->mutex_lock);
399 static int __ms_check_dpm_policy(media_streamer_s *ms_streamer)
401 int ret = MEDIA_STREAMER_ERROR_NONE;
405 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
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");
416 return MEDIA_STREAMER_ERROR_NONE;
419 static int __ms_change_dpm_policy_state(media_streamer_s *ms_streamer, media_streamer_state_e state)
421 int ret = MEDIA_STREAMER_ERROR_NONE;
425 ms_retvm_if(ms_streamer == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ms_streamer is NULL");
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);
435 case MEDIA_STREAMER_STATE_PAUSED:
436 case MEDIA_STREAMER_STATE_PLAYING:
437 case MEDIA_STREAMER_STATE_NONE:
438 case MEDIA_STREAMER_STATE_SEEKING:
443 ms_debug("Ignoring state for policy managements");