emotion: implement SPU switch for generic/vlc.
[profile/ivi/emotion.git] / src / generic_players / vlc / emotion_generic_vlc.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <errno.h>
6 #include <limits.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/mman.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <pthread.h>
15 #include <poll.h>
16
17 #include <sys/prctl.h>
18 #include <signal.h>
19
20 #include <vlc/vlc.h>
21
22 #include <Emotion_Generic_Plugin.h>
23
24 enum _Thread_Events {
25      EM_THREAD_POSITION_CHANGED,
26      EM_THREAD_PLAYBACK_STARTED,
27      EM_THREAD_PLAYBACK_STOPPED,
28      EM_THREAD_LAST
29 };
30
31 struct _App {
32      Emotion_Generic_Video_Shared *vs;
33      Emotion_Generic_Video_Frame vf;
34      libvlc_instance_t *libvlc;
35      libvlc_media_t *m;
36      libvlc_media_player_t *mp;
37      libvlc_event_manager_t *event_mgr;
38      libvlc_event_manager_t *mevent_mgr;
39      char *filename;
40      char *shmname;
41      void *tmpbuffer;
42      int w, h;
43      int fd_read; // read commands from theads here
44      int fd_write; // write commands from threads here
45      int em_read; // read commands from emotion here
46      int em_write; // write commands to emotion here
47      int size_sent;
48      int opening;
49      int closing;
50      int playing;
51 };
52
53 static pthread_mutex_t _mutex_fd = PTHREAD_MUTEX_INITIALIZER;
54
55 int
56 _em_read_safe(int fd, void *buf, ssize_t size)
57 {
58    ssize_t todo;
59    char *p;
60
61    todo = size;
62    p = buf;
63
64    while (todo > 0)
65      {
66         ssize_t r;
67
68         r = read(fd, p, todo);
69         if (r > 0)
70           {
71              todo -= r;
72              p += r;
73           }
74         else if (r == 0)
75           return 0;
76         else
77           {
78              if (errno == EINTR || errno == EAGAIN)
79                continue;
80              else
81                {
82                   fprintf(stderr, "could not read from fd %d: %s",
83                           fd, strerror(errno));
84                   return 0;
85                }
86           }
87      }
88
89    return 1;
90 }
91
92 int
93 _em_write_safe(int fd, const void *buf, ssize_t size)
94 {
95    ssize_t todo;
96    const char *p;
97
98    todo = size;
99    p = buf;
100
101    while (todo > 0)
102      {
103         ssize_t r;
104
105         r = write(fd, p, todo);
106         if (r > 0)
107           {
108              todo -= r;
109              p += r;
110           }
111         else if (r == 0)
112           return 0;
113         else
114           {
115              if (errno == EINTR || errno == EAGAIN)
116                continue;
117              else
118                {
119                   fprintf(stderr, "could not write to fd %d: %s",
120                           fd, strerror(errno));
121                   return 0;
122                }
123           }
124      }
125
126    return 1;
127 }
128
129 static int
130 _em_str_read(int fd, char **str)
131 {
132    int size;
133    int r;
134    char buf[PATH_MAX];
135
136    r = _em_read_safe(fd, &size, sizeof(size));
137    if (!r)
138      {
139         *str = NULL;
140         return 0;
141      }
142
143    if (!size)
144      {
145         *str = NULL;
146         return 1;
147      }
148
149    r = _em_read_safe(fd, buf, size);
150    if (!r)
151      {
152         *str = NULL;
153         return 0;
154      }
155
156    *str = strdup(buf);
157    return 1;
158 }
159
160 static int
161 _em_cmd_read(struct _App *app)
162 {
163    int cmd;
164    _em_read_safe(app->em_read, &cmd, sizeof(cmd));
165
166    return cmd;
167 }
168
169 static void
170 _send_cmd_start(struct _App *app, int cmd)
171 {
172    pthread_mutex_lock(&_mutex_fd);
173    _em_write_safe(app->em_write, &cmd, sizeof(cmd));
174 }
175
176 static void
177 _send_cmd_finish(struct _App *app __UNUSED__)
178 {
179    pthread_mutex_unlock(&_mutex_fd);
180 }
181
182 static void
183 _send_cmd(struct _App *app, int cmd)
184 {
185    _send_cmd_start(app, cmd);
186    _send_cmd_finish(app);
187 }
188
189 static void
190 _send_cmd_str(struct _App *app, const char *str)
191 {
192    int len;
193    if (str)
194      len = strlen(str) + 1;
195    else
196      len = 0;
197    _em_write_safe(app->em_write, &len, sizeof(len));
198    _em_write_safe(app->em_write, str, len);
199 }
200
201 #define SEND_CMD_PARAM(app, i) \
202    _em_write_safe((app)->em_write, &(i), sizeof((i)));
203
204 static void
205 _send_resize(struct _App *app, int width, int height)
206 {
207    _send_cmd_start(app, EM_RESULT_FRAME_SIZE);
208    SEND_CMD_PARAM(app, width);
209    SEND_CMD_PARAM(app, height);
210    _send_cmd_finish(app);
211 }
212
213 static void
214 _send_length_changed(struct _App *app, const struct libvlc_event_t *ev)
215 {
216    float length = ev->u.media_player_length_changed.new_length;
217    length /= 1000;
218
219    _send_cmd_start(app, EM_RESULT_LENGTH_CHANGED);
220    SEND_CMD_PARAM(app, length);
221    _send_cmd_finish(app);
222 }
223
224 static void
225 _send_time_changed(struct _App *app, const struct libvlc_event_t *ev)
226 {
227    float new_time = ev->u.media_player_time_changed.new_time;
228    new_time /= 1000;
229    if (app->vs->frame_drop > 1)
230      return;
231    _send_cmd_start(app, EM_RESULT_POSITION_CHANGED);
232    SEND_CMD_PARAM(app, new_time);
233    _send_cmd_finish(app);
234 }
235
236 static void
237 _send_seekable_changed(struct _App *app, const struct libvlc_event_t *ev)
238 {
239    int seekable = ev->u.media_player_seekable_changed.new_seekable;
240    _send_cmd_start(app, EM_RESULT_SEEKABLE_CHANGED);
241    SEND_CMD_PARAM(app, seekable);
242    _send_cmd_finish(app);
243 }
244
245 static void *
246 _lock(void *data, void **pixels)
247 {
248    struct _App *app = data;
249
250    if (app->playing)
251      *pixels = app->vf.frames[app->vs->frame.player];
252    else
253      *pixels = NULL;
254
255    return NULL; // picture identifier, not needed here
256 }
257
258 static void
259 _unlock(void *data __UNUSED__, void *id __UNUSED__, void *const *pixels __UNUSED__)
260 {
261 }
262
263 static void
264 _display(void *data, void *id __UNUSED__)
265 {
266    struct _App *app = data;
267    if (!app->playing)
268      return;
269
270    sem_wait(&app->vs->lock);
271    app->vs->frame.last = app->vs->frame.player;
272    app->vs->frame.player = app->vs->frame.next;
273    app->vs->frame.next = app->vs->frame.last;
274    if (!app->vs->frame_drop++)
275      _send_cmd(app, EM_RESULT_FRAME_NEW);
276    sem_post(&app->vs->lock);
277 }
278
279 static void *
280 _tmp_lock(void *data, void **pixels)
281 {
282    struct _App *app = data;
283    *pixels = app->tmpbuffer;
284    return NULL;
285 }
286
287 static void
288 _tmp_unlock(void *data __UNUSED__, void *id __UNUSED__, void *const *pixels __UNUSED__)
289 {
290 }
291
292 static void
293 _tmp_display(void *data __UNUSED__, void *id __UNUSED__)
294 {
295 }
296
297 static void
298 _play(struct _App *app)
299 {
300    float pos;
301
302    if (!app->mp)
303      return;
304
305    _em_read_safe(app->em_read, &pos, sizeof(pos));
306
307    if (app->playing)
308      {
309         libvlc_media_player_set_pause(app->mp, 0);
310      }
311    else
312      {
313         libvlc_time_t new_time = pos * 1000;
314         libvlc_media_player_set_time(app->mp, new_time);
315         libvlc_media_player_play(app->mp);
316         app->playing = 1;
317      }
318 }
319
320 static void
321 _stop(struct _App *app)
322 {
323    if (app->mp)
324      libvlc_media_player_set_pause(app->mp, 1);
325 }
326
327 static void
328 _send_file_closed(struct _App *app)
329 {
330    app->closing = 0;
331    emotion_generic_shm_free(app->vs);
332    _send_cmd(app, EM_RESULT_FILE_CLOSE);
333 }
334
335 static void
336 _send_file_set(struct _App *app)
337 {
338    if (app->opening)
339       _send_cmd(app, EM_RESULT_FILE_SET);
340
341    if (app->closing)
342      _send_file_closed(app);
343 }
344
345 static void
346 _event_cb(const struct libvlc_event_t *ev, void *data)
347 {
348    struct _App *app = data;
349    int thread_event;
350
351    switch (ev->type) {
352       case libvlc_MediaPlayerTimeChanged:
353          _send_time_changed(app, ev);
354          break;
355       case libvlc_MediaPlayerPositionChanged:
356          thread_event = EM_THREAD_POSITION_CHANGED;
357          write(app->fd_write, &thread_event, sizeof(thread_event));
358          break;
359       case libvlc_MediaPlayerLengthChanged:
360          _send_length_changed(app, ev);
361          break;
362       case libvlc_MediaPlayerSeekableChanged:
363          _send_seekable_changed(app, ev);
364          break;
365       case libvlc_MediaPlayerPlaying:
366          _send_resize(app, app->w, app->h);
367          thread_event = EM_THREAD_PLAYBACK_STARTED;
368          write(app->fd_write, &thread_event, sizeof(thread_event));
369          break;
370       case libvlc_MediaPlayerStopped:
371          _send_file_set(app);
372          break;
373       case libvlc_MediaPlayerEndReached:
374          thread_event = EM_THREAD_PLAYBACK_STOPPED;
375          write(app->fd_write, &thread_event, sizeof(thread_event));
376          break;
377    }
378 }
379
380 static void
381 _file_set(struct _App *app)
382 {
383    if (app->opening)
384      {
385         libvlc_media_release(app->m);
386         libvlc_media_player_release(app->mp);
387         free(app->filename);
388      }
389
390    _em_str_read(app->em_read, &app->filename);
391
392    app->m = libvlc_media_new_path(app->libvlc, app->filename);
393    if (!app->m)
394      {
395         fprintf(stderr, "could not open path: \"%s\"\n", app->filename);
396         return;
397      }
398    app->mp = libvlc_media_player_new_from_media(app->m);
399
400    if (!app->mp)
401      {
402         fprintf(stderr, "could not create new player from media.\n");
403         return;
404      }
405
406    app->opening = 1;
407    libvlc_video_set_format(app->mp, "RV32", DEFAULTWIDTH, DEFAULTHEIGHT, DEFAULTWIDTH * 4);
408    libvlc_video_set_callbacks(app->mp, _tmp_lock, _tmp_unlock, _tmp_display, app);
409    app->event_mgr = libvlc_media_player_event_manager(app->mp);
410    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPositionChanged,
411                        _event_cb, app);
412    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerStopped,
413                        _event_cb, app);
414
415    app->mevent_mgr = libvlc_media_event_manager(app->m);
416
417    app->tmpbuffer = malloc(sizeof(char) * DEFAULTWIDTH * DEFAULTHEIGHT * 4);
418    libvlc_audio_set_mute(app->mp, 1);
419    libvlc_media_player_play(app->mp);
420 }
421
422 static void
423 _position_set(struct _App *app)
424 {
425    if (!app->mp)
426      return;
427
428    float position;
429    _em_read_safe(app->em_read, &position, sizeof(position));
430
431    libvlc_time_t new_time = position * 1000;
432    libvlc_media_player_set_time(app->mp, new_time);
433 }
434
435 static void
436 _speed_set(struct _App *app)
437 {
438    float rate;
439
440    if (!app->mp)
441      return;
442
443    _em_read_safe(app->em_read, &rate, sizeof(rate));
444
445    libvlc_media_player_set_rate(app->mp, rate);
446 }
447
448 static void
449 _mute_set(struct _App *app)
450 {
451    int mute;
452
453    if (!app->mp)
454      return;
455
456    _em_read_safe(app->em_read, &mute, sizeof(mute));
457
458    libvlc_audio_set_mute(app->mp, mute);
459 }
460
461 static void
462 _volume_set(struct _App *app)
463 {
464    float volume;
465    int vol;
466
467    if (!app->mp)
468      return;
469
470    _em_read_safe(app->em_read, &volume, sizeof(volume));
471    vol = volume * 100;
472
473    libvlc_audio_set_volume(app->mp, vol);
474 }
475
476 static void
477 _spu_track_set(struct _App *app)
478 {
479    int track;
480
481    _em_read_safe(app->em_read, &track, sizeof(track));
482
483    libvlc_video_set_spu(app->mp, track);
484 }
485
486 static void
487 _audio_track_set(struct _App *app)
488 {
489    int track;
490
491    _em_read_safe(app->em_read, &track, sizeof(track));
492
493    libvlc_audio_set_track(app->mp, track);
494 }
495
496 static void
497 _video_track_set(struct _App *app)
498 {
499    int track;
500
501    _em_read_safe(app->em_read, &track, sizeof(track));
502
503    libvlc_video_set_track(app->mp, track);
504 }
505
506 static void
507 _file_set_done(struct _App *app)
508 {
509    int r;
510
511    app->opening = 0;
512
513    r = emotion_generic_shm_get(app->shmname, &app->vs, &app->vf);
514    if (!r)
515      {
516         free(app->filename);
517         libvlc_media_release(app->m);
518         libvlc_media_player_release(app->mp);
519         app->filename = NULL;
520         app->m = NULL;
521         app->mp = NULL;
522         _send_cmd_start(app, EM_RESULT_FILE_SET_DONE);
523         SEND_CMD_PARAM(app, r);
524         _send_cmd_finish(app);
525      }
526    app->w = app->vs->width;
527    app->h = app->vs->height;
528    libvlc_video_set_format(app->mp, "RV32", app->w, app->h, app->w * 4);
529    libvlc_video_set_callbacks(app->mp, _lock, _unlock, _display, app);
530
531
532    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerPlaying,
533                        _event_cb, app);
534    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerTimeChanged,
535                        _event_cb, app);
536    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerLengthChanged,
537                        _event_cb, app);
538    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerSeekableChanged,
539                        _event_cb, app);
540    libvlc_event_attach(app->event_mgr, libvlc_MediaPlayerEndReached,
541                        _event_cb, app);
542
543    libvlc_audio_set_mute(app->mp, 0);
544
545    _send_cmd_start(app, EM_RESULT_FILE_SET_DONE);
546    SEND_CMD_PARAM(app, r);
547    _send_cmd_finish(app);
548 }
549
550 static void
551 _file_close(struct _App *app)
552 {
553    app->playing = 0;
554    if (app->opening)
555      goto release_resources;
556
557    if (libvlc_media_player_get_state(app->mp) != libvlc_Playing)
558      {
559         _send_file_closed(app);
560         return;
561      }
562
563    app->closing = 1;
564
565 release_resources:
566    libvlc_media_player_stop(app->mp);
567    if (app->filename)
568      free(app->filename);
569    if (app->mp)
570      {
571         libvlc_media_release(app->m);
572         libvlc_media_player_release(app->mp);
573         free(app->tmpbuffer);
574      }
575 }
576
577 static void
578 _process_emotion_commands(struct _App *app)
579 {
580    int cmd = _em_cmd_read(app);
581    switch (cmd) {
582       case EM_CMD_FILE_SET:
583          _file_set(app);
584          break;
585       case EM_CMD_FILE_SET_DONE:
586          _file_set_done(app);
587          break;
588       case EM_CMD_FILE_CLOSE:
589          _file_close(app);
590          break;
591       case EM_CMD_PLAY:
592          _play(app);
593          break;
594       case EM_CMD_STOP:
595          _stop(app);
596          break;
597       case EM_CMD_POSITION_SET:
598          _position_set(app);
599          break;
600       case EM_CMD_SPEED_SET:
601          _speed_set(app);
602          break;
603       case EM_CMD_AUDIO_MUTE_SET:
604          _mute_set(app);
605          break;
606       case EM_CMD_VOLUME_SET:
607          _volume_set(app);
608          break;
609       case EM_CMD_SPU_TRACK_SET:
610          _spu_track_set(app);
611          break;
612       case EM_CMD_AUDIO_TRACK_SET:
613          _audio_track_set(app);
614          break;
615       case EM_CMD_VIDEO_TRACK_SET:
616          _video_track_set(app);
617          break;
618    };
619 }
620
621 static void
622 _send_track_info(struct _App *app, int cmd, int current, int count, libvlc_track_description_t *desc)
623 {
624    _send_cmd_start(app, cmd);
625    SEND_CMD_PARAM(app, current);
626    SEND_CMD_PARAM(app, count);
627    while (desc)
628      {
629         int tid = desc->i_id;
630         const char *name = desc->psz_name;
631         SEND_CMD_PARAM(app, tid);
632         _send_cmd_str(app, name);
633         desc = desc->p_next;
634      }
635    _send_cmd_finish(app);
636 }
637
638 static void
639 _send_all_track_info(struct _App *app)
640 {
641    int track_count, current;
642    libvlc_track_description_t *desc;
643
644    current = libvlc_audio_get_track(app->mp);
645    track_count = libvlc_audio_get_track_count(app->mp);
646    desc = libvlc_audio_get_track_description(app->mp);
647
648    _send_track_info(app, EM_RESULT_AUDIO_TRACK_INFO,
649                     current, track_count, desc);
650
651    current = libvlc_video_get_track(app->mp);
652    track_count = libvlc_video_get_track_count(app->mp);
653    desc = libvlc_video_get_track_description(app->mp);
654
655    _send_track_info(app, EM_RESULT_VIDEO_TRACK_INFO,
656                     current, track_count, desc);
657
658    current = libvlc_video_get_spu(app->mp);
659    track_count = libvlc_video_get_spu_count(app->mp);
660    desc = libvlc_video_get_spu_description(app->mp);
661
662    _send_track_info(app, EM_RESULT_SPU_TRACK_INFO,
663                     current, track_count, desc);
664 }
665
666 static void
667 _send_all_meta_info(struct _App *app)
668 {
669    const char *meta;
670
671    _send_cmd_start(app, EM_RESULT_META_INFO);
672
673    /*
674     * Will send in this order: title, artist, album, year,
675     * genre, comments, disc id and track count.
676     */
677    meta = libvlc_media_get_meta(app->m, libvlc_meta_Title);
678    _send_cmd_str(app, meta);
679    meta = libvlc_media_get_meta(app->m, libvlc_meta_Artist);
680    _send_cmd_str(app, meta);
681    meta = libvlc_media_get_meta(app->m, libvlc_meta_Album);
682    _send_cmd_str(app, meta);
683    meta = libvlc_media_get_meta(app->m, libvlc_meta_Date);
684    _send_cmd_str(app, meta);
685    meta = libvlc_media_get_meta(app->m, libvlc_meta_Genre);
686    _send_cmd_str(app, meta);
687    meta = NULL; // sending empty comments
688    _send_cmd_str(app, meta);
689    meta = NULL; // sending empty disc id
690    _send_cmd_str(app, meta);
691    meta = libvlc_media_get_meta(app->m, libvlc_meta_TrackNumber);
692    _send_cmd_str(app, meta);
693    _send_cmd_finish(app);
694 }
695
696 static void
697 _position_changed(struct _App *app)
698 {
699    if (!app->opening)
700      return;
701
702    /* sending size info only once */
703    int r;
704    unsigned int w, h;
705    r = libvlc_video_get_size(app->mp, 0, &w, &h);
706    if (r < 0)
707      {
708         w = 1;
709         h = 1;
710      }
711    _send_resize(app, w, h);
712
713    /* sending audio track info */
714    _send_all_track_info(app);
715
716    /* sending meta info */
717    _send_all_meta_info(app);
718
719    libvlc_media_player_stop(app->mp);
720 }
721
722 static void
723 _process_thread_events(struct _App *app)
724 {
725    int event;
726    size_t size;
727
728    size = read(app->fd_read, &event, sizeof(event));
729    if (size != sizeof(event))
730      {
731         fprintf(stderr, "player: problem when reading thread event. size = %zd\n", size);
732         return;
733      }
734
735    switch (event) {
736       case EM_THREAD_POSITION_CHANGED:
737          _position_changed(app);
738          break;
739       case EM_THREAD_PLAYBACK_STARTED:
740          _send_cmd(app, EM_RESULT_PLAYBACK_STARTED);
741          break;
742       case EM_THREAD_PLAYBACK_STOPPED:
743          libvlc_media_player_stop(app->mp);
744          app->playing = 0;
745          _send_cmd(app, EM_RESULT_PLAYBACK_STOPPED);
746          break;
747    }
748 }
749
750 int
751 main(int argc, const char *argv[])
752 {
753    struct _App app;
754    struct pollfd fds[3];
755    int tpipe[2]; // pipe for comunicating events from threads
756    char cwidth[64], cheight[64], cpitch[64], chroma[64];
757    char buf[64];
758    const char *vlc_argv[] =
759      {
760         "--quiet",
761         "--vout",
762         "vmem",
763         "--vmem-width",
764         cwidth,
765         "--vmem-height",
766         cheight,
767         "--vmem-pitch",
768         cpitch,
769         "--vmem-chroma",
770         chroma
771      };
772
773    if (argc < 3)
774      {
775         fprintf(stderr, "player: missing paramters.\n");
776         fprintf(stderr, "syntax:\n\t%s <fd read> <fd write>\n", argv[0]);
777         return -1;
778      }
779
780    app.em_read = atoi(argv[1]);
781    app.em_write = atoi(argv[2]);
782
783    int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
784    snprintf(cwidth, sizeof(cwidth), "%d", DEFAULTWIDTH);
785    snprintf(cheight, sizeof(cheight), "%d", DEFAULTHEIGHT);
786    snprintf(cpitch, sizeof(cpitch), "%d", DEFAULTWIDTH * 4);
787    snprintf(chroma, sizeof(chroma), "RV32");
788
789    /*
790     * Naughty xattr in emotion uses ecore_thread to run its thing, this
791     * may leave emotion's reference count high and it won't kill us...
792     * letting us play the video in the background.  not good.
793     *
794     * prctl(PR_SET_PDEATHSIG) is a linux only thing. Need to find ways
795     * to do it on other platforms. Until then leave it breaking on
796     * such platforms so people port it instead of ignoring.
797     */
798    prctl(PR_SET_PDEATHSIG, SIGHUP);
799
800    app.libvlc = libvlc_new(vlc_argc, vlc_argv);
801    app.mp = NULL;
802    app.filename = NULL;
803    app.w = 0;
804    app.h = 0;
805    app.size_sent = 0;
806    app.opening = 0;
807    app.playing = 0;
808    app.closing = 0;
809
810    if (_em_cmd_read(&app) != EM_CMD_INIT)
811      {
812         fprintf(stderr, "player: wrong init command!\n");
813         return -1;
814      }
815
816    int size;
817    _em_read_safe(app.em_read, &size, sizeof(size));
818    _em_read_safe(app.em_read, buf, size);
819    app.shmname = strdup(buf);
820
821    _send_cmd(&app, EM_RESULT_INIT);
822
823    pipe(tpipe);
824    app.fd_read = tpipe[0];
825    app.fd_write = tpipe[1];
826    fds[0].fd = app.em_read;
827    fds[0].events = POLLIN;
828    fds[1].fd = app.fd_read;
829    fds[1].events = POLLIN;
830    fds[2].fd = STDERR_FILENO;
831    fds[2].events = 0;
832
833    while (1)
834      {
835         int r;
836
837         r = poll(fds, 3, -1);
838         if (r == 0)
839           continue;
840         else if (r < 0)
841           {
842              fprintf(stderr,
843                      "emotion_generic_vlc: an error ocurred on poll(): %s\n",
844                      strerror(errno));
845              break;
846           }
847
848         if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
849           {
850              fputs("emotion_generic_vlc: error communicating with stdin\n",
851                    stderr);
852              break;
853           }
854         if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
855           {
856              fputs("emotion_generic_vlc: error communicating with thread\n",
857                    stderr);
858              break;
859           }
860
861         if (fds[0].revents & POLLIN)
862           _process_emotion_commands(&app);
863         if (fds[1].revents & POLLIN)
864           _process_thread_events(&app);
865         if (fds[2].revents & (POLLERR | POLLHUP | POLLNVAL))
866           break;
867      }
868
869    libvlc_release(app.libvlc);
870
871
872    return 0;
873 }
874 #undef SEND_CMD_PARAM