1 /*****************************************************************
2  * Gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <sys/time.h>
26 
27 #include <gmerlin/player.h>
28 #include <playerprivate.h>
29 
30 #define LOG_DOMAIN "player"
31 #include <gmerlin/log.h>
32 
33 /* Input callbacks */
34 
35 
duration_changed(void * data,gavl_time_t duration)36 static void duration_changed(void * data, gavl_time_t duration)
37   {
38   bg_player_t * p = data;
39   bg_player_set_duration(p, duration, p->can_seek);
40   }
41 
name_changed(void * data,const char * name)42 static void name_changed(void * data, const char * name)
43   {
44   bg_player_t * p = data;
45   bg_player_set_track_name(p, name);
46   }
47 
metadata_changed(void * data,const gavl_metadata_t * m)48 static void metadata_changed(void * data, const gavl_metadata_t * m)
49   {
50   bg_player_t * p = data;
51   bg_player_set_metadata(p, m);
52   }
53 
buffer_notify(void * data,float percentage)54 static void buffer_notify(void * data, float percentage)
55   {
56   bg_player_t * p = data;
57   bg_player_set_state(p, BG_PLAYER_STATE_BUFFERING,
58                       &percentage, NULL);
59   }
60 
aspect_changed(void * data,int stream,int pixel_width,int pixel_height)61 static void aspect_changed(void * data, int stream, int pixel_width,
62                            int pixel_height)
63   {
64   bg_player_t * p = data;
65   bg_log(BG_LOG_INFO, LOG_DOMAIN, "Aspect ratio changed");
66 
67   bg_player_ov_update_aspect(&p->video_stream,
68                              pixel_width, pixel_height);
69   }
70 
71 
bg_player_create(bg_plugin_registry_t * plugin_reg)72 bg_player_t * bg_player_create(bg_plugin_registry_t * plugin_reg)
73   {
74   bg_player_t * ret;
75 
76   ret = calloc(1, sizeof(*ret));
77 
78   /* Callbacks */
79   ret->input_callbacks.data = ret;
80   ret->input_callbacks.name_changed     = name_changed;
81   ret->input_callbacks.duration_changed = duration_changed;
82   ret->input_callbacks.metadata_changed = metadata_changed;
83   ret->input_callbacks.buffer_notify    = buffer_notify;
84   ret->input_callbacks.aspect_changed   = aspect_changed;
85 
86   /* Create message queues */
87 
88   ret->command_queue  = bg_msg_queue_create();
89 
90   ret->message_queues = bg_msg_queue_list_create();
91 
92   ret->visualizer = bg_visualizer_create(plugin_reg);
93 
94   ret->thread_common = bg_player_thread_common_create();
95 
96   /* Create contexts */
97 
98   bg_player_audio_create(ret, plugin_reg);
99   bg_player_video_create(ret, plugin_reg);
100   bg_player_subtitle_create(ret);
101 
102   bg_player_input_create(ret);
103   bg_player_ov_create(ret);
104 
105   ret->threads[0] = ret->audio_stream.th;
106   ret->threads[1] = ret->video_stream.th;
107 
108   pthread_mutex_init(&ret->state_mutex, NULL);
109   pthread_mutex_init(&ret->config_mutex, NULL);
110 
111 
112 
113 
114   /* Subtitles are off by default */
115   ret->current_subtitle_stream = -1;
116   //  ret->current_subtitle_stream = 5;
117   ret->state = BG_PLAYER_STATE_INIT;
118 
119   ret->wait_time = 10000;
120 
121   return ret;
122   }
123 
bg_player_destroy(bg_player_t * player)124 void bg_player_destroy(bg_player_t * player)
125   {
126 
127   bg_player_input_destroy(player);
128   bg_player_ov_destroy(player);
129   bg_player_audio_destroy(player);
130   bg_player_video_destroy(player);
131   bg_player_subtitle_destroy(player);
132 
133   bg_visualizer_destroy(player->visualizer);
134 
135   bg_msg_queue_destroy(player->command_queue);
136 
137   bg_msg_queue_list_destroy(player->message_queues);
138 
139   pthread_mutex_destroy(&player->state_mutex);
140   pthread_mutex_destroy(&player->config_mutex);
141 
142   bg_player_thread_common_destroy(player->thread_common);
143 
144   free(player);
145   }
146 
bg_player_add_message_queue(bg_player_t * player,bg_msg_queue_t * message_queue)147 void bg_player_add_message_queue(bg_player_t * player,
148                                  bg_msg_queue_t * message_queue)
149   {
150   bg_msg_queue_list_add(player->message_queues, message_queue);
151   }
152 
bg_player_delete_message_queue(bg_player_t * player,bg_msg_queue_t * message_queue)153 void bg_player_delete_message_queue(bg_player_t * player,
154                                     bg_msg_queue_t * message_queue)
155   {
156   bg_msg_queue_list_remove(player->message_queues, message_queue);
157   }
158 
159 
bg_player_get_state(bg_player_t * player)160 int  bg_player_get_state(bg_player_t * player)
161   {
162   int ret;
163   pthread_mutex_lock(&player->state_mutex);
164   ret = player->state;
165   pthread_mutex_unlock(&player->state_mutex);
166   return ret;
167   }
168 
169 struct state_struct
170   {
171   int state;
172   float percentage;
173   int want_new;
174   int can_pause;
175   };
176 
msg_state(bg_msg_t * msg,const void * data)177 static void msg_state(bg_msg_t * msg,
178                       const void * data)
179   {
180   struct state_struct * s;
181   s = (struct state_struct *)data;
182 
183 
184   bg_msg_set_id(msg, BG_PLAYER_MSG_STATE_CHANGED);
185   bg_msg_set_arg_int(msg, 0, s->state);
186 
187   if(s->state == BG_PLAYER_STATE_BUFFERING)
188     {
189     bg_msg_set_arg_float(msg, 1, s->percentage);
190     }
191   else if(s->state == BG_PLAYER_STATE_CHANGING)
192     {
193     bg_msg_set_arg_int(msg, 1, s->want_new);
194     }
195   else if(s->state == BG_PLAYER_STATE_PLAYING)
196     {
197     bg_msg_set_arg_int(msg, 1, s->can_pause);
198     }
199   }
200 
bg_player_set_state(bg_player_t * player,int state,const void * arg1,const void * arg2)201 void bg_player_set_state(bg_player_t * player, int state,
202                          const void * arg1, const void * arg2)
203   {
204   struct state_struct s;
205   pthread_mutex_lock(&player->state_mutex);
206   player->state = state;
207   pthread_mutex_unlock(&player->state_mutex);
208 
209   /* Broadcast this message */
210 
211   //  memset(&state, 0, sizeof(state));
212 
213   s.state = state;
214 
215   if(state == BG_PLAYER_STATE_BUFFERING)
216     s.percentage = *((const float*)arg1);
217   else if(state == BG_PLAYER_STATE_CHANGING)
218     s.want_new = *((const int*)arg1);
219   else if(state == BG_PLAYER_STATE_PLAYING)
220     s.can_pause = *((const int*)arg1);
221 
222   bg_msg_queue_list_send(player->message_queues,
223                          msg_state,
224                          &s);
225   }
226 
227 const bg_parameter_info_t *
bg_player_get_visualization_parameters(bg_player_t * player)228 bg_player_get_visualization_parameters(bg_player_t *  player)
229   {
230   return bg_visualizer_get_parameters(player->visualizer);
231   }
232 
233 void
bg_player_set_visualization_parameter(void * data,const char * name,const bg_parameter_value_t * val)234 bg_player_set_visualization_parameter(void*data,
235                                       const char * name,
236                                       const bg_parameter_value_t*val)
237   {
238   bg_player_t * p;
239   int do_init;
240 
241   p = (bg_player_t*)data;
242   do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
243 
244   bg_visualizer_set_parameter(p->visualizer, name, val);
245 
246   if(!do_init)
247     {
248     if(bg_visualizer_need_restart(p->visualizer))
249       {
250       bg_player_interrupt(p);
251       bg_player_interrupt_resume(p);
252       }
253     }
254   }
255 
256 void
bg_player_set_visualization_plugin_parameter(void * data,const char * name,const bg_parameter_value_t * val)257 bg_player_set_visualization_plugin_parameter(void*data,
258                                              const char * name,
259                                              const bg_parameter_value_t*val)
260   {
261   bg_player_t * p;
262   p = (bg_player_t*)data;
263   bg_visualizer_set_vis_parameter(p->visualizer, name, val);
264   }
265 
266 void
bg_player_set_visualization(bg_player_t * p,int enable)267 bg_player_set_visualization(bg_player_t * p,
268                             int enable)
269   {
270   int was_enabled;
271   int do_init;
272   do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
273 
274   pthread_mutex_lock(&p->config_mutex);
275   was_enabled = p->visualizer_enabled;
276   p->visualizer_enabled = enable;
277   pthread_mutex_unlock(&p->config_mutex);
278 
279   if((was_enabled != enable) && !do_init)
280     {
281     bg_player_interrupt(p);
282     bg_player_interrupt_resume(p);
283     }
284   }
285 
286 void
bg_player_set_visualization_plugin(bg_player_t * p,const bg_plugin_info_t * plugin_info)287 bg_player_set_visualization_plugin(bg_player_t * p, const bg_plugin_info_t * plugin_info)
288   {
289   int do_init;
290   do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
291 
292   bg_visualizer_set_vis_plugin(p->visualizer, plugin_info);
293 
294   if(!do_init)
295     {
296     if(bg_visualizer_need_restart(p->visualizer))
297       {
298       bg_player_interrupt(p);
299       bg_player_interrupt_resume(p);
300       }
301     }
302 
303   }
304 
305 static const bg_parameter_info_t parameters[] =
306   {
307     {
308       .name        = "message_interval",
309       .long_name   = TRS("Control loop interval"),
310       .type        = BG_PARAMETER_INT,
311       .val_default = { .val_i = 10 },
312     },
313     {
314       .name         = "time_update",
315       .long_name    = TRS("Time update interval"),
316       .type         = BG_PARAMETER_STRINGLIST,
317       .multi_names  = (char const *[]){ "seconds", "frames", NULL },
318       .multi_labels = (char const *[]){ TRS("Seconds"), TRS("frames"), NULL },
319       .val_default  = { .val_str = "seconds" },
320     },
321     {
322       .name         = "report_peak",
323       .long_name    = TRS("Report peak values for audio"),
324       .type         = BG_PARAMETER_CHECKBUTTON,
325     },
326     {
327       .name         = "finish_mode",
328       .long_name    = TRS("Finish mode"),
329       .type         = BG_PARAMETER_STRINGLIST,
330       .multi_names  = (char const *[]){ "change", "pause", NULL },
331       .multi_labels = (char const *[]){ TRS("Change"), TRS("Pause"), NULL },
332       .val_default  = { .val_str = "change" },
333     },
334     { /* End of parameters */ }
335   };
336 
bg_player_get_parameters(bg_player_t * player)337 const bg_parameter_info_t * bg_player_get_parameters(bg_player_t * player)
338   {
339   return parameters;
340   }
341 
342 
bg_player_set_parameter(void * player,const char * name,const bg_parameter_value_t * val)343 void bg_player_set_parameter(void * player, const char * name,
344                              const bg_parameter_value_t * val)
345   {
346   bg_player_t * p = player;
347   if(!name)
348     return;
349   else if(!strcmp(name, "message_interval"))
350     {
351     p->wait_time = val->val_i;
352     p->wait_time *= 1000;
353     }
354   else if(!strcmp(name, "time_update"))
355     {
356     if(!strcmp(val->val_str, "second"))
357       {
358       p->time_update_mode = TIME_UPDATE_SECOND;
359       }
360     else if(!strcmp(val->val_str, "frame"))
361       {
362       p->time_update_mode = TIME_UPDATE_FRAME;
363       }
364     }
365   else if(!strcmp(name, "finish_mode"))
366     {
367     if(!strcmp(val->val_str, "change"))
368       {
369       p->finish_mode = BG_PLAYER_FINISH_CHANGE;
370       }
371     else if(!strcmp(val->val_str, "pause"))
372       {
373       p->finish_mode = BG_PLAYER_FINISH_PAUSE;
374       }
375     }
376   else if(!strcmp(name, "report_peak"))
377     {
378     if(val->val_i)
379       p->flags |= PLAYER_DO_REPORT_PEAK;
380     else
381       p->flags &= ~PLAYER_DO_REPORT_PEAK;
382     }
383   }
384