2 * omnplay.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
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.
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.
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.
32 #include <gdk/gdkkeysyms.h>
40 #include "omplrclnt.h"
42 static gboolean
on_main_window_delete_event( GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
48 omnplay_instance_t
* omnplay_create(int argc
, char** argv
)
51 omnplay_instance_t
* app
;
53 /* prepare application instance */
54 app
= (omnplay_instance_t
*)malloc(sizeof(omnplay_instance_t
));
55 memset(app
, 0, sizeof(omnplay_instance_t
));
57 /* load parameters from command line */
58 if(!omnplay_opt(argc
, argv
, app
) && app
->players
.count
)
59 app
->window
= ui_omnplay(app
);
66 void omnplay_destroy(omnplay_instance_t
* app
)
71 static void omnplay_update_status(omnplay_player_t
* player
, OmPlrStatus
*prev
, OmPlrStatus
*curr
)
73 char tc_cur
[32], tc_rem
[32], state
[32], status
[32];
78 frames2tc(curr
->pos
- curr
->minPos
, 25.0, tc_cur
);
79 frames2tc(curr
->maxPos
- curr
->pos
, 25.0, tc_rem
);
80 strcpy(status
, "ONLINE");
81 clip
= curr
->currClipName
;
85 case omPlrStateStopped
: strcpy(state
, "STOPPED"); break;
86 case omPlrStateCuePlay
: strcpy(state
, "CUE_PLAY"); break;
87 case omPlrStatePlay
: strcpy(state
, "PLAY"); break;
88 case omPlrStateCueRecord
: strcpy(state
, "CUE_RECORD"); break;
89 case omPlrStateRecord
: strcpy(state
, "RECORD"); break;
98 strcpy(status
, "OFFLINE");
102 gtk_label_set_text(GTK_LABEL (player
->label_tc_cur
), tc_cur
);
103 gtk_label_set_text(GTK_LABEL (player
->label_tc_rem
), tc_rem
);
104 gtk_label_set_text(GTK_LABEL (player
->label_state
), state
);
105 gtk_label_set_text(GTK_LABEL (player
->label_status
), status
);
106 gtk_label_set_text(GTK_LABEL (player
->label_clip
), clip
);
110 memcpy(prev
, curr
, sizeof(OmPlrStatus
));
113 static void* omnplay_thread_proc(void* data
)
116 OmPlrStatus st_curr
, st_prev
;
117 omnplay_player_t
* player
= (omnplay_player_t
*)data
;
120 pthread_mutex_lock(&player
->app
->players
.lock
);
121 r
= OmPlrOpen(player
->host
, player
->name
, (OmPlrHandle
*)&player
->handle
);
122 pthread_mutex_unlock(&player
->app
->players
.lock
);
125 fprintf(stderr
, "ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
126 player
->host
, player
->name
, r
);
131 /* setup to do not reconnect */
132 pthread_mutex_lock(&player
->app
->players
.lock
);
133 OmPlrSetRetryOpen((OmPlrHandle
)player
->handle
, 0);
134 pthread_mutex_unlock(&player
->app
->players
.lock
);
136 /* setup directory */
137 if(player
->app
->players
.path
[0])
139 pthread_mutex_lock(&player
->app
->players
.lock
);
140 r
= OmPlrClipSetDirectory((OmPlrHandle
)player
->handle
, player
->app
->players
.path
);
141 pthread_mutex_unlock(&player
->app
->players
.lock
);
145 fprintf(stderr
, "ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
146 player
->app
->players
.path
, r
);
148 pthread_mutex_lock(&player
->app
->players
.lock
);
149 OmPlrClose((OmPlrHandle
)player
->handle
);
150 pthread_mutex_unlock(&player
->app
->players
.lock
);
157 for(r
= 0 ; !player
->app
->f_exit
&& !r
;)
163 pthread_mutex_lock(&player
->app
->players
.lock
);
164 st_curr
.size
= sizeof(OmPlrStatus
);
165 r
= OmPlrGetPlayerStatus((OmPlrHandle
)player
->handle
, &st_curr
);
166 pthread_mutex_unlock(&player
->app
->players
.lock
);
169 fprintf(stderr
, "ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r
);
171 if(memcmp(&st_curr
, &st_prev
, sizeof(OmPlrStatus
)))
172 omnplay_update_status(player
, &st_prev
, &st_curr
);
175 pthread_mutex_lock(&player
->app
->players
.lock
);
176 OmPlrClose((OmPlrHandle
)player
->handle
);
177 pthread_mutex_unlock(&player
->app
->players
.lock
);
182 static int get_first_selected_item_playlist(omnplay_instance_t
* app
)
187 GtkTreeSelection
*selection
;
189 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
));
190 if(selection
&& gtk_tree_selection_get_selected(selection
, &model
, &iter
))
192 gtk_tree_model_get(model
, &iter
, 7, &idx
, -1);
198 static int get_playlist_block(omnplay_instance_t
* app
, int idx
, int* start_ptr
, int* stop_ptr
)
202 for(start
= idx
; start
>= 0; start
--)
203 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
)
206 for(stop
= idx
; stop
< app
->playlist
.count
; stop
++)
207 if(app
->playlist
.item
[stop
].type
& OMNPLAY_PLAYLIST_BLOCK_END
)
210 fprintf(stderr
, "get_playlist_block: range %d -> %d\n", start
, stop
);
212 /* check block range */
213 if(start
>= 0 && stop
< app
->playlist
.count
)
217 return (stop
- start
+ 1);
223 static omnplay_player_t
*get_player_at_pos(omnplay_instance_t
* app
, int pos
)
225 /* check player range */
226 if(app
->playlist
.item
[pos
].player
> -1 && app
->playlist
.item
[pos
].player
< app
->players
.count
)
227 return &app
->players
.item
[app
->playlist
.item
[pos
].player
];
232 static void omnplay_ctl(omnplay_instance_t
* app
, control_buttons_t button
)
235 int idx
, start
, stop
;
236 omnplay_player_t
*player
;
238 pthread_mutex_lock(&app
->playlist
.lock
);
240 idx
= get_first_selected_item_playlist(app
);
244 pthread_mutex_unlock(&app
->playlist
.lock
);
248 fprintf(stderr
, "cue: selected item is %d\n", idx
);
250 if(get_playlist_block(app
, idx
, &start
, &stop
) < 0)
252 pthread_mutex_unlock(&app
->playlist
.lock
);
256 fprintf(stderr
, "cue: range %d -> %d\n", start
, stop
);
258 player
= get_player_at_pos(app
, start
);
262 pthread_mutex_unlock(&app
->playlist
.lock
);
266 pthread_mutex_lock(&app
->players
.lock
);
268 if(BUTTON_PLAYER_STOP
== button
|| BUTTON_PLAYER_CUE
== button
)
271 OmPlrStop((OmPlrHandle
)player
->handle
);
273 /* detach previous clips */
274 player
->playlist_start
= -1;
275 player
->playlist_count
= -1;
276 OmPlrDetachAllClips((OmPlrHandle
)player
->handle
);
279 if(BUTTON_PLAYER_CUE
== button
)
283 /* Attach clips to timeline */
284 for(i
= start
, c
= 0, o
= 0; i
<= stop
; i
++)
289 clip
.maxMsTracks
= 0;
290 clip
.size
= sizeof(clip
);
291 r
= OmPlrClipGetInfo((OmPlrHandle
)player
->handle
, app
->playlist
.item
[i
].id
, &clip
);
297 fprintf(stderr
, "OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
298 app
->playlist
.item
[i
].id
, clip
.firstFrame
, clip
.lastFrame
);
300 /* should we fix playlist clip timings */
302 app
->playlist
.item
[i
].in
>= clip
.firstFrame
&&
303 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
<= clip
.lastFrame
) ||
304 !app
->playlist
.item
[i
].dur
)
306 fprintf(stderr
, "cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
307 app
->playlist
.item
[i
].id
,
308 app
->playlist
.item
[i
].in
, app
->playlist
.item
[i
].dur
,
309 clip
.firstFrame
, clip
.lastFrame
- clip
.firstFrame
);
311 app
->playlist
.item
[i
].in
= clip
.firstFrame
;
312 app
->playlist
.item
[i
].dur
= clip
.lastFrame
- clip
.firstFrame
;
313 omnplay_playlist_draw_item(app
, i
);
316 r
= OmPlrAttach((OmPlrHandle
)player
->handle
,
317 app
->playlist
.item
[i
].id
,
318 app
->playlist
.item
[i
].in
,
319 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
,
320 0, omPlrShiftModeAfter
, &l
);
325 fprintf(stderr
, "cue: failed with %d, %s\n", r
, OmPlrGetErrorString((OmPlrError
)r
));
326 app
->playlist
.item
[i
].omn_idx
= -1;
327 app
->playlist
.item
[i
].omn_offset
= -1;
331 app
->playlist
.item
[i
].omn_idx
= c
;
332 app
->playlist
.item
[i
].omn_offset
= o
;
334 /* save selected item offset */
338 o
+= app
->playlist
.item
[i
].dur
;
346 /* Set timeline min/max */
347 OmPlrSetMinPosMin((OmPlrHandle
)player
->handle
);
348 OmPlrSetMaxPosMax((OmPlrHandle
)player
->handle
);
350 /* Set timeline position */
352 hs
.size
= sizeof(OmPlrStatus
);
353 OmPlrGetPlayerStatus((OmPlrHandle
)player
->handle
, &hs
);
354 OmPlrSetPos((OmPlrHandle
)player
->handle
, hs
.minPos
+ p
);
357 OmPlrCuePlay((OmPlrHandle
)player
->handle
, 0.0);
358 OmPlrPlay((OmPlrHandle
)player
->handle
, 0.0);
360 player
->playlist_start
= start
;
361 player
->playlist_count
= stop
- start
+ 1;
365 if(BUTTON_PLAYER_PLAY
== button
)
368 OmPlrPlay((OmPlrHandle
)player
->handle
, 1.0);
371 if(BUTTON_PLAYER_PAUSE
== button
)
373 OmPlrPlay((OmPlrHandle
)player
->handle
, 0.0);
375 pthread_mutex_unlock(&app
->players
.lock
);
377 pthread_mutex_unlock(&app
->playlist
.lock
);
380 static gboolean
omnplay_button_click(omnplay_instance_t
* app
, control_buttons_t button
)
384 case BUTTON_PLAYLIST_ITEM_ADD
:
385 case BUTTON_PLAYLIST_ITEM_DEL
:
386 case BUTTON_PLAYLIST_ITEM_EDIT
:
388 case BUTTON_PLAYLIST_LOAD
:
389 omnplay_playlist_load(app
);
391 case BUTTON_PLAYLIST_SAVE
:
392 omnplay_playlist_save(app
);
394 case BUTTON_PLAYLIST_BLOCK_SINGLE
:
395 case BUTTON_PLAYLIST_BLOCK_LOOP
:
397 case BUTTON_PLAYLIST_ITEM_UP
:
398 case BUTTON_PLAYLIST_ITEM_DOWN
:
400 case BUTTON_PLAYER_CUE
:
401 case BUTTON_PLAYER_PLAY
:
402 case BUTTON_PLAYER_PAUSE
:
403 case BUTTON_PLAYER_STOP
:
404 omnplay_ctl(app
, button
);
406 case BUTTON_LIBRARY_ADD
:
407 case BUTTON_LIBRARY_REFRESH
:
414 static gboolean
on_button_click(GtkWidget
*button
, gpointer user_data
)
417 omnplay_instance_t
* app
= (omnplay_instance_t
*)user_data
;
419 for(i
= 1; i
< BUTTON_LAST
; i
++)
420 if(app
->buttons
[i
] == button
)
421 return omnplay_button_click(app
, (control_buttons_t
)i
);
426 void omnplay_init(omnplay_instance_t
* app
)
429 pthread_mutexattr_t attr
;
431 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
433 gtk_signal_connect( GTK_OBJECT( app
->window
), "destroy",
434 GTK_SIGNAL_FUNC(on_main_window_delete_event
), app
);
437 pthread_mutex_init(&app
->players
.lock
, &attr
);
439 /* create a omneon status thread */
440 for(i
= 0; i
< app
->players
.count
; i
++)
441 pthread_create(&app
->players
.item
[i
].thread
, NULL
,
442 omnplay_thread_proc
, &app
->players
.item
[i
]);
445 pthread_mutex_init(&app
->playlist
.lock
, &attr
);
447 /* attach buttons click */
448 for(i
= 1; i
< BUTTON_LAST
; i
++)
449 gtk_signal_connect(GTK_OBJECT(app
->buttons
[i
]), "clicked",
450 GTK_SIGNAL_FUNC( on_button_click
), app
);
454 void omnplay_release(omnplay_instance_t
* app
)
461 for(i
= 0; i
< app
->players
.count
; i
++)
462 /* create a omneon status thread */
463 pthread_join(app
->players
.item
[i
].thread
, &r
);
466 pthread_mutex_destroy(&app
->players
.lock
);
469 pthread_mutex_destroy(&app
->playlist
.lock
);