5858d1d05f7641fdd471f9c3aa9a77a41649ac4c
[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 g_print ("delete event occurred [start]\n");
88 gdk_threads_leave();
89 omnplay_release((omnplay_instance_t*)user_data);
90 gdk_threads_enter();
91 g_print ("delete event occurred [finish]\n");
92
93 return FALSE;
94 }
95
96 static void on_main_window_destroy( GtkWidget *widget, gpointer user_data )
97 {
98 g_print ("destroy occurred\n");
99 gtk_main_quit();
100 }
101
102 omnplay_instance_t* omnplay_create(int argc, char** argv)
103 {
104 int i, c;
105 omnplay_instance_t* app;
106
107 /* prepare application instance */
108 app = (omnplay_instance_t*)malloc(sizeof(omnplay_instance_t));
109 memset(app, 0, sizeof(omnplay_instance_t));
110
111 /* load parameters from command line */
112 if(!omnplay_opt(argc, argv, app) && app->players.count)
113 app->window = ui_omnplay(app);
114 else
115 omnplay_usage();
116
117 return app;
118 };
119
120 void omnplay_destroy(omnplay_instance_t* app)
121 {
122 free(app);
123 };
124
125 static int find_index_of_playlist_item(omnplay_instance_t* app, int start, int idx)
126 {
127 while(1)
128 {
129 if(app->playlist.item[start].omn_idx == idx)
130 return start;
131
132 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_END)
133 break;
134
135 start++;
136 };
137
138 return -1;
139 };
140
141 static void omnplay_update_status(omnplay_player_t* player, OmPlrStatus *prev , OmPlrStatus *curr)
142 {
143 int idx;
144 char tc_cur[32], tc_rem[32], state[32], status[32];
145 const char *clip;
146
147 if(curr)
148 {
149 frames2tc(curr->pos - curr->minPos, 25.0, tc_cur);
150 frames2tc(curr->maxPos - curr->pos, 25.0, tc_rem);
151 strcpy(status, "ONLINE");
152 clip = curr->currClipName;
153
154 switch(curr->state)
155 {
156 case omPlrStateStopped: strcpy(state, "STOPPED"); break;
157 case omPlrStateCuePlay: strcpy(state, "CUE_PLAY"); break;
158 case omPlrStatePlay: strcpy(state, "PLAY"); break;
159 case omPlrStateCueRecord: strcpy(state, "CUE_RECORD"); break;
160 case omPlrStateRecord: strcpy(state, "RECORD"); break;
161 };
162 }
163 else
164 {
165 tc_cur[0] = 0;
166 tc_rem[0] = 0;
167 clip = "";
168 state[0] = 0;
169 strcpy(status, "OFFLINE");
170 };
171
172 /* update status in status page */
173 gdk_threads_enter();
174 gtk_label_set_text(GTK_LABEL (player->label_tc_cur), tc_cur);
175 gtk_label_set_text(GTK_LABEL (player->label_tc_rem), tc_rem);
176 gtk_label_set_text(GTK_LABEL (player->label_state), state);
177 gtk_label_set_text(GTK_LABEL (player->label_status), status);
178 gtk_label_set_text(GTK_LABEL (player->label_clip), clip);
179 gdk_flush();
180 gdk_threads_leave();
181
182 /* update remaining time */
183 gdk_threads_enter();
184 pthread_mutex_lock(&player->app->playlist.lock);
185 pthread_mutex_lock(&player->app->players.lock);
186 if(curr->state == omPlrStatePlay || curr->state == omPlrStateCuePlay)
187 {
188 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
189 if(idx >= 0)
190 {
191 frames2tc(curr->currClipStartPos + curr->currClipLen - curr->pos, 25.0, tc_rem);
192 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
193 }
194 if(curr->currClipNum != prev->currClipNum && 1 != prev->numClips)
195 {
196 tc_rem[0] = 0;
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 }
202 else
203 {
204 tc_rem[0] = 0;
205 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
206 if(idx >= 0)
207 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
208 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
209 if(idx >= 0)
210 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
211 };
212 pthread_mutex_unlock(&player->app->players.lock);
213 pthread_mutex_unlock(&player->app->playlist.lock);
214 gdk_flush();
215 gdk_threads_leave();
216
217
218 memcpy(prev, curr, sizeof(OmPlrStatus));
219 };
220
221 static void* omnplay_thread_proc(void* data)
222 {
223 int r;
224 OmPlrStatus st_curr, st_prev;
225 omnplay_player_t* player = (omnplay_player_t*)data;
226
227 /* connect */
228 pthread_mutex_lock(&player->app->players.lock);
229 r = OmPlrOpen(player->host, player->name, (OmPlrHandle*)&player->handle);
230 pthread_mutex_unlock(&player->app->players.lock);
231 if(r)
232 {
233 fprintf(stderr, "ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
234 player->host, player->name, r);
235
236 return (void*)r;
237 };
238
239 /* setup to do not reconnect */
240 pthread_mutex_lock(&player->app->players.lock);
241 OmPlrSetRetryOpen((OmPlrHandle)player->handle, 0);
242 pthread_mutex_unlock(&player->app->players.lock);
243
244 /* setup directory */
245 if(player->app->players.path[0])
246 {
247 pthread_mutex_lock(&player->app->players.lock);
248 r = OmPlrClipSetDirectory((OmPlrHandle)player->handle, player->app->players.path);
249 pthread_mutex_unlock(&player->app->players.lock);
250
251 if(r)
252 {
253 fprintf(stderr, "ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
254 player->app->players.path, r);
255
256 pthread_mutex_lock(&player->app->players.lock);
257 OmPlrClose((OmPlrHandle)player->handle);
258 pthread_mutex_unlock(&player->app->players.lock);
259
260 return (void*)r;
261 };
262 };
263
264 /* endless loop */
265 for(r = 0 ; !player->app->f_exit && !r;)
266 {
267 /* sleep */
268 usleep(100000);
269
270 /* get status */
271 pthread_mutex_lock(&player->app->players.lock);
272 st_curr.size = sizeof(OmPlrStatus);
273 r = OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &st_curr);
274 pthread_mutex_unlock(&player->app->players.lock);
275
276 if(r)
277 fprintf(stderr, "ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r);
278 else
279 if(memcmp(&st_curr, &st_prev, sizeof(OmPlrStatus)))
280 omnplay_update_status(player, &st_prev , &st_curr);
281 };
282
283 pthread_mutex_lock(&player->app->players.lock);
284 OmPlrClose((OmPlrHandle)player->handle);
285 pthread_mutex_unlock(&player->app->players.lock);
286
287 return NULL;
288 };
289
290 void get_selected_items_playlist_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
291 {
292 int idx, *list = (int*)data;
293 gtk_tree_model_get(model, iter, 7, &idx, -1);
294 list[list[0] + 1] = idx;
295 list[0] = list[0] + 1;
296 };
297
298 static int* get_selected_items_playlist(omnplay_instance_t* app)
299 {
300 int* list = NULL;
301 GtkTreeSelection *selection;
302
303 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
304 if(selection)
305 {
306 list = (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
307 memset(list, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
308
309 gtk_tree_selection_selected_foreach(
310 selection,
311 get_selected_items_playlist_proc,
312 list);
313
314 if(!list[0])
315 {
316 free(list);
317 list = NULL;
318 };
319 };
320
321 return list;
322 };
323
324 static int idx_in_players_range(omnplay_instance_t* app, int idx)
325 {
326 int i, r = 0;
327
328 for(i = 0; i < app->players.count && !r; i++)
329 {
330 int a, b;
331
332 a = app->players.item[i].playlist_start;
333 b = app->players.item[i].playlist_length;
334
335 if(b <= 0)
336 continue;
337
338 b = a + b - 1;
339
340 if(idx >= a && idx <= b) r = 1;
341 };
342
343 return r;
344 };
345
346 static int idxs_in_players_range(omnplay_instance_t* app, int start, int stop)
347 {
348 int i, r = 0;
349
350 for(i = 0; i < app->players.count && !r; i++)
351 {
352 int a, b;
353
354 a = app->players.item[i].playlist_start;
355 b = app->players.item[i].playlist_length;
356
357 if(b <= 0)
358 continue;
359
360 b = a + b - 1;
361
362 #define IN_RANGE(A,B,C) (A <= C && C <= B)
363 if( IN_RANGE(a,b,start) ||
364 IN_RANGE(a,b,stop) ||
365 IN_RANGE(start,stop,a) ||
366 IN_RANGE(start,stop,b))
367 r = 1;
368 };
369
370 return r;
371 };
372
373 static void omnplay_playlist_block(omnplay_instance_t* app, control_buttons_t button)
374 {
375 int start, stop, r, i;
376 int* list = get_selected_items_playlist(app);
377
378 if(!list)
379 return;
380
381 pthread_mutex_lock(&app->playlist.lock);
382 pthread_mutex_lock(&app->players.lock);
383
384 start = list[1];
385 stop = list[list[0]];
386
387 if(!idxs_in_players_range(app, start, stop))
388 {
389 int loop = (button == BUTTON_PLAYLIST_BLOCK_LOOP)?OMNPLAY_PLAYLIST_BLOCK_LOOP:0;
390
391 /* update selected item */
392 for(i = start; i <= stop; i++)
393 {
394 int t = OMNPLAY_PLAYLIST_BLOCK_BODY | loop;
395
396 if(i == start) t |= OMNPLAY_PLAYLIST_BLOCK_BEGIN;
397 if(i == stop) t |= OMNPLAY_PLAYLIST_BLOCK_END;
398
399 app->playlist.item[i].type = (playlist_item_type_t)t;
400
401 omnplay_playlist_draw_item(app, i);
402 };
403
404 /* update border items */
405 if(start && !(app->playlist.item[start - 1].type & OMNPLAY_PLAYLIST_BLOCK_END))
406 {
407 app->playlist.item[start - 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_END
408 | app->playlist.item[start - 1].type);
409 omnplay_playlist_draw_item(app, start - 1);
410 };
411 if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN))
412 {
413 app->playlist.item[stop + 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_BEGIN
414 | app->playlist.item[stop + 1].type);
415 omnplay_playlist_draw_item(app, stop + 1);
416 };
417 }
418 else
419 fprintf(stderr, "omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
420 start, stop);
421
422 pthread_mutex_unlock(&app->players.lock);
423 pthread_mutex_unlock(&app->playlist.lock);
424
425 free(list);
426 };
427
428 static int get_first_selected_item_playlist(omnplay_instance_t* app)
429 {
430 int idx;
431 int* list = get_selected_items_playlist(app);
432 if(!list) return -1;
433 idx = list[1];
434 free(list);
435 return idx;
436 };
437
438 static int get_playlist_block(omnplay_instance_t* app, int idx, int* start_ptr, int* stop_ptr)
439 {
440 int start, stop;
441
442 for(start = idx; start >= 0; start--)
443 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
444 break;
445
446 for(stop = idx; stop < app->playlist.count; stop++)
447 if(app->playlist.item[stop].type & OMNPLAY_PLAYLIST_BLOCK_END)
448 break;
449
450 fprintf(stderr, "get_playlist_block: range %d -> %d\n", start, stop);
451
452 /* check block range */
453 if(start >= 0 && stop < app->playlist.count)
454 {
455 *start_ptr = start;
456 *stop_ptr = stop;
457 return (stop - start + 1);
458 };
459
460 return -1;
461 };
462
463 static omnplay_player_t *get_player_at_pos(omnplay_instance_t* app, int pos)
464 {
465 /* check player range */
466 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
467 return &app->players.item[app->playlist.item[pos].player];
468
469 return NULL;
470 };
471
472 static void omnplay_playlist_delete_items(omnplay_instance_t* app, int* idxs, int count)
473 {
474 int i, j, idx;
475 GtkTreePath* path;
476
477 pthread_mutex_lock(&app->playlist.lock);
478 pthread_mutex_lock(&app->players.lock);
479
480 for(j = 0; j < count; j++)
481 {
482 idx = idxs[j] - j;
483
484 /* fix block types */
485 if( app->playlist.item[idx].type != OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY &&
486 app->playlist.item[idx].type != OMNPLAY_PLAYLIST_ITEM_LOOP_BODY)
487 {
488 if(idx)
489 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
490 OMNPLAY_PLAYLIST_BLOCK_END);
491 if(idx + 1 < app->playlist.count)
492 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
493 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
494 };
495
496 /* shift playlist items */
497 memmove
498 (
499 &app->playlist.item[idx],
500 &app->playlist.item[idx + 1],
501 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
502 );
503
504 /* decrement items count */
505 app->playlist.count--;
506
507 /* increment servers indexes */
508 for(i = 0; i < app->players.count; i++)
509 if(app->players.item[i].playlist_start >= idx)
510 app->players.item[i].playlist_start--;
511
512
513 };
514
515 /* redraw playlist */
516 omnplay_playlist_draw(app);
517
518 /* select */
519 path = gtk_tree_path_new_from_indices(idxs[0], -1);
520 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
521 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
522 gtk_tree_path_free(path);
523
524
525 pthread_mutex_unlock(&app->players.lock);
526 pthread_mutex_unlock(&app->playlist.lock);
527 };
528
529 static void omnplay_playlist_item_del(omnplay_instance_t* app)
530 {
531 int i, idx, c;
532 int *list, *list2;
533
534 list = get_selected_items_playlist(app);
535 if(!list) return;
536
537 list2 = (int*)malloc(sizeof(int) * list[0]);
538
539 for(i = 0, c = 0; i < list[0]; i++)
540 {
541 /* check for playing block */
542 if(idx_in_players_range(app, list[i + 1]))
543 continue;
544
545 /* save index */
546 list2[c++] = list[i + 1];
547 };
548
549 if(c)
550 omnplay_playlist_delete_items(app, list2, c);
551
552 free(list2);
553 free(list);
554 };
555
556 static int omnplay_playlist_insert_check(omnplay_instance_t* app, int idx, playlist_item_type_t* t)
557 {
558 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
559
560 /* before or after playlist */
561 if(!idx || idx == app->playlist.count)
562 return 1;
563
564 /* check for block borders */
565 if( app->playlist.item[idx - 1].type & OMNPLAY_PLAYLIST_BLOCK_END &&
566 app->playlist.item[idx + 0].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
567 return 1;
568
569 /* check for playing block */
570 if(idx_in_players_range(app, idx))
571 return 0;
572
573 if(app->playlist.item[idx].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
574 *t = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY;
575 else
576 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY;
577
578 return 1;
579 };
580
581 static void omnplay_playlist_insert_items(omnplay_instance_t* app, int idx,
582 playlist_item_t* items, int count)
583 {
584 int i;
585 GtkTreePath* path;
586
587 pthread_mutex_lock(&app->playlist.lock);
588 pthread_mutex_lock(&app->players.lock);
589
590 /* shift playlist items */
591 memmove
592 (
593 &app->playlist.item[idx + count],
594 &app->playlist.item[idx],
595 (app->playlist.count - idx) * sizeof(playlist_item_t)
596 );
597
598 /* copy new items */
599 memcpy
600 (
601 &app->playlist.item[idx],
602 items,
603 count * sizeof(playlist_item_t)
604 );
605
606 /* increment servers indexes */
607 for(i = 0; i < app->players.count; i++)
608 if(app->players.item[i].playlist_start >= idx)
609 app->players.item[i].playlist_start += idx;
610
611 /* increment items count */
612 app->playlist.count += count;
613
614 /* redraw playlist */
615 omnplay_playlist_draw(app);
616
617 /* select */
618 path = gtk_tree_path_new_from_indices(idx + count, -1);
619 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
620 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
621 gtk_tree_path_free(path);
622
623 pthread_mutex_unlock(&app->players.lock);
624 pthread_mutex_unlock(&app->playlist.lock);
625 };
626
627 static void omnplay_playlist_item_add(omnplay_instance_t* app, int after)
628 {
629 int idx;
630 playlist_item_t item;
631 playlist_item_type_t t;
632
633 /* find insert position */
634 idx = get_first_selected_item_playlist(app);
635 if(idx < 0)
636 idx = 0;
637 else
638 idx += (after)?1:0;
639
640 if(!omnplay_playlist_insert_check(app, idx, &t))
641 return;
642
643 fprintf(stderr, "allowed insert into idx=%d\n", idx);
644
645 /* clear item */
646 memset(&item, 0, sizeof(playlist_item_t));
647 if(ui_playlist_item_dialog(app, &item))
648 {
649 omnplay_library_normalize_item(app, &item);
650 item.type = t;
651 omnplay_playlist_insert_items(app, idx, &item, 1);
652 };
653 };
654
655 static void omnplay_playlist_item_edit(omnplay_instance_t* app)
656 {
657 int idx;
658 playlist_item_t item;
659
660 /* find insert position */
661 idx = get_first_selected_item_playlist(app);
662
663 if(idx < 0)
664 return;
665
666 /* check for playing block */
667 if(idx_in_players_range(app, idx))
668 return;
669
670 item = app->playlist.item[idx];
671
672 if(ui_playlist_item_dialog(app, &item))
673 {
674 omnplay_library_normalize_item(app, &item);
675 app->playlist.item[idx] = item;
676 omnplay_playlist_draw_item(app, idx);
677 };
678 };
679
680 static void omnplay_ctl(omnplay_instance_t* app, control_buttons_t button)
681 {
682 int i, r;
683 int idx, start, stop;
684 omnplay_player_t *player;
685
686 pthread_mutex_lock(&app->playlist.lock);
687
688 idx = get_first_selected_item_playlist(app);
689
690 if(idx < 0)
691 {
692 pthread_mutex_unlock(&app->playlist.lock);
693 return;
694 };
695
696 fprintf(stderr, "cue: selected item is %d\n", idx);
697
698 if(get_playlist_block(app, idx, &start, &stop) < 0)
699 {
700 pthread_mutex_unlock(&app->playlist.lock);
701 return;
702 };
703
704 fprintf(stderr, "cue: range %d -> %d\n", start, stop);
705
706 player = get_player_at_pos(app, start);
707
708 if(!player)
709 {
710 pthread_mutex_unlock(&app->playlist.lock);
711 return;
712 };
713
714 pthread_mutex_lock(&app->players.lock);
715
716 if(BUTTON_PLAYER_STOP == button || BUTTON_PLAYER_CUE == button)
717 {
718 /* stop */
719 OmPlrStop((OmPlrHandle)player->handle);
720
721 /* detach previous clips */
722 player->playlist_length = -1;
723 OmPlrDetachAllClips((OmPlrHandle)player->handle);
724 };
725
726 if(BUTTON_PLAYER_CUE == button)
727 {
728 int o, c, p = 0;
729
730 /* Attach clips to timeline */
731 for(i = start, c = 0, o = 0; i <= stop; i++)
732 {
733 OmPlrClipInfo clip;
734
735 /* get clip info */
736 clip.maxMsTracks = 0;
737 clip.size = sizeof(clip);
738 r = OmPlrClipGetInfo((OmPlrHandle)player->handle, app->playlist.item[i].id, &clip);
739
740 if(!r)
741 {
742 unsigned int l;
743
744 fprintf(stderr, "OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
745 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
746
747 /* should we fix playlist clip timings */
748 if(!(
749 app->playlist.item[i].in >= clip.firstFrame &&
750 app->playlist.item[i].in + app->playlist.item[i].dur <= clip.lastFrame) ||
751 !app->playlist.item[i].dur)
752 {
753 fprintf(stderr, "cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
754 app->playlist.item[i].id,
755 app->playlist.item[i].in, app->playlist.item[i].dur,
756 clip.firstFrame, clip.lastFrame - clip.firstFrame);
757
758 app->playlist.item[i].in = clip.firstFrame;
759 app->playlist.item[i].dur = clip.lastFrame - clip.firstFrame;
760 omnplay_playlist_draw_item(app, i);
761 };
762
763 r = OmPlrAttach((OmPlrHandle)player->handle,
764 app->playlist.item[i].id,
765 app->playlist.item[i].in,
766 app->playlist.item[i].in + app->playlist.item[i].dur,
767 0, omPlrShiftModeAfter, &l);
768 };
769
770 if(r)
771 {
772 fprintf(stderr, "cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
773 app->playlist.item[i].omn_idx = -1;
774 app->playlist.item[i].omn_offset = -1;
775 app->playlist.item[i].error |= PLAYLIST_ITEM_ERROR_CUE;
776 }
777 else
778 {
779 app->playlist.item[i].omn_idx = c;
780 app->playlist.item[i].omn_offset = o;
781 app->playlist.item[i].error &= 0xF ^ PLAYLIST_ITEM_ERROR_CUE;
782
783 /* save selected item offset */
784 if(i == idx) p = o;
785
786 c++;
787 o += app->playlist.item[i].dur;
788 };
789 };
790
791 if(c)
792 {
793 OmPlrStatus hs;
794
795 /* Set timeline min/max */
796 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
797 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
798
799 /* Set timeline position */
800 hs.minPos = 0;
801 hs.size = sizeof(OmPlrStatus);
802 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
803 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
804
805 /* setup loop */
806 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
807 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
808 else
809 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
810
811 player->playlist_start = start;
812 player->playlist_length = stop - start + 1;
813
814 /* Cue */
815 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
816 };
817 };
818
819 if(BUTTON_PLAYER_PLAY == button)
820 {
821 /* play */
822 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
823 };
824
825 if(BUTTON_PLAYER_PAUSE == button)
826 /* pause */
827 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
828
829 pthread_mutex_unlock(&app->players.lock);
830
831 pthread_mutex_unlock(&app->playlist.lock);
832 };
833
834 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
835 {
836 int sel, a, b, e = 1;
837 GtkTreePath* path;
838 playlist_item_t item;
839
840 /* find insert position */
841 sel = get_first_selected_item_playlist(app);
842 if(sel < 0)
843 return;
844
845 if(dir < 0)
846 {
847 a = sel - 1;
848 b = sel;
849 sel = a;
850 }
851 else
852 {
853 a = sel;
854 b = sel + 1;
855 sel = b;
856 };
857
858 /* check for playing block */
859 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
860 return;
861
862 pthread_mutex_lock(&app->playlist.lock);
863 pthread_mutex_lock(&app->players.lock);
864
865 /* swap */
866 item = app->playlist.item[a];
867 app->playlist.item[a] = app->playlist.item[b];
868 app->playlist.item[b] = item;
869
870 /* rewite type */
871 if(app->playlist.item[a].type != app->playlist.item[b].type)
872 {
873 e = 0;
874 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
875 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
876 };
877
878 /* redraw main items */
879 omnplay_playlist_draw_item(app, a);
880 omnplay_playlist_draw_item(app, b);
881
882 /* fix block types */
883 if(a && !e)
884 {
885 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
886 OMNPLAY_PLAYLIST_BLOCK_END);
887 omnplay_playlist_draw_item(app, a - 1);
888 };
889 if(b + 1 < app->playlist.count && !e)
890 {
891 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
892 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
893 omnplay_playlist_draw_item(app, b + 1);
894 };
895
896 /* select */
897 path = gtk_tree_path_new_from_indices(sel, -1);
898 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
899 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
900 gtk_tree_path_free(path);
901
902 pthread_mutex_unlock(&app->players.lock);
903 pthread_mutex_unlock(&app->playlist.lock);
904 };
905
906 static void omnplay_library_add(omnplay_instance_t* app, int after)
907 {
908 int idx, c, i;
909 playlist_item_t* items;
910 playlist_item_type_t t;
911
912 /* find insert position */
913 idx = get_first_selected_item_playlist(app);
914 if(idx < 0)
915 idx = 0;
916 else
917 idx += (after)?1:0;
918
919 if(!omnplay_playlist_insert_check(app, idx, &t))
920 return;
921
922 items = omnplay_library_get_selected(app, &c);
923
924 /* clear item */
925 if(items)
926 {
927 for(i = 0; i < c; i++)
928 {
929 items[i].type = t;
930 items[i].error = 0;
931 };
932 omnplay_playlist_insert_items(app, idx, items, c);
933 };
934 };
935
936
937 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
938 {
939 switch(button)
940 {
941 case BUTTON_PLAYLIST_ITEM_ADD:
942 omnplay_playlist_item_add(app, 0);
943 break;
944 case BUTTON_PLAYLIST_ITEM_DEL:
945 omnplay_playlist_item_del(app);
946 break;
947 case BUTTON_PLAYLIST_ITEM_EDIT:
948 omnplay_playlist_item_edit(app);
949 break;
950 case BUTTON_PLAYLIST_LOAD:
951 omnplay_playlist_load(app);
952 break;
953 case BUTTON_PLAYLIST_SAVE:
954 omnplay_playlist_save(app);
955 break;
956 case BUTTON_PLAYLIST_BLOCK_SINGLE:
957 case BUTTON_PLAYLIST_BLOCK_LOOP:
958 omnplay_playlist_block(app, button);
959 break;
960 case BUTTON_PLAYLIST_ITEM_UP:
961 omnplay_playlist_item_swap(app, -1);
962 break;
963 case BUTTON_PLAYLIST_ITEM_DOWN:
964 omnplay_playlist_item_swap(app, +1);
965 break;
966 case BUTTON_PLAYER_CUE:
967 case BUTTON_PLAYER_PLAY:
968 case BUTTON_PLAYER_PAUSE:
969 case BUTTON_PLAYER_STOP:
970 omnplay_ctl(app, button);
971 break;
972 case BUTTON_LIBRARY_ADD:
973 omnplay_library_add(app, 0);
974 break;
975 case BUTTON_LIBRARY_REFRESH:
976 omnplay_library_refresh(app);
977 break;
978 case BUTTON_LIBRARY_FIND:
979 omnplay_library_search(app, 0);
980 break;
981 case BUTTON_LIBRARY_FIND_NEXT:
982 omnplay_library_search(app, 1);
983 break;
984 };
985
986 return TRUE;
987 };
988
989 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
990 {
991 int i;
992 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
993
994 for(i = 1; i < BUTTON_LAST; i++)
995 if(app->buttons[i] == button)
996 return omnplay_button_click(app, (control_buttons_t)i);
997
998 return FALSE;
999 };
1000
1001 static void omnplay_playlist_item_copy(omnplay_instance_t* app)
1002 {
1003 int *list, i;
1004
1005 list = get_selected_items_playlist(app);
1006 if(!list) return;
1007
1008 for(i = 0; i < list[0]; i++)
1009 app->clipboard.item[i] = app->playlist.item[list[i + 1]];
1010 app->clipboard.count = list[0];
1011
1012 free(list);
1013 };
1014
1015 static void omnplay_playlist_item_paste(omnplay_instance_t* app, int after)
1016 {
1017 int idx, i;
1018 playlist_item_t* items;
1019 playlist_item_type_t t;
1020
1021 /* find insert position */
1022 idx = get_first_selected_item_playlist(app);
1023 if(idx < 0)
1024 idx = 0;
1025 else
1026 idx += (after)?1:0;
1027
1028 if(!omnplay_playlist_insert_check(app, idx, &t))
1029 return;
1030
1031 /* clear item */
1032 if(app->clipboard.count)
1033 {
1034 for(i = 0; i < app->clipboard.count; i++)
1035 {
1036 app->clipboard.item[i].type = t;
1037 app->clipboard.item[i].error = 0;
1038 };
1039 omnplay_playlist_insert_items(app, idx, app->clipboard.item, app->clipboard.count);
1040 };
1041 };
1042
1043 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1044 {
1045 omnplay_instance_t* app = (omnplay_instance_t*)data;
1046
1047 switch(event->keyval)
1048 {
1049 case GDK_C:
1050 case GDK_c:
1051 if(event->state & GDK_CONTROL_MASK)
1052 {
1053 omnplay_playlist_item_copy(app);
1054 return TRUE;
1055 };
1056 break;
1057 case GDK_V:
1058 case GDK_v:
1059 if(event->state & GDK_CONTROL_MASK)
1060 {
1061 omnplay_playlist_item_paste(app, 0);
1062 return TRUE;
1063 };
1064 break;
1065 case GDK_X:
1066 case GDK_x:
1067 if(event->state & GDK_CONTROL_MASK)
1068 {
1069 omnplay_playlist_item_copy(app);
1070 omnplay_playlist_item_del(app);
1071 return TRUE;
1072 };
1073 break;
1074 case GDK_S:
1075 case GDK_s:
1076 if(event->state & GDK_CONTROL_MASK)
1077 {
1078 omnplay_playlist_save(app);
1079 return TRUE;
1080 };
1081 break;
1082 case GDK_O:
1083 case GDK_o:
1084 if(event->state & GDK_CONTROL_MASK)
1085 {
1086 omnplay_playlist_load(app);
1087 return TRUE;
1088 };
1089 break;
1090 case GDK_KEY_space:
1091 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
1092 return TRUE;
1093 case GDK_KEY_Return:
1094 omnplay_ctl(app, BUTTON_PLAYER_CUE);
1095 return TRUE;
1096 case GDK_KEY_Insert:
1097 omnplay_playlist_item_add(app, 0);
1098 return TRUE;
1099 case GDK_KEY_Delete:
1100 omnplay_playlist_item_del(app);
1101 return TRUE;
1102 case GDK_KEY_BackSpace:
1103 omnplay_playlist_item_edit(app);
1104 return TRUE;
1105 };
1106
1107 return FALSE;
1108 };
1109
1110 static gboolean on_library_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1111 {
1112 omnplay_instance_t* app = (omnplay_instance_t*)data;
1113
1114 switch(event->keyval)
1115 {
1116 case GDK_C:
1117 case GDK_c:
1118 if(event->state & GDK_CONTROL_MASK)
1119 {
1120 int count;
1121 playlist_item_t* items;
1122
1123 items = omnplay_library_get_selected(app, &count);
1124
1125 if(items)
1126 {
1127 int i;
1128
1129 for(i = 0; i < count; i++)
1130 app->clipboard.item[i] = items[i];
1131
1132 app->clipboard.count = count;
1133 };
1134
1135 return TRUE;
1136 };
1137 break;
1138 case GDK_V:
1139 case GDK_v:
1140 if(event->state & GDK_CONTROL_MASK)
1141 {
1142 fprintf(stderr, "CTRL+v\n");
1143 return TRUE;
1144 };
1145 break;
1146 case GDK_X:
1147 case GDK_x:
1148 if(event->state & GDK_CONTROL_MASK)
1149 {
1150 fprintf(stderr, "CTRL+x\n");
1151 return TRUE;
1152 };
1153 break;
1154 };
1155
1156 return FALSE;
1157 };
1158
1159 static gboolean on_library_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1160 {
1161 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1162 {
1163 omnplay_library_add((omnplay_instance_t* )data, 0);
1164 return TRUE;
1165 };
1166
1167 return FALSE;
1168 };
1169
1170 static gboolean on_playlist_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1171 {
1172 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1173 {
1174 omnplay_ctl((omnplay_instance_t* )data, BUTTON_PLAYER_CUE);
1175 return TRUE;
1176 };
1177
1178 return FALSE;
1179 };
1180
1181 void omnplay_init(omnplay_instance_t* app)
1182 {
1183 int i;
1184 pthread_mutexattr_t attr;
1185
1186 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1187
1188 gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event",
1189 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
1190
1191 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
1192 GTK_SIGNAL_FUNC(on_main_window_destroy), app);
1193
1194 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
1195 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
1196 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
1197
1198 gtk_widget_add_events(app->library_grid, GDK_BUTTON_PRESS_MASK);
1199 gtk_signal_connect(GTK_OBJECT(app->library_grid), "key-press-event",
1200 GTK_SIGNAL_FUNC(on_library_grid_key), app);
1201
1202 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "button-press-event",
1203 GTK_SIGNAL_FUNC(on_playlist_grid_button), app);
1204
1205 gtk_signal_connect(GTK_OBJECT(app->library_grid), "button-press-event",
1206 GTK_SIGNAL_FUNC(on_library_grid_button), app);
1207
1208 /* create lock */
1209 pthread_mutex_init(&app->players.lock, &attr);
1210
1211 /* create a omneon status thread */
1212 for(i = 0; i < app->players.count; i++)
1213 pthread_create(&app->players.item[i].thread, NULL,
1214 omnplay_thread_proc, &app->players.item[i]);
1215
1216 /* create lock */
1217 pthread_mutex_init(&app->playlist.lock, &attr);
1218
1219 /* attach buttons click */
1220 for(i = 1; i < BUTTON_LAST; i++)
1221 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
1222 GTK_SIGNAL_FUNC( on_button_click), app );
1223
1224 /* create lock */
1225 pthread_mutex_init(&app->library.lock, &attr);
1226
1227 /* load library */
1228 omnplay_library_load(app);
1229 };
1230
1231 void omnplay_release(omnplay_instance_t* app)
1232 {
1233 int i;
1234 void* r;
1235
1236 app->f_exit = 1;
1237
1238 for(i = 0; i < app->players.count; i++)
1239 /* create a omneon status thread */
1240 pthread_join(app->players.item[i].thread, &r);
1241
1242 /* destroy lock */
1243 pthread_mutex_destroy(&app->players.lock);
1244
1245 /* destroy lock */
1246 pthread_mutex_destroy(&app->playlist.lock);
1247
1248 /* load library */
1249 omnplay_library_save(app);
1250
1251 /* destroy library lock */
1252 pthread_mutex_destroy(&app->library.lock);
1253 };
1254
1255 void omnplay_playlist_normalize(omnplay_instance_t* app)
1256 {
1257 int i;
1258
1259 /* normalize playlist */
1260 for(i = 0; i < app->playlist.count; i++)
1261 if(omnplay_library_normalize_item(app, &app->playlist.item[i]))
1262 omnplay_playlist_draw_item(app, i);
1263 };