refactor cue function
[omnplay] / src / omnplay.cpp
1 /*
2 * omnplay.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <pthread.h>
30
31 #include "omnplay.h"
32 #include "ui.h"
33 #include "opts.h"
34 #include "timecode.h"
35
36 #include "omplrclnt.h"
37
38 static gboolean on_main_window_delete_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
39 {
40 gtk_exit(0);
41 return TRUE;
42 }
43
44 omnplay_instance_t* omnplay_create(int argc, char** argv)
45 {
46 int i, c;
47 omnplay_instance_t* app;
48
49 /* prepare application instance */
50 app = (omnplay_instance_t*)malloc(sizeof(omnplay_instance_t));
51 memset(app, 0, sizeof(omnplay_instance_t));
52
53 /* load parameters from command line */
54 if(!omnplay_opt(argc, argv, app) && app->players.count)
55 app->window = ui_omnplay(app);
56 else
57 omnplay_usage();
58
59 return app;
60 };
61
62 void omnplay_destroy(omnplay_instance_t* app)
63 {
64 free(app);
65 };
66
67 static void omnplay_update_status(omnplay_player_t* player, OmPlrStatus *prev , OmPlrStatus *curr)
68 {
69 char tc_cur[32], tc_rem[32], state[32], status[32];
70 const char *clip;
71
72 if(curr)
73 {
74 frames2tc(curr->pos - curr->minPos, 25.0, tc_cur);
75 frames2tc(curr->maxPos - curr->pos, 25.0, tc_rem);
76 strcpy(status, "ONLINE");
77 clip = curr->currClipName;
78
79 switch(curr->state)
80 {
81 case omPlrStateStopped: strcpy(state, "STOPPED"); break;
82 case omPlrStateCuePlay: strcpy(state, "CUE_PLAY"); break;
83 case omPlrStatePlay: strcpy(state, "PLAY"); break;
84 case omPlrStateCueRecord: strcpy(state, "CUE_RECORD"); break;
85 case omPlrStateRecord: strcpy(state, "RECORD"); break;
86 };
87 }
88 else
89 {
90 tc_cur[0] = 0;
91 tc_rem[0] = 0;
92 clip = "";
93 state[0] = 0;
94 strcpy(status, "OFFLINE");
95 };
96
97 gdk_threads_enter();
98 gtk_label_set_text(GTK_LABEL (player->label_tc_cur), tc_cur);
99 gtk_label_set_text(GTK_LABEL (player->label_tc_rem), tc_rem);
100 gtk_label_set_text(GTK_LABEL (player->label_state), state);
101 gtk_label_set_text(GTK_LABEL (player->label_status), status);
102 gtk_label_set_text(GTK_LABEL (player->label_clip), clip);
103 gdk_flush();
104 gdk_threads_leave();
105
106 memcpy(prev, curr, sizeof(OmPlrStatus));
107 };
108
109 static void* omnplay_thread_proc(void* data)
110 {
111 int r;
112 OmPlrStatus st_curr, st_prev;
113 omnplay_player_t* player = (omnplay_player_t*)data;
114
115 /* connect */
116 pthread_mutex_lock(&player->app->players.lock);
117 r = OmPlrOpen(player->host, player->name, (OmPlrHandle*)&player->handle);
118 pthread_mutex_unlock(&player->app->players.lock);
119 if(r)
120 {
121 fprintf(stderr, "ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
122 player->host, player->name, r);
123
124 return (void*)r;
125 };
126
127 /* setup to do not reconnect */
128 pthread_mutex_lock(&player->app->players.lock);
129 OmPlrSetRetryOpen((OmPlrHandle)player->handle, 0);
130 pthread_mutex_unlock(&player->app->players.lock);
131
132 /* setup directory */
133 if(player->app->players.path[0])
134 {
135 pthread_mutex_lock(&player->app->players.lock);
136 // r = OmPlrClipSetDirectory((OmPlrHandle)player->handle, player->app->players.path);
137 pthread_mutex_unlock(&player->app->players.lock);
138
139 if(r)
140 {
141 fprintf(stderr, "ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
142 player->app->players.path, r);
143
144 pthread_mutex_lock(&player->app->players.lock);
145 OmPlrClose((OmPlrHandle)player->handle);
146 pthread_mutex_unlock(&player->app->players.lock);
147
148 return (void*)r;
149 };
150 };
151
152 /* endless loop */
153 for(r = 0 ; !player->app->f_exit && !r;)
154 {
155 /* sleep */
156 usleep(100000);
157
158 /* get status */
159 pthread_mutex_lock(&player->app->players.lock);
160 st_curr.size = sizeof(OmPlrStatus);
161 r = OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &st_curr);
162 pthread_mutex_unlock(&player->app->players.lock);
163
164 if(r)
165 fprintf(stderr, "ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r);
166 else
167 if(memcmp(&st_curr, &st_prev, sizeof(OmPlrStatus)))
168 omnplay_update_status(player, &st_prev , &st_curr);
169 };
170
171 pthread_mutex_lock(&player->app->players.lock);
172 OmPlrClose((OmPlrHandle)player->handle);
173 pthread_mutex_unlock(&player->app->players.lock);
174
175 return NULL;
176 };
177
178 static int get_first_selected_item_playlist(omnplay_instance_t* app)
179 {
180 int idx;
181 GtkTreeIter iter;
182 GtkTreeModel *model;
183 GtkTreeSelection *selection;
184
185 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
186 if(selection && gtk_tree_selection_get_selected(selection, &model, &iter))
187 {
188 gtk_tree_model_get(model, &iter, 7, &idx, -1);
189 return idx;
190 };
191 return -1;
192 };
193
194 static int get_playlist_block(omnplay_instance_t* app, int idx, int* start_ptr, int* stop_ptr)
195 {
196 int start, stop;
197
198 for(start = idx; start >= 0; start--)
199 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
200 break;
201
202 for(stop = idx; stop < app->playlist.count; stop++)
203 if(app->playlist.item[stop].type & OMNPLAY_PLAYLIST_BLOCK_END)
204 break;
205
206 fprintf(stderr, "get_playlist_block: range %d -> %d\n", start, stop);
207
208 /* check block range */
209 if(start >= 0 && stop < app->playlist.count)
210 {
211 *start_ptr = start;
212 *stop_ptr = stop;
213 return (stop - start + 1);
214 };
215
216 return -1;
217 };
218
219 static omnplay_player_t *get_player_at_pos(omnplay_instance_t* app, int pos)
220 {
221 /* check player range */
222 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
223 return &app->players.item[app->playlist.item[pos].player];
224
225 return NULL;
226 };
227
228 static void omnplay_cue(omnplay_instance_t* app)
229 {
230 int idx, start, stop;
231 omnplay_player_t *player;
232
233 pthread_mutex_lock(&app->playlist.lock);
234
235 idx = get_first_selected_item_playlist(app);
236
237 if(idx < 0)
238 {
239 pthread_mutex_unlock(&app->playlist.lock);
240 return;
241 };
242
243 fprintf(stderr, "cue: selected item is %d\n", idx);
244
245 if(get_playlist_block(app, idx, &start, &stop) < 0)
246 {
247 pthread_mutex_unlock(&app->playlist.lock);
248 return;
249 };
250
251 fprintf(stderr, "cue: range %d -> %d\n", start, stop);
252
253 player = get_player_at_pos(app, start);
254
255 if(!player)
256 {
257 pthread_mutex_unlock(&app->playlist.lock);
258 return;
259 };
260
261 /* 1. stop */
262 pthread_mutex_lock(&app->players.lock);
263 OmPlrStop((OmPlrHandle)player->handle);
264 player->playlist_start = -1;
265 player->playlist_count = -1;
266 pthread_mutex_unlock(&app->players.lock);
267
268 /* 2. detach previous clips */
269 pthread_mutex_lock(&app->players.lock);
270 OmPlrDetachAllClips((OmPlrHandle)player->handle);
271 pthread_mutex_unlock(&app->players.lock);
272
273 // http://research.m1stereo.tv/viewvc-int/viewvc.cgi/Ingest2Srv/trunk/SrvPlayCtl.cpp?view=markup
274
275 pthread_mutex_unlock(&app->playlist.lock);
276
277 };
278
279 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
280 {
281 switch(button)
282 {
283 case BUTTON_PLAYLIST_ITEM_ADD:
284 case BUTTON_PLAYLIST_ITEM_DEL:
285 case BUTTON_PLAYLIST_ITEM_EDIT:
286 case BUTTON_PLAYLIST_LOAD:
287 omnplay_playlist_load(app);
288 break;
289 case BUTTON_PLAYLIST_SAVE:
290 omnplay_playlist_save(app);
291 break;
292 case BUTTON_PLAYLIST_BLOCK_SINGLE:
293 case BUTTON_PLAYLIST_BLOCK_LOOP:
294 case BUTTON_PLAYLIST_ITEM_UP:
295 case BUTTON_PLAYLIST_ITEM_DOWN:
296 case BUTTON_PLAYER_CUE:
297 omnplay_cue(app);
298 case BUTTON_PLAYER_PLAY:
299 case BUTTON_PLAYER_PAUSE:
300 case BUTTON_PLAYER_STOP:
301 case BUTTON_LIBRARY_ADD:
302 case BUTTON_LIBRARY_REFRESH:
303 break;
304 };
305
306 return TRUE;
307 };
308
309 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
310 {
311 int i;
312 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
313
314 for(i = 1; i < BUTTON_LAST; i++)
315 if(app->buttons[i] == button)
316 return omnplay_button_click(app, (control_buttons_t)i);
317
318 return FALSE;
319 };
320
321 void omnplay_init(omnplay_instance_t* app)
322 {
323 int i;
324
325 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
326 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
327
328 /* create lock */
329 pthread_mutex_init(&app->players.lock, NULL);
330
331 /* create a omneon status thread */
332 for(i = 0; i < app->players.count; i++)
333 pthread_create(&app->players.item[i].thread, NULL,
334 omnplay_thread_proc, &app->players.item[i]);
335
336 /* create lock */
337 pthread_mutex_init(&app->playlist.lock, NULL);
338
339 /* attach buttons click */
340 for(i = 1; i < BUTTON_LAST; i++)
341 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
342 GTK_SIGNAL_FUNC( on_button_click), app );
343
344 };
345
346 void omnplay_release(omnplay_instance_t* app)
347 {
348 int i;
349 void* r;
350
351 app->f_exit = 1;
352
353 for(i = 0; i < app->players.count; i++)
354 /* create a omneon status thread */
355 pthread_join(app->players.item[i].thread, &r);
356
357 /* destroy lock */
358 pthread_mutex_destroy(&app->players.lock);
359
360 /* destroy lock */
361 pthread_mutex_destroy(&app->playlist.lock);
362 };