typo fix
[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(idx)
486 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
487 OMNPLAY_PLAYLIST_BLOCK_END);
488 if(idx + 1 < app->playlist.count)
489 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
490 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
491
492 /* shift playlist items */
493 memmove
494 (
495 &app->playlist.item[idx],
496 &app->playlist.item[idx + 1],
497 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
498 );
499
500 /* decrement items count */
501 app->playlist.count--;
502
503 /* increment servers indexes */
504 for(i = 0; i < app->players.count; i++)
505 if(app->players.item[i].playlist_start >= idx)
506 app->players.item[i].playlist_start--;
507
508
509 };
510
511 /* redraw playlist */
512 omnplay_playlist_draw(app);
513
514 /* select */
515 path = gtk_tree_path_new_from_indices(idxs[0], -1);
516 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
517 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
518 gtk_tree_path_free(path);
519
520
521 pthread_mutex_unlock(&app->players.lock);
522 pthread_mutex_unlock(&app->playlist.lock);
523 };
524
525 static void omnplay_playlist_item_del(omnplay_instance_t* app)
526 {
527 int i, idx, c;
528 int *list, *list2;
529
530 list = get_selected_items_playlist(app);
531 if(!list) return;
532
533 list2 = (int*)malloc(sizeof(int) * list[0]);
534
535 for(i = 0, c = 0; i < list[0]; i++)
536 {
537 /* check for playing block */
538 if(idx_in_players_range(app, list[i + 1]))
539 continue;
540
541 /* save index */
542 list2[c++] = list[i + 1];
543 };
544
545 if(c)
546 omnplay_playlist_delete_items(app, list2, c);
547
548 free(list2);
549 free(list);
550 };
551
552 static int omnplay_playlist_insert_check(omnplay_instance_t* app, int idx, playlist_item_type_t* t)
553 {
554 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
555
556 /* before or after playlist */
557 if(!idx || idx == app->playlist.count)
558 return 1;
559
560 /* check for block borders */
561 if( app->playlist.item[idx - 1].type & OMNPLAY_PLAYLIST_BLOCK_END &&
562 app->playlist.item[idx + 0].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
563 return 1;
564
565 /* check for playing block */
566 if(idx_in_players_range(app, idx))
567 return 0;
568
569 if(app->playlist.item[idx].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
570 *t = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY;
571 else
572 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY;
573
574 return 1;
575 };
576
577 static void omnplay_playlist_insert_items(omnplay_instance_t* app, int idx,
578 playlist_item_t* items, int count)
579 {
580 int i;
581 GtkTreePath* path;
582
583 pthread_mutex_lock(&app->playlist.lock);
584 pthread_mutex_lock(&app->players.lock);
585
586 /* shift playlist items */
587 memmove
588 (
589 &app->playlist.item[idx + count],
590 &app->playlist.item[idx],
591 (app->playlist.count - idx) * sizeof(playlist_item_t)
592 );
593
594 /* copy new items */
595 memcpy
596 (
597 &app->playlist.item[idx],
598 items,
599 count * sizeof(playlist_item_t)
600 );
601
602 /* increment servers indexes */
603 for(i = 0; i < app->players.count; i++)
604 if(app->players.item[i].playlist_start >= idx)
605 app->players.item[i].playlist_start += idx;
606
607 /* increment items count */
608 app->playlist.count += count;
609
610 /* redraw playlist */
611 omnplay_playlist_draw(app);
612
613 /* select */
614 path = gtk_tree_path_new_from_indices(idx + count, -1);
615 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
616 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
617 gtk_tree_path_free(path);
618
619 pthread_mutex_unlock(&app->players.lock);
620 pthread_mutex_unlock(&app->playlist.lock);
621 };
622
623 static void omnplay_playlist_item_add(omnplay_instance_t* app, int after)
624 {
625 int idx;
626 playlist_item_t item;
627 playlist_item_type_t t;
628
629 /* find insert position */
630 idx = get_first_selected_item_playlist(app);
631 if(idx < 0)
632 idx = 0;
633 else
634 idx += (after)?1:0;
635
636 if(!omnplay_playlist_insert_check(app, idx, &t))
637 return;
638
639 fprintf(stderr, "allowed insert into idx=%d\n", idx);
640
641 /* clear item */
642 memset(&item, 0, sizeof(playlist_item_t));
643 if(ui_playlist_item_dialog(app, &item))
644 {
645 omnplay_library_normalize_item(app, &item);
646 item.type = t;
647 omnplay_playlist_insert_items(app, idx, &item, 1);
648 };
649 };
650
651 static void omnplay_playlist_item_edit(omnplay_instance_t* app)
652 {
653 int idx;
654 playlist_item_t item;
655
656 /* find insert position */
657 idx = get_first_selected_item_playlist(app);
658
659 if(idx < 0)
660 return;
661
662 /* check for playing block */
663 if(idx_in_players_range(app, idx))
664 return;
665
666 item = app->playlist.item[idx];
667
668 if(ui_playlist_item_dialog(app, &item))
669 {
670 omnplay_library_normalize_item(app, &item);
671 app->playlist.item[idx] = item;
672 omnplay_playlist_draw_item(app, idx);
673 };
674 };
675
676 static void omnplay_ctl(omnplay_instance_t* app, control_buttons_t button)
677 {
678 int i, r;
679 int idx, start, stop;
680 omnplay_player_t *player;
681
682 pthread_mutex_lock(&app->playlist.lock);
683
684 idx = get_first_selected_item_playlist(app);
685
686 if(idx < 0)
687 {
688 pthread_mutex_unlock(&app->playlist.lock);
689 return;
690 };
691
692 fprintf(stderr, "cue: selected item is %d\n", idx);
693
694 if(get_playlist_block(app, idx, &start, &stop) < 0)
695 {
696 pthread_mutex_unlock(&app->playlist.lock);
697 return;
698 };
699
700 fprintf(stderr, "cue: range %d -> %d\n", start, stop);
701
702 player = get_player_at_pos(app, start);
703
704 if(!player)
705 {
706 pthread_mutex_unlock(&app->playlist.lock);
707 return;
708 };
709
710 pthread_mutex_lock(&app->players.lock);
711
712 if(BUTTON_PLAYER_STOP == button || BUTTON_PLAYER_CUE == button)
713 {
714 /* stop */
715 OmPlrStop((OmPlrHandle)player->handle);
716
717 /* detach previous clips */
718 player->playlist_length = -1;
719 OmPlrDetachAllClips((OmPlrHandle)player->handle);
720 };
721
722 if(BUTTON_PLAYER_CUE == button)
723 {
724 int o, c, p = 0;
725
726 /* Attach clips to timeline */
727 for(i = start, c = 0, o = 0; i <= stop; i++)
728 {
729 OmPlrClipInfo clip;
730
731 /* get clip info */
732 clip.maxMsTracks = 0;
733 clip.size = sizeof(clip);
734 r = OmPlrClipGetInfo((OmPlrHandle)player->handle, app->playlist.item[i].id, &clip);
735
736 if(!r)
737 {
738 unsigned int l;
739
740 fprintf(stderr, "OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
741 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
742
743 /* should we fix playlist clip timings */
744 if(!(
745 app->playlist.item[i].in >= clip.firstFrame &&
746 app->playlist.item[i].in + app->playlist.item[i].dur <= clip.lastFrame) ||
747 !app->playlist.item[i].dur)
748 {
749 fprintf(stderr, "cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
750 app->playlist.item[i].id,
751 app->playlist.item[i].in, app->playlist.item[i].dur,
752 clip.firstFrame, clip.lastFrame - clip.firstFrame);
753
754 app->playlist.item[i].in = clip.firstFrame;
755 app->playlist.item[i].dur = clip.lastFrame - clip.firstFrame;
756 omnplay_playlist_draw_item(app, i);
757 };
758
759 r = OmPlrAttach((OmPlrHandle)player->handle,
760 app->playlist.item[i].id,
761 app->playlist.item[i].in,
762 app->playlist.item[i].in + app->playlist.item[i].dur,
763 0, omPlrShiftModeAfter, &l);
764 };
765
766 if(r)
767 {
768 fprintf(stderr, "cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
769 app->playlist.item[i].omn_idx = -1;
770 app->playlist.item[i].omn_offset = -1;
771 app->playlist.item[i].error |= PLAYLIST_ITEM_ERROR_CUE;
772 }
773 else
774 {
775 app->playlist.item[i].omn_idx = c;
776 app->playlist.item[i].omn_offset = o;
777 app->playlist.item[i].error &= 0xF ^ PLAYLIST_ITEM_ERROR_CUE;
778
779 /* save selected item offset */
780 if(i == idx) p = o;
781
782 c++;
783 o += app->playlist.item[i].dur;
784 };
785 };
786
787 if(c)
788 {
789 OmPlrStatus hs;
790
791 /* Set timeline min/max */
792 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
793 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
794
795 /* Set timeline position */
796 hs.minPos = 0;
797 hs.size = sizeof(OmPlrStatus);
798 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
799 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
800
801 /* setup loop */
802 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
803 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
804 else
805 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
806
807 player->playlist_start = start;
808 player->playlist_length = stop - start + 1;
809
810 /* Cue */
811 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
812 };
813 };
814
815 if(BUTTON_PLAYER_PLAY == button)
816 {
817 /* play */
818 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
819 };
820
821 if(BUTTON_PLAYER_PAUSE == button)
822 /* pause */
823 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
824
825 pthread_mutex_unlock(&app->players.lock);
826
827 pthread_mutex_unlock(&app->playlist.lock);
828 };
829
830 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
831 {
832 int sel, a, b, e = 1;
833 GtkTreePath* path;
834 playlist_item_t item;
835
836 /* find insert position */
837 sel = get_first_selected_item_playlist(app);
838 if(sel < 0)
839 return;
840
841 if(dir < 0)
842 {
843 a = sel - 1;
844 b = sel;
845 sel = a;
846 }
847 else
848 {
849 a = sel;
850 b = sel + 1;
851 sel = b;
852 };
853
854 /* check for playing block */
855 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
856 return;
857
858 pthread_mutex_lock(&app->playlist.lock);
859 pthread_mutex_lock(&app->players.lock);
860
861 /* swap */
862 item = app->playlist.item[a];
863 app->playlist.item[a] = app->playlist.item[b];
864 app->playlist.item[b] = item;
865
866 /* rewite type */
867 if(app->playlist.item[a].type != app->playlist.item[b].type)
868 {
869 e = 0;
870 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
871 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
872 };
873
874 /* redraw main items */
875 omnplay_playlist_draw_item(app, a);
876 omnplay_playlist_draw_item(app, b);
877
878 /* fix block types */
879 if(a && !e)
880 {
881 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
882 OMNPLAY_PLAYLIST_BLOCK_END);
883 omnplay_playlist_draw_item(app, a - 1);
884 };
885 if(b + 1 < app->playlist.count && !e)
886 {
887 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
888 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
889 omnplay_playlist_draw_item(app, b + 1);
890 };
891
892 /* select */
893 path = gtk_tree_path_new_from_indices(sel, -1);
894 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
895 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
896 gtk_tree_path_free(path);
897
898 pthread_mutex_unlock(&app->players.lock);
899 pthread_mutex_unlock(&app->playlist.lock);
900 };
901
902 static void omnplay_library_add(omnplay_instance_t* app, int after)
903 {
904 int idx, c, i;
905 playlist_item_t* items;
906 playlist_item_type_t t;
907
908 /* find insert position */
909 idx = get_first_selected_item_playlist(app);
910 if(idx < 0)
911 idx = 0;
912 else
913 idx += (after)?1:0;
914
915 if(!omnplay_playlist_insert_check(app, idx, &t))
916 return;
917
918 items = omnplay_library_get_selected(app, &c);
919
920 /* clear item */
921 if(items)
922 {
923 for(i = 0; i < c; i++)
924 {
925 items[i].type = t;
926 items[i].error = 0;
927 };
928 omnplay_playlist_insert_items(app, idx, items, c);
929 };
930 };
931
932
933 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
934 {
935 switch(button)
936 {
937 case BUTTON_PLAYLIST_ITEM_ADD:
938 omnplay_playlist_item_add(app, 0);
939 break;
940 case BUTTON_PLAYLIST_ITEM_DEL:
941 omnplay_playlist_item_del(app);
942 break;
943 case BUTTON_PLAYLIST_ITEM_EDIT:
944 omnplay_playlist_item_edit(app);
945 break;
946 case BUTTON_PLAYLIST_LOAD:
947 omnplay_playlist_load(app);
948 break;
949 case BUTTON_PLAYLIST_SAVE:
950 omnplay_playlist_save(app);
951 break;
952 case BUTTON_PLAYLIST_BLOCK_SINGLE:
953 case BUTTON_PLAYLIST_BLOCK_LOOP:
954 omnplay_playlist_block(app, button);
955 break;
956 case BUTTON_PLAYLIST_ITEM_UP:
957 omnplay_playlist_item_swap(app, -1);
958 break;
959 case BUTTON_PLAYLIST_ITEM_DOWN:
960 omnplay_playlist_item_swap(app, +1);
961 break;
962 case BUTTON_PLAYER_CUE:
963 case BUTTON_PLAYER_PLAY:
964 case BUTTON_PLAYER_PAUSE:
965 case BUTTON_PLAYER_STOP:
966 omnplay_ctl(app, button);
967 break;
968 case BUTTON_LIBRARY_ADD:
969 omnplay_library_add(app, 0);
970 break;
971 case BUTTON_LIBRARY_REFRESH:
972 omnplay_library_refresh(app);
973 break;
974 };
975
976 return TRUE;
977 };
978
979 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
980 {
981 int i;
982 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
983
984 for(i = 1; i < BUTTON_LAST; i++)
985 if(app->buttons[i] == button)
986 return omnplay_button_click(app, (control_buttons_t)i);
987
988 return FALSE;
989 };
990
991 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
992 {
993 omnplay_instance_t* app = (omnplay_instance_t*)data;
994
995 switch(event->keyval)
996 {
997 case GDK_C:
998 case GDK_c:
999 if(event->state & GDK_CONTROL_MASK)
1000 {
1001 fprintf(stderr, "CTRL+c\n");
1002 return TRUE;
1003 };
1004 break;
1005 case GDK_V:
1006 case GDK_v:
1007 if(event->state & GDK_CONTROL_MASK)
1008 {
1009 fprintf(stderr, "CTRL+v\n");
1010 return TRUE;
1011 };
1012 break;
1013 case GDK_X:
1014 case GDK_x:
1015 if(event->state & GDK_CONTROL_MASK)
1016 {
1017 fprintf(stderr, "CTRL+x\n");
1018 return TRUE;
1019 };
1020 break;
1021 case GDK_S:
1022 case GDK_s:
1023 if(event->state & GDK_CONTROL_MASK)
1024 {
1025 omnplay_playlist_save(app);
1026 return TRUE;
1027 };
1028 break;
1029 case GDK_O:
1030 case GDK_o:
1031 if(event->state & GDK_CONTROL_MASK)
1032 {
1033 omnplay_playlist_load(app);
1034 return TRUE;
1035 };
1036 break;
1037 case GDK_KEY_space:
1038 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
1039 return TRUE;
1040 case GDK_KEY_Return:
1041 omnplay_ctl(app, BUTTON_PLAYER_CUE);
1042 return TRUE;
1043 case GDK_KEY_Insert:
1044 omnplay_playlist_item_add(app, 0);
1045 return TRUE;
1046 case GDK_KEY_Delete:
1047 omnplay_playlist_item_del(app);
1048 return TRUE;
1049 case GDK_KEY_BackSpace:
1050 omnplay_playlist_item_edit(app);
1051 return TRUE;
1052 };
1053
1054 return FALSE;
1055 };
1056
1057 void omnplay_init(omnplay_instance_t* app)
1058 {
1059 int i;
1060 pthread_mutexattr_t attr;
1061
1062 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1063
1064 gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event",
1065 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
1066
1067 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
1068 GTK_SIGNAL_FUNC(on_main_window_destroy), app);
1069
1070 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
1071 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
1072 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
1073
1074 /* create lock */
1075 pthread_mutex_init(&app->players.lock, &attr);
1076
1077 /* create a omneon status thread */
1078 for(i = 0; i < app->players.count; i++)
1079 pthread_create(&app->players.item[i].thread, NULL,
1080 omnplay_thread_proc, &app->players.item[i]);
1081
1082 /* create lock */
1083 pthread_mutex_init(&app->playlist.lock, &attr);
1084
1085 /* attach buttons click */
1086 for(i = 1; i < BUTTON_LAST; i++)
1087 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
1088 GTK_SIGNAL_FUNC( on_button_click), app );
1089
1090 /* create lock */
1091 pthread_mutex_init(&app->library.lock, &attr);
1092
1093 /* load library */
1094 omnplay_library_load(app);
1095 };
1096
1097 void omnplay_release(omnplay_instance_t* app)
1098 {
1099 int i;
1100 void* r;
1101
1102 app->f_exit = 1;
1103
1104 for(i = 0; i < app->players.count; i++)
1105 /* create a omneon status thread */
1106 pthread_join(app->players.item[i].thread, &r);
1107
1108 /* destroy lock */
1109 pthread_mutex_destroy(&app->players.lock);
1110
1111 /* destroy lock */
1112 pthread_mutex_destroy(&app->playlist.lock);
1113
1114 /* load library */
1115 omnplay_library_save(app);
1116
1117 /* destroy library lock */
1118 pthread_mutex_destroy(&app->library.lock);
1119 };