2912ecdcda00a13300b8dfe313066e4d17d751ab
[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 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <pthread.h>
34
35 #include "omnplay.h"
36 #include "ui.h"
37 #include "opts.h"
38 #include "timecode.h"
39
40 #include "omplrclnt.h"
41
42 int omnplay_get_content(omnplay_instance_t* app, playlist_item_t *items, int limit,
43 omnplay_get_content_cb_proc proc, void* data)
44 {
45 int r, c = 0;
46 OmPlrClipInfo clip_info;
47 char clip_name[omPlrMaxClipDirLen];
48
49 pthread_mutex_lock(&app->players.lock);
50
51 r = OmPlrClipGetFirst((OmPlrHandle)app->players.item[0].handle, clip_name, sizeof(clip_name));
52 for(; c < limit && !r;)
53 {
54 /* get clip info */
55 clip_info.maxMsTracks = 0;
56 clip_info.size = sizeof(clip_info);
57
58 r = OmPlrClipGetInfo((OmPlrHandle)app->players.item[0].handle, clip_name, &clip_info);
59
60 if(!r)
61 {
62 /* copy item props */
63 strncpy(items[c].id, clip_name, PATH_MAX);
64 items[c].in = clip_info.firstFrame;
65 items[c].dur = clip_info.lastFrame - clip_info.firstFrame;
66
67 /* callback */
68 pthread_mutex_unlock(&app->players.lock);
69 if(proc)
70 proc(app, &items[c], data);
71 pthread_mutex_lock(&app->players.lock);
72
73 c++;
74 };
75
76 r = OmPlrClipGetNext((OmPlrHandle)app->players.item[0].handle, clip_name, sizeof(clip_name));
77 };
78
79 pthread_mutex_unlock(&app->players.lock);
80
81 return c;
82 };
83
84
85 static gboolean on_main_window_delete_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
86 {
87 gtk_exit(0);
88 return TRUE;
89 }
90
91 omnplay_instance_t* omnplay_create(int argc, char** argv)
92 {
93 int i, c;
94 omnplay_instance_t* app;
95
96 /* prepare application instance */
97 app = (omnplay_instance_t*)malloc(sizeof(omnplay_instance_t));
98 memset(app, 0, sizeof(omnplay_instance_t));
99
100 /* load parameters from command line */
101 if(!omnplay_opt(argc, argv, app) && app->players.count)
102 app->window = ui_omnplay(app);
103 else
104 omnplay_usage();
105
106 return app;
107 };
108
109 void omnplay_destroy(omnplay_instance_t* app)
110 {
111 free(app);
112 };
113
114 static int find_index_of_playlist_item(omnplay_instance_t* app, int start, int idx)
115 {
116 while(1)
117 {
118 if(app->playlist.item[start].omn_idx == idx)
119 return start;
120
121 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_END)
122 break;
123
124 start++;
125 };
126
127 return -1;
128 };
129
130 static void omnplay_update_status(omnplay_player_t* player, OmPlrStatus *prev , OmPlrStatus *curr)
131 {
132 int idx;
133 char tc_cur[32], tc_rem[32], state[32], status[32];
134 const char *clip;
135
136 if(curr)
137 {
138 frames2tc(curr->pos - curr->minPos, 25.0, tc_cur);
139 frames2tc(curr->maxPos - curr->pos, 25.0, tc_rem);
140 strcpy(status, "ONLINE");
141 clip = curr->currClipName;
142
143 switch(curr->state)
144 {
145 case omPlrStateStopped: strcpy(state, "STOPPED"); break;
146 case omPlrStateCuePlay: strcpy(state, "CUE_PLAY"); break;
147 case omPlrStatePlay: strcpy(state, "PLAY"); break;
148 case omPlrStateCueRecord: strcpy(state, "CUE_RECORD"); break;
149 case omPlrStateRecord: strcpy(state, "RECORD"); break;
150 };
151 }
152 else
153 {
154 tc_cur[0] = 0;
155 tc_rem[0] = 0;
156 clip = "";
157 state[0] = 0;
158 strcpy(status, "OFFLINE");
159 };
160
161 /* update status in status page */
162 gdk_threads_enter();
163 gtk_label_set_text(GTK_LABEL (player->label_tc_cur), tc_cur);
164 gtk_label_set_text(GTK_LABEL (player->label_tc_rem), tc_rem);
165 gtk_label_set_text(GTK_LABEL (player->label_state), state);
166 gtk_label_set_text(GTK_LABEL (player->label_status), status);
167 gtk_label_set_text(GTK_LABEL (player->label_clip), clip);
168 gdk_flush();
169 gdk_threads_leave();
170
171 /* update remaining time */
172 gdk_threads_enter();
173 pthread_mutex_lock(&player->app->playlist.lock);
174 pthread_mutex_lock(&player->app->players.lock);
175 if(curr->state == omPlrStatePlay || curr->state == omPlrStateCuePlay)
176 {
177 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
178 if(idx >= 0)
179 {
180 frames2tc(curr->currClipStartPos + curr->currClipLen - curr->pos, 25.0, tc_rem);
181 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
182 }
183 if(curr->currClipNum != prev->currClipNum && 1 != prev->numClips)
184 {
185 tc_rem[0] = 0;
186 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
187 if(idx >= 0)
188 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
189 };
190 }
191 else
192 {
193 tc_rem[0] = 0;
194 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
195 if(idx >= 0)
196 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
197 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
198 if(idx >= 0)
199 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
200 };
201 pthread_mutex_unlock(&player->app->players.lock);
202 pthread_mutex_unlock(&player->app->playlist.lock);
203 gdk_flush();
204 gdk_threads_leave();
205
206
207 memcpy(prev, curr, sizeof(OmPlrStatus));
208 };
209
210 static void* omnplay_thread_proc(void* data)
211 {
212 int r;
213 OmPlrStatus st_curr, st_prev;
214 omnplay_player_t* player = (omnplay_player_t*)data;
215
216 /* connect */
217 pthread_mutex_lock(&player->app->players.lock);
218 r = OmPlrOpen(player->host, player->name, (OmPlrHandle*)&player->handle);
219 pthread_mutex_unlock(&player->app->players.lock);
220 if(r)
221 {
222 fprintf(stderr, "ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
223 player->host, player->name, r);
224
225 return (void*)r;
226 };
227
228 /* setup to do not reconnect */
229 pthread_mutex_lock(&player->app->players.lock);
230 OmPlrSetRetryOpen((OmPlrHandle)player->handle, 0);
231 pthread_mutex_unlock(&player->app->players.lock);
232
233 /* setup directory */
234 if(player->app->players.path[0])
235 {
236 pthread_mutex_lock(&player->app->players.lock);
237 r = OmPlrClipSetDirectory((OmPlrHandle)player->handle, player->app->players.path);
238 pthread_mutex_unlock(&player->app->players.lock);
239
240 if(r)
241 {
242 fprintf(stderr, "ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
243 player->app->players.path, r);
244
245 pthread_mutex_lock(&player->app->players.lock);
246 OmPlrClose((OmPlrHandle)player->handle);
247 pthread_mutex_unlock(&player->app->players.lock);
248
249 return (void*)r;
250 };
251 };
252
253 /* endless loop */
254 for(r = 0 ; !player->app->f_exit && !r;)
255 {
256 /* sleep */
257 usleep(100000);
258
259 /* get status */
260 pthread_mutex_lock(&player->app->players.lock);
261 st_curr.size = sizeof(OmPlrStatus);
262 r = OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &st_curr);
263 pthread_mutex_unlock(&player->app->players.lock);
264
265 if(r)
266 fprintf(stderr, "ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r);
267 else
268 if(memcmp(&st_curr, &st_prev, sizeof(OmPlrStatus)))
269 omnplay_update_status(player, &st_prev , &st_curr);
270 };
271
272 pthread_mutex_lock(&player->app->players.lock);
273 OmPlrClose((OmPlrHandle)player->handle);
274 pthread_mutex_unlock(&player->app->players.lock);
275
276 return NULL;
277 };
278
279 void get_selected_items_playlist_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
280 {
281 int idx, *list = (int*)data;
282 gtk_tree_model_get(model, iter, 7, &idx, -1);
283 list[list[0] + 1] = idx;
284 list[0] = list[0] + 1;
285 };
286
287 static int* get_selected_items_playlist(omnplay_instance_t* app)
288 {
289 int* list = NULL;
290 GtkTreeSelection *selection;
291
292 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
293 if(selection)
294 {
295 list = (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
296 memset(list, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
297
298 gtk_tree_selection_selected_foreach(
299 selection,
300 get_selected_items_playlist_proc,
301 list);
302
303 if(!list[0])
304 {
305 free(list);
306 list = NULL;
307 };
308 };
309
310 return list;
311 };
312
313 static int idx_in_players_range(omnplay_instance_t* app, int idx)
314 {
315 int i, r = 0;
316
317 for(i = 0; i < app->players.count && !r; i++)
318 {
319 int a, b;
320
321 a = app->players.item[i].playlist_start;
322 b = app->players.item[i].playlist_length;
323
324 if(b <= 0)
325 continue;
326
327 b = a + b - 1;
328
329 if(idx >= a && idx <= b) r = 1;
330 };
331
332 return r;
333 };
334
335 static int idxs_in_players_range(omnplay_instance_t* app, int start, int stop)
336 {
337 int i, r = 0;
338
339 for(i = 0; i < app->players.count && !r; i++)
340 {
341 int a, b;
342
343 a = app->players.item[i].playlist_start;
344 b = app->players.item[i].playlist_length;
345
346 if(b <= 0)
347 continue;
348
349 b = a + b - 1;
350
351 #define IN_RANGE(A,B,C) (A <= C && C <= B)
352 if( IN_RANGE(a,b,start) ||
353 IN_RANGE(a,b,stop) ||
354 IN_RANGE(start,stop,a) ||
355 IN_RANGE(start,stop,b))
356 r = 1;
357 };
358
359 return r;
360 };
361
362 static void omnplay_playlist_block(omnplay_instance_t* app, control_buttons_t button)
363 {
364 int start, stop, r, i;
365 int* list = get_selected_items_playlist(app);
366
367 if(!list)
368 return;
369
370 pthread_mutex_lock(&app->playlist.lock);
371 pthread_mutex_lock(&app->players.lock);
372
373 start = list[1];
374 stop = list[list[0]];
375
376 if(!idxs_in_players_range(app, start, stop))
377 {
378 int loop = (button == BUTTON_PLAYLIST_BLOCK_LOOP)?OMNPLAY_PLAYLIST_BLOCK_LOOP:0;
379
380 /* update selected item */
381 for(i = start; i <= stop; i++)
382 {
383 int t = OMNPLAY_PLAYLIST_BLOCK_BODY | loop;
384
385 if(i == start) t |= OMNPLAY_PLAYLIST_BLOCK_BEGIN;
386 if(i == stop) t |= OMNPLAY_PLAYLIST_BLOCK_END;
387
388 app->playlist.item[i].type = (playlist_item_type_t)t;
389
390 omnplay_playlist_draw_item(app, i);
391 };
392
393 /* update border items */
394 if(!start && !(app->playlist.item[start - 1].type & OMNPLAY_PLAYLIST_BLOCK_END))
395 {
396 app->playlist.item[start - 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_END
397 | app->playlist.item[start - 1].type);
398 omnplay_playlist_draw_item(app, start - 1);
399 };
400 if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN))
401 {
402 app->playlist.item[stop + 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_BEGIN
403 | app->playlist.item[stop + 1].type);
404 omnplay_playlist_draw_item(app, stop + 1);
405 };
406 }
407 else
408 fprintf(stderr, "omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
409 start, stop);
410
411 pthread_mutex_unlock(&app->players.lock);
412 pthread_mutex_unlock(&app->playlist.lock);
413
414 free(list);
415 };
416
417 static int get_first_selected_item_playlist(omnplay_instance_t* app)
418 {
419 int idx;
420 int* list = get_selected_items_playlist(app);
421 if(!list) return -1;
422 idx = list[1];
423 free(list);
424 return idx;
425 };
426
427 static int get_playlist_block(omnplay_instance_t* app, int idx, int* start_ptr, int* stop_ptr)
428 {
429 int start, stop;
430
431 for(start = idx; start >= 0; start--)
432 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
433 break;
434
435 for(stop = idx; stop < app->playlist.count; stop++)
436 if(app->playlist.item[stop].type & OMNPLAY_PLAYLIST_BLOCK_END)
437 break;
438
439 fprintf(stderr, "get_playlist_block: range %d -> %d\n", start, stop);
440
441 /* check block range */
442 if(start >= 0 && stop < app->playlist.count)
443 {
444 *start_ptr = start;
445 *stop_ptr = stop;
446 return (stop - start + 1);
447 };
448
449 return -1;
450 };
451
452 static omnplay_player_t *get_player_at_pos(omnplay_instance_t* app, int pos)
453 {
454 /* check player range */
455 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
456 return &app->players.item[app->playlist.item[pos].player];
457
458 return NULL;
459 };
460
461 static void omnplay_playlist_delete_items(omnplay_instance_t* app, int* idxs, int count)
462 {
463 int i, j, idx;
464 GtkTreePath* path;
465
466 pthread_mutex_lock(&app->playlist.lock);
467 pthread_mutex_lock(&app->players.lock);
468
469 for(j = 0; j < count; j++)
470 {
471 idx = idxs[j] - j;
472
473 /* fix block types */
474 if(idx)
475 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
476 OMNPLAY_PLAYLIST_BLOCK_END);
477 if(idx + 1 < app->playlist.count)
478 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
479 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
480
481 /* shift playlist items */
482 memmove
483 (
484 &app->playlist.item[idx],
485 &app->playlist.item[idx + 1],
486 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
487 );
488
489 /* decrement items count */
490 app->playlist.count--;
491
492 /* increment servers indexes */
493 for(i = 0; i < app->players.count; i++)
494 if(app->players.item[i].playlist_start >= idx)
495 app->players.item[i].playlist_start--;
496
497
498 };
499
500 /* redraw playlist */
501 omnplay_playlist_draw(app);
502
503 /* select */
504 path = gtk_tree_path_new_from_indices(idxs[0], -1);
505 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
506 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
507 gtk_tree_path_free(path);
508
509
510 pthread_mutex_unlock(&app->players.lock);
511 pthread_mutex_unlock(&app->playlist.lock);
512 };
513
514 static void omnplay_playlist_item_del(omnplay_instance_t* app)
515 {
516 int i, idx, c;
517 int *list, *list2;
518
519 list = get_selected_items_playlist(app);
520 if(!list) return;
521
522 list2 = (int*)malloc(sizeof(int) * list[0]);
523
524 for(i = 0, c = 0; i < list[0]; i++)
525 {
526 /* check for playing block */
527 if(idx_in_players_range(app, list[i + 1]))
528 continue;
529
530 /* save index */
531 list2[c++] = list[i + 1];
532 };
533
534 if(c)
535 omnplay_playlist_delete_items(app, list2, c);
536
537 free(list2);
538 free(list);
539 };
540
541 static int omnplay_playlist_insert_check(omnplay_instance_t* app, int idx, playlist_item_type_t* t)
542 {
543 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
544
545 /* before or after playlist */
546 if(!idx || idx == app->playlist.count)
547 return 1;
548
549 /* check for block borders */
550 if( app->playlist.item[idx - 1].type & OMNPLAY_PLAYLIST_BLOCK_END &&
551 app->playlist.item[idx + 0].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
552 return 1;
553
554 /* check for playing block */
555 if(idx_in_players_range(app, idx))
556 return 0;
557
558 if(app->playlist.item[idx].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
559 *t = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY;
560 else
561 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY;
562
563 return 1;
564 };
565
566 static void omnplay_playlist_insert_items(omnplay_instance_t* app, int idx,
567 playlist_item_t* items, int count)
568 {
569 int i;
570 GtkTreePath* path;
571
572 pthread_mutex_lock(&app->playlist.lock);
573 pthread_mutex_lock(&app->players.lock);
574
575 /* shift playlist items */
576 memmove
577 (
578 &app->playlist.item[idx + count],
579 &app->playlist.item[idx],
580 (app->playlist.count - idx) * sizeof(playlist_item_t)
581 );
582
583 /* copy new items */
584 memcpy
585 (
586 &app->playlist.item[idx],
587 items,
588 count * sizeof(playlist_item_t)
589 );
590
591 /* increment servers indexes */
592 for(i = 0; i < app->players.count; i++)
593 if(app->players.item[i].playlist_start >= idx)
594 app->players.item[i].playlist_start += idx;
595
596 /* increment items count */
597 app->playlist.count += count;
598
599 /* redraw playlist */
600 omnplay_playlist_draw(app);
601
602 /* select */
603 path = gtk_tree_path_new_from_indices(idx + count, -1);
604 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
605 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
606 gtk_tree_path_free(path);
607
608 pthread_mutex_unlock(&app->players.lock);
609 pthread_mutex_unlock(&app->playlist.lock);
610 };
611
612 static void omnplay_playlist_item_add(omnplay_instance_t* app, int after)
613 {
614 int idx;
615 playlist_item_t item;
616 playlist_item_type_t t;
617
618 /* find insert position */
619 idx = get_first_selected_item_playlist(app);
620 if(idx < 0)
621 idx = 0;
622 else
623 idx += (after)?1:0;
624
625 if(!omnplay_playlist_insert_check(app, idx, &t))
626 return;
627
628 fprintf(stderr, "allowed insert into idx=%d\n", idx);
629
630 /* clear item */
631 memset(&item, 0, sizeof(playlist_item_t));
632 if(ui_playlist_item_dialog(app, &item))
633 {
634 item.type = t;
635 omnplay_playlist_insert_items(app, idx, &item, 1);
636 };
637 };
638
639 static void omnplay_playlist_item_edit(omnplay_instance_t* app)
640 {
641 int idx;
642 playlist_item_t item;
643
644 /* find insert position */
645 idx = get_first_selected_item_playlist(app);
646
647 if(idx < 0)
648 return;
649
650 /* check for playing block */
651 if(idx_in_players_range(app, idx))
652 return;
653
654 item = app->playlist.item[idx];
655
656 if(ui_playlist_item_dialog(app, &item))
657 {
658 app->playlist.item[idx] = item;
659 omnplay_playlist_draw_item(app, idx);
660 };
661 };
662
663 static void omnplay_ctl(omnplay_instance_t* app, control_buttons_t button)
664 {
665 int i, r;
666 int idx, start, stop;
667 omnplay_player_t *player;
668
669 pthread_mutex_lock(&app->playlist.lock);
670
671 idx = get_first_selected_item_playlist(app);
672
673 if(idx < 0)
674 {
675 pthread_mutex_unlock(&app->playlist.lock);
676 return;
677 };
678
679 fprintf(stderr, "cue: selected item is %d\n", idx);
680
681 if(get_playlist_block(app, idx, &start, &stop) < 0)
682 {
683 pthread_mutex_unlock(&app->playlist.lock);
684 return;
685 };
686
687 fprintf(stderr, "cue: range %d -> %d\n", start, stop);
688
689 player = get_player_at_pos(app, start);
690
691 if(!player)
692 {
693 pthread_mutex_unlock(&app->playlist.lock);
694 return;
695 };
696
697 pthread_mutex_lock(&app->players.lock);
698
699 if(BUTTON_PLAYER_STOP == button || BUTTON_PLAYER_CUE == button)
700 {
701 /* stop */
702 OmPlrStop((OmPlrHandle)player->handle);
703
704 /* detach previous clips */
705 player->playlist_length = -1;
706 OmPlrDetachAllClips((OmPlrHandle)player->handle);
707 };
708
709 if(BUTTON_PLAYER_CUE == button)
710 {
711 int o, c, p = 0;
712
713 /* Attach clips to timeline */
714 for(i = start, c = 0, o = 0; i <= stop; i++)
715 {
716 OmPlrClipInfo clip;
717
718 /* get clip info */
719 clip.maxMsTracks = 0;
720 clip.size = sizeof(clip);
721 r = OmPlrClipGetInfo((OmPlrHandle)player->handle, app->playlist.item[i].id, &clip);
722
723 if(!r)
724 {
725 unsigned int l;
726
727 fprintf(stderr, "OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
728 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
729
730 /* should we fix playlist clip timings */
731 if(!(
732 app->playlist.item[i].in >= clip.firstFrame &&
733 app->playlist.item[i].in + app->playlist.item[i].dur <= clip.lastFrame) ||
734 !app->playlist.item[i].dur)
735 {
736 fprintf(stderr, "cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
737 app->playlist.item[i].id,
738 app->playlist.item[i].in, app->playlist.item[i].dur,
739 clip.firstFrame, clip.lastFrame - clip.firstFrame);
740
741 app->playlist.item[i].in = clip.firstFrame;
742 app->playlist.item[i].dur = clip.lastFrame - clip.firstFrame;
743 omnplay_playlist_draw_item(app, i);
744 };
745
746 r = OmPlrAttach((OmPlrHandle)player->handle,
747 app->playlist.item[i].id,
748 app->playlist.item[i].in,
749 app->playlist.item[i].in + app->playlist.item[i].dur,
750 0, omPlrShiftModeAfter, &l);
751 };
752
753 if(r)
754 {
755 fprintf(stderr, "cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
756 app->playlist.item[i].omn_idx = -1;
757 app->playlist.item[i].omn_offset = -1;
758 }
759 else
760 {
761 app->playlist.item[i].omn_idx = c;
762 app->playlist.item[i].omn_offset = o;
763
764 /* save selected item offset */
765 if(i == idx) p = o;
766
767 c++;
768 o += app->playlist.item[i].dur;
769 };
770 };
771
772 if(c)
773 {
774 OmPlrStatus hs;
775
776 /* Set timeline min/max */
777 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
778 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
779
780 /* Set timeline position */
781 hs.minPos = 0;
782 hs.size = sizeof(OmPlrStatus);
783 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
784 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
785
786 /* setup loop */
787 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
788 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
789 else
790 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
791
792 player->playlist_start = start;
793 player->playlist_length = stop - start + 1;
794
795 /* Cue */
796 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
797 };
798 };
799
800 if(BUTTON_PLAYER_PLAY == button)
801 {
802 /* play */
803 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
804 };
805
806 if(BUTTON_PLAYER_PAUSE == button)
807 /* pause */
808 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
809
810 pthread_mutex_unlock(&app->players.lock);
811
812 pthread_mutex_unlock(&app->playlist.lock);
813 };
814
815 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
816 {
817 int sel, a, b;
818 GtkTreePath* path;
819 playlist_item_t item;
820
821 /* find insert position */
822 sel = get_first_selected_item_playlist(app);
823 if(sel < 0)
824 return;
825
826 if(dir < 0)
827 {
828 a = sel - 1;
829 b = sel;
830 sel = a;
831 }
832 else
833 {
834 a = sel;
835 b = sel + 1;
836 sel = b;
837 };
838
839 /* check for playing block */
840 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
841 return;
842
843 pthread_mutex_lock(&app->playlist.lock);
844 pthread_mutex_lock(&app->players.lock);
845
846 /* swap */
847 item = app->playlist.item[a];
848 app->playlist.item[a] = app->playlist.item[b];
849 app->playlist.item[b] = item;
850
851 /* rewite type */
852 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
853 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
854
855 /* redraw main items */
856 omnplay_playlist_draw_item(app, a);
857 omnplay_playlist_draw_item(app, b);
858
859 /* fix block types */
860 if(a)
861 {
862 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
863 OMNPLAY_PLAYLIST_BLOCK_END);
864 omnplay_playlist_draw_item(app, a - 1);
865 };
866 if(b + 1 < app->playlist.count)
867 {
868 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
869 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
870 omnplay_playlist_draw_item(app, b + 1);
871 };
872
873 /* select */
874 path = gtk_tree_path_new_from_indices(sel, -1);
875 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
876 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
877 gtk_tree_path_free(path);
878
879 pthread_mutex_unlock(&app->players.lock);
880 pthread_mutex_unlock(&app->playlist.lock);
881 };
882
883 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
884 {
885 switch(button)
886 {
887 case BUTTON_PLAYLIST_ITEM_ADD:
888 omnplay_playlist_item_add(app, 0);
889 break;
890 case BUTTON_PLAYLIST_ITEM_DEL:
891 omnplay_playlist_item_del(app);
892 break;
893 case BUTTON_PLAYLIST_ITEM_EDIT:
894 omnplay_playlist_item_edit(app);
895 break;
896 case BUTTON_PLAYLIST_LOAD:
897 omnplay_playlist_load(app);
898 break;
899 case BUTTON_PLAYLIST_SAVE:
900 omnplay_playlist_save(app);
901 break;
902 case BUTTON_PLAYLIST_BLOCK_SINGLE:
903 case BUTTON_PLAYLIST_BLOCK_LOOP:
904 omnplay_playlist_block(app, button);
905 break;
906 case BUTTON_PLAYLIST_ITEM_UP:
907 omnplay_playlist_item_swap(app, -1);
908 break;
909 case BUTTON_PLAYLIST_ITEM_DOWN:
910 omnplay_playlist_item_swap(app, +1);
911 break;
912 case BUTTON_PLAYER_CUE:
913 case BUTTON_PLAYER_PLAY:
914 case BUTTON_PLAYER_PAUSE:
915 case BUTTON_PLAYER_STOP:
916 omnplay_ctl(app, button);
917 break;
918 case BUTTON_LIBRARY_ADD:
919 break;
920 case BUTTON_LIBRARY_REFRESH:
921 omnplay_library_refresh(app);
922 break;
923 };
924
925 return TRUE;
926 };
927
928 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
929 {
930 int i;
931 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
932
933 for(i = 1; i < BUTTON_LAST; i++)
934 if(app->buttons[i] == button)
935 return omnplay_button_click(app, (control_buttons_t)i);
936
937 return FALSE;
938 };
939
940 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
941 {
942 omnplay_instance_t* app = (omnplay_instance_t*)data;
943
944 switch(event->keyval)
945 {
946 case GDK_C:
947 case GDK_c:
948 if(event->state & GDK_CONTROL_MASK)
949 {
950 fprintf(stderr, "CTRL+c\n");
951 return TRUE;
952 };
953 break;
954 case GDK_V:
955 case GDK_v:
956 if(event->state & GDK_CONTROL_MASK)
957 {
958 fprintf(stderr, "CTRL+v\n");
959 return TRUE;
960 };
961 break;
962 case GDK_X:
963 case GDK_x:
964 if(event->state & GDK_CONTROL_MASK)
965 {
966 fprintf(stderr, "CTRL+x\n");
967 return TRUE;
968 };
969 break;
970 case GDK_KEY_space:
971 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
972 return TRUE;
973 case GDK_KEY_Return:
974 omnplay_ctl(app, BUTTON_PLAYER_CUE);
975 return TRUE;
976 case GDK_KEY_Insert:
977 omnplay_playlist_item_add(app, 0);
978 return TRUE;
979 case GDK_KEY_Delete:
980 omnplay_playlist_item_del(app);
981 return TRUE;
982 case GDK_KEY_BackSpace:
983 omnplay_playlist_item_edit(app);
984 return TRUE;
985 };
986
987 return FALSE;
988 };
989
990 void omnplay_init(omnplay_instance_t* app)
991 {
992 int i;
993 pthread_mutexattr_t attr;
994
995 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
996
997 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
998 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
999
1000 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
1001 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
1002 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
1003
1004 /* create lock */
1005 pthread_mutex_init(&app->players.lock, &attr);
1006
1007 /* create a omneon status thread */
1008 for(i = 0; i < app->players.count; i++)
1009 pthread_create(&app->players.item[i].thread, NULL,
1010 omnplay_thread_proc, &app->players.item[i]);
1011
1012 /* create lock */
1013 pthread_mutex_init(&app->playlist.lock, &attr);
1014
1015 /* attach buttons click */
1016 for(i = 1; i < BUTTON_LAST; i++)
1017 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
1018 GTK_SIGNAL_FUNC( on_button_click), app );
1019
1020 /* create lock */
1021 pthread_mutex_init(&app->library.lock, &attr);
1022
1023 /* load library */
1024 omnplay_library_load(app);
1025 };
1026
1027 void omnplay_release(omnplay_instance_t* app)
1028 {
1029 int i;
1030 void* r;
1031
1032 app->f_exit = 1;
1033
1034 for(i = 0; i < app->players.count; i++)
1035 /* create a omneon status thread */
1036 pthread_join(app->players.item[i].thread, &r);
1037
1038 /* destroy lock */
1039 pthread_mutex_destroy(&app->players.lock);
1040
1041 /* destroy lock */
1042 pthread_mutex_destroy(&app->playlist.lock);
1043
1044 /* load library */
1045 omnplay_library_save(app);
1046
1047 /* destroy library lock */
1048 pthread_mutex_destroy(&app->library.lock);
1049 };