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