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