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