configure: update FFmpeg revision (bc63a76).
[vaapi:mplayer.git] / command.c
1 /*
2  * This file is part of MPlayer.
3  *
4  * MPlayer is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * MPlayer is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #define _BSD_SOURCE
20
21 #include <stdlib.h>
22 #include <inttypes.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <strings.h>
26
27 #include "config.h"
28 #include "command.h"
29 #include "input/input.h"
30 #include "stream/stream.h"
31 #include "libmpdemux/demuxer.h"
32 #include "libmpdemux/stheader.h"
33 #include "codec-cfg.h"
34 #include "mp_msg.h"
35 #include "mplayer.h"
36 #include "sub/sub.h"
37 #include "m_option.h"
38 #include "m_property.h"
39 #include "help_mp.h"
40 #include "metadata.h"
41 #include "libmpcodecs/vf.h"
42 #include "libmpcodecs/vd.h"
43 #include "libvo/video_out.h"
44 #include "libvo/aspect.h"
45 #include "sub/font_load.h"
46 #include "playtree.h"
47 #include "libao2/audio_out.h"
48 #include "mpcommon.h"
49 #include "mixer.h"
50 #include "libmpcodecs/dec_video.h"
51 #include "libmpcodecs/dec_teletext.h"
52 #include "osdep/strsep.h"
53 #include "sub/vobsub.h"
54 #include "sub/spudec.h"
55 #include "path.h"
56 #include "stream/tv.h"
57 #include "stream/stream_radio.h"
58 #include "stream/pvr.h"
59 #ifdef CONFIG_DVBIN
60 #include "stream/dvbin.h"
61 #endif
62 #include "stream/stream_bd.h"
63 #ifdef CONFIG_DVDREAD
64 #include "stream/stream_dvd.h"
65 #endif
66 #include "stream/stream_dvdnav.h"
67 #include "sub/ass_mp.h"
68 #include "m_struct.h"
69 #include "libmenu/menu.h"
70 #include "gui/interface.h"
71 #include "sub/eosd.h"
72 #include "pnm_loader.h"
73
74 #include "mp_core.h"
75 #include "mp_fifo.h"
76 #include "libavutil/avstring.h"
77 #include "edl.h"
78
79 #define IS_STREAMTYPE(t) (mpctx->stream && mpctx->stream->type == STREAMTYPE_##t)
80
81 static void rescale_input_coordinates(int ix, int iy, double *dx, double *dy)
82 {
83     //remove the borders, if any, and rescale to the range [0,1],[0,1]
84     int w = vo_dwidth, h = vo_dheight;
85     if (aspect_scaling()) {
86         aspect(&w, &h, A_WINZOOM);
87         panscan_calc_windowed();
88         w += vo_panscan_x;
89         h += vo_panscan_y;
90         ix -= (vo_dwidth  - w) / 2;
91         iy -= (vo_dheight - h) / 2;
92     }
93     if (ix < 0 || ix > w || iy < 0 || iy > h) {
94         // ignore movements outside movie area
95         *dx = *dy = -1.0;
96         return;
97     }
98
99     *dx = (double) ix / (double) w;
100     *dy = (double) iy / (double) h;
101
102     mp_msg(MSGT_CPLAYER, MSGL_V,
103            "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
104            *dx, *dy, vo_screenwidth, vo_screenheight, vo_dwidth,
105            vo_dheight, vo_fs);
106 }
107
108 static int sub_pos_by_source(MPContext *mpctx, int src)
109 {
110     int i, cnt = 0;
111     if (src >= SUB_SOURCES || mpctx->sub_counts[src] == 0)
112         return -1;
113     for (i = 0; i < src; i++)
114         cnt += mpctx->sub_counts[i];
115     return cnt;
116 }
117
118 static int sub_source_and_index_by_pos(MPContext *mpctx, int *pos)
119 {
120     int start = 0;
121     int i;
122     for (i = 0; i < SUB_SOURCES; i++) {
123         int cnt = mpctx->sub_counts[i];
124         if (*pos >= start && *pos < start + cnt) {
125             *pos -= start;
126             return i;
127         }
128         start += cnt;
129     }
130     *pos = -1;
131     return -1;
132 }
133
134 static int sub_source_by_pos(MPContext *mpctx, int pos)
135 {
136     return sub_source_and_index_by_pos(mpctx, &pos);
137 }
138
139 static int sub_source_pos(MPContext *mpctx)
140 {
141     int pos = mpctx->global_sub_pos;
142     sub_source_and_index_by_pos(mpctx, &pos);
143     return pos;
144 }
145
146 static int sub_source(MPContext *mpctx)
147 {
148     return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
149 }
150
151 static void update_global_sub_size(MPContext *mpctx)
152 {
153     int i;
154     int cnt = 0;
155
156     // update number of demuxer sub streams
157     for (i = 0; i < MAX_S_STREAMS; i++)
158         if (mpctx->demuxer->s_streams[i])
159             cnt++;
160     if (cnt > mpctx->sub_counts[SUB_SOURCE_DEMUX])
161         mpctx->sub_counts[SUB_SOURCE_DEMUX] = cnt;
162
163     // update global size
164     mpctx->global_sub_size = 0;
165     for (i = 0; i < SUB_SOURCES; i++)
166         mpctx->global_sub_size += mpctx->sub_counts[i];
167
168     // update global_sub_pos if we auto-detected a demuxer sub
169     if (mpctx->global_sub_pos == -1) {
170         int sub_id = -1;
171         if (mpctx->demuxer->sub)
172             sub_id = mpctx->demuxer->sub->id;
173         if (sub_id < 0)
174             sub_id = dvdsub_id;
175         if (sub_id >= 0 && sub_id < mpctx->sub_counts[SUB_SOURCE_DEMUX])
176             mpctx->global_sub_pos = sub_pos_by_source(mpctx, SUB_SOURCE_DEMUX) +
177                                     sub_id;
178     }
179 }
180
181 /**
182  * \brief Log the currently displayed subtitle to a file
183  *
184  * Logs the current or last displayed subtitle together with filename
185  * and time information to ~/.mplayer/subtitle_log
186  *
187  * Intended purpose is to allow convenient marking of bogus subtitles
188  * which need to be fixed while watching the movie.
189  */
190
191 static void log_sub(void)
192 {
193     char *fname;
194     FILE *f;
195     int i;
196
197     if (subdata == NULL || vo_sub_last == NULL)
198         return;
199     fname = get_path("subtitle_log");
200     f = fopen(fname, "a");
201     if (!f)
202         goto out;
203     fprintf(f, "----------------------------------------------------------\n");
204     if (subdata->sub_uses_time) {
205         fprintf(f,
206                 "N: %s S: %02ld:%02ld:%02ld.%02ld E: %02ld:%02ld:%02ld.%02ld\n",
207                 filename, vo_sub_last->start / 360000,
208                 (vo_sub_last->start / 6000) % 60,
209                 (vo_sub_last->start / 100) % 60, vo_sub_last->start % 100,
210                 vo_sub_last->end / 360000, (vo_sub_last->end / 6000) % 60,
211                 (vo_sub_last->end / 100) % 60, vo_sub_last->end % 100);
212     } else {
213         fprintf(f, "N: %s S: %ld E: %ld\n", filename, vo_sub_last->start,
214                 vo_sub_last->end);
215     }
216     for (i = 0; i < vo_sub_last->lines; i++) {
217         fprintf(f, "%s\n", vo_sub_last->text[i]);
218     }
219     fclose(f);
220 out:
221     free(fname);
222 }
223
224
225 /// \defgroup properties Properties
226 ///@{
227
228 /// \defgroup GeneralProperties General properties
229 /// \ingroup Properties
230 ///@{
231
232 /// OSD level (RW)
233 static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
234                                 MPContext *mpctx)
235 {
236     return m_property_choice(prop, action, arg, &osd_level);
237 }
238
239 /// Loop (RW)
240 static int mp_property_loop(m_option_t *prop, int action, void *arg,
241                             MPContext *mpctx)
242 {
243     switch (action) {
244     case M_PROPERTY_PRINT:
245         if (!arg) return M_PROPERTY_ERROR;
246         if (mpctx->loop_times < 0)
247             *(char**)arg = strdup("off");
248         else if (mpctx->loop_times == 0)
249             *(char**)arg = strdup("inf");
250         else
251             break;
252         return M_PROPERTY_OK;
253     }
254     return m_property_int_range(prop, action, arg, &mpctx->loop_times);
255 }
256
257 /// Playback speed (RW)
258 static int mp_property_playback_speed(m_option_t *prop, int action,
259                                       void *arg, MPContext *mpctx)
260 {
261     switch (action) {
262     case M_PROPERTY_SET:
263         if (!arg)
264             return M_PROPERTY_ERROR;
265         M_PROPERTY_CLAMP(prop, *(float *) arg);
266         playback_speed = *(float *) arg;
267         reinit_audio_chain();
268         return M_PROPERTY_OK;
269     case M_PROPERTY_STEP_UP:
270     case M_PROPERTY_STEP_DOWN:
271         playback_speed += (arg ? *(float *) arg : 0.1) *
272             (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
273         M_PROPERTY_CLAMP(prop, playback_speed);
274         reinit_audio_chain();
275         return M_PROPERTY_OK;
276     }
277     return m_property_float_range(prop, action, arg, &playback_speed);
278 }
279
280 /// filename with path (RO)
281 static int mp_property_path(m_option_t *prop, int action, void *arg,
282                             MPContext *mpctx)
283 {
284     return m_property_string_ro(prop, action, arg, filename);
285 }
286
287 /// filename without path (RO)
288 static int mp_property_filename(m_option_t *prop, int action, void *arg,
289                                 MPContext *mpctx)
290 {
291     char *f;
292     if (!filename)
293         return M_PROPERTY_UNAVAILABLE;
294     f = (char *)mp_basename(filename);
295     if (!*f)
296         f = filename;
297     return m_property_string_ro(prop, action, arg, f);
298 }
299
300 /// Demuxer name (RO)
301 static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
302                                MPContext *mpctx)
303 {
304     if (!mpctx->demuxer)
305         return M_PROPERTY_UNAVAILABLE;
306     return m_property_string_ro(prop, action, arg,
307                                 (char *) mpctx->demuxer->desc->name);
308 }
309
310 /// Position in the stream (RW)
311 static int mp_property_stream_pos(m_option_t *prop, int action, void *arg,
312                                   MPContext *mpctx)
313 {
314     if (!mpctx->demuxer || !mpctx->demuxer->stream)
315         return M_PROPERTY_UNAVAILABLE;
316     if (!arg)
317         return M_PROPERTY_ERROR;
318     switch (action) {
319     case M_PROPERTY_GET:
320         *(off_t *) arg = stream_tell(mpctx->demuxer->stream);
321         return M_PROPERTY_OK;
322     case M_PROPERTY_SET:
323         M_PROPERTY_CLAMP(prop, *(off_t *) arg);
324         stream_seek(mpctx->demuxer->stream, *(off_t *) arg);
325         return M_PROPERTY_OK;
326     }
327     return M_PROPERTY_NOT_IMPLEMENTED;
328 }
329
330 /// Stream start offset (RO)
331 static int mp_property_stream_start(m_option_t *prop, int action,
332                                     void *arg, MPContext *mpctx)
333 {
334     if (!mpctx->demuxer || !mpctx->demuxer->stream)
335         return M_PROPERTY_UNAVAILABLE;
336     switch (action) {
337     case M_PROPERTY_GET:
338         *(off_t *) arg = mpctx->demuxer->stream->start_pos;
339         return M_PROPERTY_OK;
340     }
341     return M_PROPERTY_NOT_IMPLEMENTED;
342 }
343
344 /// Stream end offset (RO)
345 static int mp_property_stream_end(m_option_t *prop, int action, void *arg,
346                                   MPContext *mpctx)
347 {
348     if (!mpctx->demuxer || !mpctx->demuxer->stream)
349         return M_PROPERTY_UNAVAILABLE;
350     switch (action) {
351     case M_PROPERTY_GET:
352         *(off_t *) arg = mpctx->demuxer->stream->end_pos;
353         return M_PROPERTY_OK;
354     }
355     return M_PROPERTY_NOT_IMPLEMENTED;
356 }
357
358 /// Stream length (RO)
359 static int mp_property_stream_length(m_option_t *prop, int action,
360                                      void *arg, MPContext *mpctx)
361 {
362     if (!mpctx->demuxer || !mpctx->demuxer->stream)
363         return M_PROPERTY_UNAVAILABLE;
364     switch (action) {
365     case M_PROPERTY_GET:
366         *(off_t *) arg =
367             mpctx->demuxer->stream->end_pos - mpctx->demuxer->stream->start_pos;
368         return M_PROPERTY_OK;
369     }
370     return M_PROPERTY_NOT_IMPLEMENTED;
371 }
372
373 /// Current stream position in seconds (RO)
374 static int mp_property_stream_time_pos(m_option_t *prop, int action,
375                                        void *arg, MPContext *mpctx)
376 {
377     if (!mpctx->demuxer || mpctx->demuxer->stream_pts == MP_NOPTS_VALUE)
378         return M_PROPERTY_UNAVAILABLE;
379
380     return m_property_time_ro(prop, action, arg, mpctx->demuxer->stream_pts);
381 }
382
383
384 /// Media length in seconds (RO)
385 static int mp_property_length(m_option_t *prop, int action, void *arg,
386                               MPContext *mpctx)
387 {
388     double len;
389
390     if (!mpctx->demuxer ||
391         !(int) (len = demuxer_get_time_length(mpctx->demuxer)))
392         return M_PROPERTY_UNAVAILABLE;
393
394     return m_property_time_ro(prop, action, arg, len);
395 }
396
397 /// Current position in percent (RW)
398 static int mp_property_percent_pos(m_option_t *prop, int action,
399                                    void *arg, MPContext *mpctx) {
400     int pos;
401
402     if (!mpctx->demuxer)
403         return M_PROPERTY_UNAVAILABLE;
404
405     switch(action) {
406     case M_PROPERTY_SET:
407         if(!arg) return M_PROPERTY_ERROR;
408         M_PROPERTY_CLAMP(prop, *(int*)arg);
409         pos = *(int*)arg;
410         break;
411     case M_PROPERTY_STEP_UP:
412     case M_PROPERTY_STEP_DOWN:
413         pos = demuxer_get_percent_pos(mpctx->demuxer);
414         pos += (arg ? *(int*)arg : 10) *
415             (action == M_PROPERTY_STEP_UP ? 1 : -1);
416         M_PROPERTY_CLAMP(prop, pos);
417         break;
418     default:
419         return m_property_int_ro(prop, action, arg,
420                                  demuxer_get_percent_pos(mpctx->demuxer));
421     }
422
423     abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR;
424     rel_seek_secs = pos / 100.0;
425     return M_PROPERTY_OK;
426 }
427
428 /// Current position in seconds (RW)
429 static int mp_property_time_pos(m_option_t *prop, int action,
430                                 void *arg, MPContext *mpctx) {
431     if (!(mpctx->sh_video || (mpctx->sh_audio && mpctx->audio_out)))
432         return M_PROPERTY_UNAVAILABLE;
433
434     switch(action) {
435     case M_PROPERTY_SET:
436         if(!arg) return M_PROPERTY_ERROR;
437         M_PROPERTY_CLAMP(prop, *(double*)arg);
438         abs_seek_pos = SEEK_ABSOLUTE;
439         rel_seek_secs = *(double*)arg;
440         return M_PROPERTY_OK;
441     case M_PROPERTY_STEP_UP:
442     case M_PROPERTY_STEP_DOWN:
443         rel_seek_secs += (arg ? *(double*)arg : 10.0) *
444             (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
445         return M_PROPERTY_OK;
446     }
447     return m_property_time_ro(prop, action, arg,
448                               mpctx->sh_video ? mpctx->sh_video->pts :
449                               playing_audio_pts(mpctx->sh_audio,
450                                                 mpctx->d_audio,
451                                                 mpctx->audio_out));
452 }
453
454 /// Current chapter (RW)
455 static int mp_property_chapter(m_option_t *prop, int action, void *arg,
456                                MPContext *mpctx)
457 {
458     int chapter = -1;
459     float next_pts = 0;
460     int chapter_num;
461     int step_all;
462     char *chapter_name = NULL;
463
464     if (mpctx->demuxer)
465         chapter = demuxer_get_current_chapter(mpctx->demuxer);
466     if (chapter < 0)
467         return M_PROPERTY_UNAVAILABLE;
468
469     switch (action) {
470     case M_PROPERTY_GET:
471         if (!arg)
472             return M_PROPERTY_ERROR;
473         *(int *) arg = chapter;
474         return M_PROPERTY_OK;
475     case M_PROPERTY_PRINT: {
476         if (!arg)
477             return M_PROPERTY_ERROR;
478         chapter_name = demuxer_chapter_display_name(mpctx->demuxer, chapter);
479         if (!chapter_name)
480             return M_PROPERTY_UNAVAILABLE;
481         *(char **) arg = chapter_name;
482         return M_PROPERTY_OK;
483     }
484     case M_PROPERTY_SET:
485         if (!arg)
486             return M_PROPERTY_ERROR;
487         M_PROPERTY_CLAMP(prop, *(int*)arg);
488         step_all = *(int *)arg - chapter;
489         chapter += step_all;
490         break;
491     case M_PROPERTY_STEP_UP:
492     case M_PROPERTY_STEP_DOWN: {
493         step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
494                    * (action == M_PROPERTY_STEP_UP ? 1 : -1);
495         chapter += step_all;
496         if (chapter < 0)
497             chapter = 0;
498         break;
499     }
500     default:
501         return M_PROPERTY_NOT_IMPLEMENTED;
502     }
503     rel_seek_secs = 0;
504     abs_seek_pos = 0;
505     chapter = demuxer_seek_chapter(mpctx->demuxer, chapter, 1,
506                                    &next_pts, &chapter_num, &chapter_name);
507     if (chapter >= 0) {
508         if (next_pts > -1.0) {
509             abs_seek_pos = SEEK_ABSOLUTE;
510             rel_seek_secs = next_pts;
511         }
512         if (chapter_name)
513             set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
514                         MSGTR_OSDChapter, chapter + 1, chapter_name);
515     }
516     else if (step_all > 0)
517         rel_seek_secs = 1000000000.;
518     else
519         set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
520                     MSGTR_OSDChapter, 0, MSGTR_Unknown);
521     free(chapter_name);
522     return M_PROPERTY_OK;
523 }
524
525 /// Number of titles in file
526 static int mp_property_titles(m_option_t *prop, int action, void *arg,
527                               MPContext *mpctx)
528 {
529     if (!mpctx->demuxer)
530         return M_PROPERTY_UNAVAILABLE;
531     if (mpctx->demuxer->num_titles == 0)
532         stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_NUM_TITLES, &mpctx->demuxer->num_titles);
533     return m_property_int_ro(prop, action, arg, mpctx->demuxer->num_titles);
534 }
535
536 /// Number of chapters in file
537 static int mp_property_chapters(m_option_t *prop, int action, void *arg,
538                                MPContext *mpctx)
539 {
540     if (!mpctx->demuxer)
541         return M_PROPERTY_UNAVAILABLE;
542     if (mpctx->demuxer->num_chapters == 0)
543         stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &mpctx->demuxer->num_chapters);
544     return m_property_int_ro(prop, action, arg, mpctx->demuxer->num_chapters);
545 }
546
547 /// Current dvd angle (RW)
548 static int mp_property_angle(m_option_t *prop, int action, void *arg,
549                                MPContext *mpctx)
550 {
551     int angle = -1;
552     int angles;
553     char *angle_name = NULL;
554
555     if (mpctx->demuxer)
556         angle = demuxer_get_current_angle(mpctx->demuxer);
557     if (angle < 0)
558         return M_PROPERTY_UNAVAILABLE;
559     angles = demuxer_angles_count(mpctx->demuxer);
560     if (angles <= 1)
561         return M_PROPERTY_UNAVAILABLE;
562
563     switch (action) {
564     case M_PROPERTY_GET:
565         if (!arg)
566             return M_PROPERTY_ERROR;
567         *(int *) arg = angle;
568         return M_PROPERTY_OK;
569     case M_PROPERTY_PRINT: {
570         if (!arg)
571             return M_PROPERTY_ERROR;
572         angle_name = calloc(1, 64);
573         if (!angle_name)
574             return M_PROPERTY_UNAVAILABLE;
575         snprintf(angle_name, 64, "%d/%d", angle, angles);
576         *(char **) arg = angle_name;
577         return M_PROPERTY_OK;
578     }
579     case M_PROPERTY_SET:
580         if (!arg)
581             return M_PROPERTY_ERROR;
582         angle = *(int *)arg;
583         M_PROPERTY_CLAMP(prop, angle);
584         break;
585     case M_PROPERTY_STEP_UP:
586     case M_PROPERTY_STEP_DOWN: {
587         int step = 0;
588         if(arg)
589             step = *(int*)arg;
590         if(!step)
591             step = 1;
592         step *= (action == M_PROPERTY_STEP_UP ? 1 : -1);
593         angle += step;
594         if (angle < 1) //cycle
595             angle = angles;
596         else if (angle > angles)
597             angle = 1;
598         break;
599     }
600     default:
601         return M_PROPERTY_NOT_IMPLEMENTED;
602     }
603     angle = demuxer_set_angle(mpctx->demuxer, angle);
604     set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
605                         MSGTR_OSDAngle, angle, angles);
606     free(angle_name);
607     return M_PROPERTY_OK;
608 }
609
610 /// Demuxer meta data
611 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
612                                 MPContext *mpctx) {
613     m_property_action_t* ka;
614     char* meta;
615     static const m_option_t key_type =
616         { "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL };
617     if (!mpctx->demuxer)
618         return M_PROPERTY_UNAVAILABLE;
619
620     switch(action) {
621     case M_PROPERTY_GET:
622         if(!arg) return M_PROPERTY_ERROR;
623         *(char***)arg = mpctx->demuxer->info;
624         return M_PROPERTY_OK;
625     case M_PROPERTY_KEY_ACTION:
626         if(!arg) return M_PROPERTY_ERROR;
627         ka = arg;
628         if(!(meta = demux_info_get(mpctx->demuxer,ka->key)))
629             return M_PROPERTY_UNKNOWN;
630         switch(ka->action) {
631         case M_PROPERTY_GET:
632             if(!ka->arg) return M_PROPERTY_ERROR;
633             *(char**)ka->arg = meta;
634             return M_PROPERTY_OK;
635         case M_PROPERTY_GET_TYPE:
636             if(!ka->arg) return M_PROPERTY_ERROR;
637             *(const m_option_t**)ka->arg = &key_type;
638             return M_PROPERTY_OK;
639         }
640     }
641     return M_PROPERTY_NOT_IMPLEMENTED;
642 }
643
644 static int mp_property_pause(m_option_t *prop, int action, void *arg,
645                              MPContext *mpctx)
646 {
647     return m_property_flag_ro(prop, action, arg, mpctx->osd_function == OSD_PAUSE);
648 }
649
650
651 ///@}
652
653 /// \defgroup AudioProperties Audio properties
654 /// \ingroup Properties
655 ///@{
656
657 /// Volume (RW)
658 static int mp_property_volume(m_option_t *prop, int action, void *arg,
659                               MPContext *mpctx)
660 {
661
662     if (!mpctx->sh_audio)
663         return M_PROPERTY_UNAVAILABLE;
664
665     switch (action) {
666     case M_PROPERTY_GET:
667         if (!arg)
668             return M_PROPERTY_ERROR;
669         mixer_getbothvolume(&mpctx->mixer, arg);
670         return M_PROPERTY_OK;
671     case M_PROPERTY_PRINT:{
672             float vol;
673             if (!arg)
674                 return M_PROPERTY_ERROR;
675             mixer_getbothvolume(&mpctx->mixer, &vol);
676             return m_property_float_range(prop, action, arg, &vol);
677         }
678     case M_PROPERTY_STEP_UP:
679     case M_PROPERTY_STEP_DOWN:
680     case M_PROPERTY_SET:
681         break;
682     default:
683         return M_PROPERTY_NOT_IMPLEMENTED;
684     }
685
686     if (mpctx->edl_muted)
687         return M_PROPERTY_DISABLED;
688     mpctx->user_muted = 0;
689
690     switch (action) {
691     case M_PROPERTY_SET:
692         if (!arg)
693             return M_PROPERTY_ERROR;
694         M_PROPERTY_CLAMP(prop, *(float *) arg);
695         mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
696         return M_PROPERTY_OK;
697     case M_PROPERTY_STEP_UP:
698         if (arg && *(float *) arg <= 0)
699             mixer_decvolume(&mpctx->mixer);
700         else
701             mixer_incvolume(&mpctx->mixer);
702         return M_PROPERTY_OK;
703     case M_PROPERTY_STEP_DOWN:
704         if (arg && *(float *) arg <= 0)
705             mixer_incvolume(&mpctx->mixer);
706         else
707             mixer_decvolume(&mpctx->mixer);
708         return M_PROPERTY_OK;
709     }
710     return M_PROPERTY_NOT_IMPLEMENTED;
711 }
712
713 /// Mute (RW)
714 static int mp_property_mute(m_option_t *prop, int action, void *arg,
715                             MPContext *mpctx)
716 {
717
718     if (!mpctx->sh_audio)
719         return M_PROPERTY_UNAVAILABLE;
720
721     switch (action) {
722     case M_PROPERTY_SET:
723         if (mpctx->edl_muted)
724             return M_PROPERTY_DISABLED;
725         if (!arg)
726             return M_PROPERTY_ERROR;
727         if ((!!*(int *) arg) != mpctx->mixer.muted)
728             mixer_mute(&mpctx->mixer);
729         mpctx->user_muted = mpctx->mixer.muted;
730         return M_PROPERTY_OK;
731     case M_PROPERTY_STEP_UP:
732     case M_PROPERTY_STEP_DOWN:
733         if (mpctx->edl_muted)
734             return M_PROPERTY_DISABLED;
735         mixer_mute(&mpctx->mixer);
736         mpctx->user_muted = mpctx->mixer.muted;
737         return M_PROPERTY_OK;
738     case M_PROPERTY_PRINT:
739         if (!arg)
740             return M_PROPERTY_ERROR;
741         if (mpctx->edl_muted) {
742             *(char **) arg = strdup(MSGTR_EnabledEdl);
743             return M_PROPERTY_OK;
744         }
745     default:
746         return m_property_flag(prop, action, arg, &mpctx->mixer.muted);
747
748     }
749 }
750
751 /// Audio delay (RW)
752 static int mp_property_audio_delay(m_option_t *prop, int action,
753                                    void *arg, MPContext *mpctx)
754 {
755     if (!(mpctx->sh_audio && mpctx->sh_video))
756         return M_PROPERTY_UNAVAILABLE;
757     switch (action) {
758     case M_PROPERTY_SET:
759     case M_PROPERTY_STEP_UP:
760     case M_PROPERTY_STEP_DOWN: {
761             int ret;
762             float delay = audio_delay;
763             ret = m_property_delay(prop, action, arg, &audio_delay);
764             if (ret != M_PROPERTY_OK)
765                 return ret;
766             if (mpctx->sh_audio)
767                 mpctx->delay -= audio_delay - delay;
768         }
769         return M_PROPERTY_OK;
770     default:
771         return m_property_delay(prop, action, arg, &audio_delay);
772     }
773 }
774
775 /// Audio codec tag (RO)
776 static int mp_property_audio_format(m_option_t *prop, int action,
777                                     void *arg, MPContext *mpctx)
778 {
779     if (!mpctx->sh_audio)
780         return M_PROPERTY_UNAVAILABLE;
781     return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
782 }
783
784 /// Audio codec name (RO)
785 static int mp_property_audio_codec(m_option_t *prop, int action,
786                                    void *arg, MPContext *mpctx)
787 {
788     if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
789         return M_PROPERTY_UNAVAILABLE;
790     return m_property_string_ro(prop, action, arg, mpctx->sh_audio->codec->name);
791 }
792
793 /// Audio bitrate (RO)
794 static int mp_property_audio_bitrate(m_option_t *prop, int action,
795                                      void *arg, MPContext *mpctx)
796 {
797     if (!mpctx->sh_audio)
798         return M_PROPERTY_UNAVAILABLE;
799     return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
800 }
801
802 /// Samplerate (RO)
803 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
804                                   MPContext *mpctx)
805 {
806     if (!mpctx->sh_audio)
807         return M_PROPERTY_UNAVAILABLE;
808     switch(action) {
809     case M_PROPERTY_PRINT:
810         if(!arg) return M_PROPERTY_ERROR;
811         *(char**)arg = malloc(16);
812         sprintf(*(char**)arg,"%d kHz",mpctx->sh_audio->samplerate/1000);
813         return M_PROPERTY_OK;
814     }
815     return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
816 }
817
818 /// Number of channels (RO)
819 static int mp_property_channels(m_option_t *prop, int action, void *arg,
820                                 MPContext *mpctx)
821 {
822     if (!mpctx->sh_audio)
823         return M_PROPERTY_UNAVAILABLE;
824     switch (action) {
825     case M_PROPERTY_PRINT:
826         if (!arg)
827             return M_PROPERTY_ERROR;
828         switch (mpctx->sh_audio->channels) {
829         case 1:
830             *(char **) arg = strdup("mono");
831             break;
832         case 2:
833             *(char **) arg = strdup("stereo");
834             break;
835         default:
836             *(char **) arg = malloc(32);
837             sprintf(*(char **) arg, "%d channels", mpctx->sh_audio->channels);
838         }
839         return M_PROPERTY_OK;
840     }
841     return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
842 }
843
844 /// Balance (RW)
845 static int mp_property_balance(m_option_t *prop, int action, void *arg,
846                               MPContext *mpctx)
847 {
848     float bal;
849
850     if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2)
851         return M_PROPERTY_UNAVAILABLE;
852
853     switch (action) {
854     case M_PROPERTY_GET:
855         if (!arg)
856             return M_PROPERTY_ERROR;
857         mixer_getbalance(&mpctx->mixer, arg);
858         return M_PROPERTY_OK;
859     case M_PROPERTY_PRINT: {
860             char** str = arg;
861             if (!arg)
862                 return M_PROPERTY_ERROR;
863             mixer_getbalance(&mpctx->mixer, &bal);
864             if (bal == 0.f)
865                 *str = strdup("center");
866             else if (bal == -1.f)
867                 *str = strdup("left only");
868             else if (bal == 1.f)
869                 *str = strdup("right only");
870             else {
871                 unsigned right = (bal + 1.f) / 2.f * 100.f;
872                 *str = malloc(sizeof("left xxx%, right xxx%"));
873                 sprintf(*str, "left %d%%, right %d%%", 100 - right, right);
874             }
875             return M_PROPERTY_OK;
876         }
877     case M_PROPERTY_STEP_UP:
878     case M_PROPERTY_STEP_DOWN:
879         mixer_getbalance(&mpctx->mixer, &bal);
880         bal += (arg ? *(float*)arg : .1f) *
881             (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
882         M_PROPERTY_CLAMP(prop, bal);
883         mixer_setbalance(&mpctx->mixer, bal);
884         return M_PROPERTY_OK;
885     case M_PROPERTY_SET:
886         if (!arg)
887             return M_PROPERTY_ERROR;
888         M_PROPERTY_CLAMP(prop, *(float*)arg);
889         mixer_setbalance(&mpctx->mixer, *(float*)arg);
890         return M_PROPERTY_OK;
891     }
892     return M_PROPERTY_NOT_IMPLEMENTED;
893 }
894
895 /// Selected audio id (RW)
896 static int mp_property_audio(m_option_t *prop, int action, void *arg,
897                              MPContext *mpctx)
898 {
899     int current_id, tmp;
900     if (!mpctx->demuxer || !mpctx->demuxer->audio)
901         return M_PROPERTY_UNAVAILABLE;
902     current_id = mpctx->demuxer->audio->id;
903     if (current_id >= 0)
904         audio_id = ((sh_audio_t *)mpctx->demuxer->a_streams[current_id])->aid;
905
906     switch (action) {
907     case M_PROPERTY_GET:
908         if (!arg)
909             return M_PROPERTY_ERROR;
910         *(int *) arg = audio_id;
911         return M_PROPERTY_OK;
912     case M_PROPERTY_PRINT:
913         if (!arg)
914             return M_PROPERTY_ERROR;
915
916         if (current_id < 0)
917             *(char **) arg = strdup(MSGTR_Disabled);
918         else {
919             char lang[40] = MSGTR_Unknown;
920             demuxer_audio_lang(mpctx->demuxer, current_id, lang, sizeof(lang));
921             *(char **) arg = malloc(64);
922             snprintf(*(char **) arg, 64, "(%d) %s", audio_id, lang);
923         }
924         return M_PROPERTY_OK;
925
926     case M_PROPERTY_STEP_UP:
927     case M_PROPERTY_SET:
928         if (action == M_PROPERTY_SET && arg)
929             tmp = *((int *) arg);
930         else
931             tmp = -1;
932         tmp = demuxer_switch_audio(mpctx->demuxer, tmp);
933         if (tmp == -2
934             || (tmp > -1
935                 && mpctx->demuxer->audio->id != current_id && current_id != -2)) {
936             uninit_player(INITIALIZED_AO | INITIALIZED_ACODEC);
937             audio_id = tmp;
938         }
939         if (tmp > -1 && mpctx->demuxer->audio->id != current_id) {
940             sh_audio_t *sh2;
941             sh2 = mpctx->demuxer->a_streams[mpctx->demuxer->audio->id];
942             if (sh2) {
943                 audio_id = sh2->aid;
944                 sh2->ds = mpctx->demuxer->audio;
945                 mpctx->sh_audio = sh2;
946                 reinit_audio_chain();
947             }
948         }
949         mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", audio_id);
950         return M_PROPERTY_OK;
951     default:
952         return M_PROPERTY_NOT_IMPLEMENTED;
953     }
954
955 }
956
957 /// Selected video id (RW)
958 static int mp_property_video(m_option_t *prop, int action, void *arg,
959                              MPContext *mpctx)
960 {
961     int current_id, tmp;
962     if (!mpctx->demuxer || !mpctx->demuxer->video)
963         return M_PROPERTY_UNAVAILABLE;
964     current_id = mpctx->demuxer->video->id;
965     if (current_id >= 0)
966         video_id = ((sh_video_t *)mpctx->demuxer->v_streams[current_id])->vid;
967
968     switch (action) {
969     case M_PROPERTY_GET:
970         if (!arg)
971             return M_PROPERTY_ERROR;
972         *(int *) arg = video_id;
973         return M_PROPERTY_OK;
974     case M_PROPERTY_PRINT:
975         if (!arg)
976             return M_PROPERTY_ERROR;
977
978         if (current_id < 0)
979             *(char **) arg = strdup(MSGTR_Disabled);
980         else {
981             char lang[40] = MSGTR_Unknown;
982             *(char **) arg = malloc(64);
983             snprintf(*(char **) arg, 64, "(%d) %s", video_id, lang);
984         }
985         return M_PROPERTY_OK;
986
987     case M_PROPERTY_STEP_UP:
988     case M_PROPERTY_SET:
989         if (action == M_PROPERTY_SET && arg)
990             tmp = *((int *) arg);
991         else
992             tmp = -1;
993         tmp = demuxer_switch_video(mpctx->demuxer, tmp);
994         if (tmp == -2
995             || (tmp > -1 && mpctx->demuxer->video->id != current_id
996                 && current_id != -2)) {
997             uninit_player(INITIALIZED_VCODEC |
998                           (fixed_vo && tmp != -2 ? 0 : INITIALIZED_VO));
999             video_id = tmp;
1000         }
1001         if (tmp > -1 && mpctx->demuxer->video->id != current_id) {
1002             sh_video_t *sh2;
1003             sh2 = mpctx->demuxer->v_streams[mpctx->demuxer->video->id];
1004             if (sh2) {
1005                 video_id = sh2->vid;
1006                 sh2->ds = mpctx->demuxer->video;
1007                 mpctx->sh_video = sh2;
1008                 reinit_video_chain();
1009             }
1010         }
1011         mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", video_id);
1012         return M_PROPERTY_OK;
1013
1014     default:
1015         return M_PROPERTY_NOT_IMPLEMENTED;
1016     }
1017 }
1018
1019 static int mp_property_program(m_option_t *prop, int action, void *arg,
1020                                MPContext *mpctx)
1021 {
1022     demux_program_t prog;
1023
1024     switch (action) {
1025     case M_PROPERTY_STEP_UP:
1026     case M_PROPERTY_SET:
1027         if (action == M_PROPERTY_SET && arg)
1028             prog.progid = *((int *) arg);
1029         else
1030             prog.progid = -1;
1031         if (demux_control
1032             (mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1033              &prog) == DEMUXER_CTRL_NOTIMPL)
1034             return M_PROPERTY_ERROR;
1035
1036         if (prog.aid < 0 && prog.vid < 0) {
1037             mp_msg(MSGT_CPLAYER, MSGL_ERR, "Selected program contains no audio or video streams!\n");
1038             return M_PROPERTY_ERROR;
1039         }
1040         mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1041         mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1042         return M_PROPERTY_OK;
1043
1044     default:
1045         return M_PROPERTY_NOT_IMPLEMENTED;
1046     }
1047 }
1048
1049 ///@}
1050
1051 /// \defgroup VideoProperties Video properties
1052 /// \ingroup Properties
1053 ///@{
1054
1055 /// Fullscreen state (RW)
1056 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1057                                   MPContext *mpctx)
1058 {
1059
1060     if (!mpctx->video_out)
1061         return M_PROPERTY_UNAVAILABLE;
1062
1063     switch (action) {
1064     case M_PROPERTY_SET:
1065         if (!arg)
1066             return M_PROPERTY_ERROR;
1067         M_PROPERTY_CLAMP(prop, *(int *) arg);
1068         if (vo_fs == !!*(int *) arg)
1069             return M_PROPERTY_OK;
1070     case M_PROPERTY_STEP_UP:
1071     case M_PROPERTY_STEP_DOWN:
1072 #ifdef CONFIG_GUI
1073         if (use_gui)
1074             gui(GUI_RUN_COMMAND, (void *) MP_CMD_VO_FULLSCREEN);
1075         else
1076 #endif
1077         if (vo_config_count)
1078             mpctx->video_out->control(VOCTRL_FULLSCREEN, 0);
1079         return M_PROPERTY_OK;
1080     default:
1081         return m_property_flag(prop, action, arg, &vo_fs);
1082     }
1083 }
1084
1085 static int mp_property_deinterlace(m_option_t *prop, int action,
1086                                    void *arg, MPContext *mpctx)
1087 {
1088     int deinterlace, deinterlace_old;
1089     int result;
1090     vf_instance_t *vf;
1091     if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1092         return M_PROPERTY_UNAVAILABLE;
1093     vf = mpctx->sh_video->vfilter;
1094     switch (action) {
1095     case M_PROPERTY_GET:
1096         if (!arg)
1097             return M_PROPERTY_ERROR;
1098         vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1099         return M_PROPERTY_OK;
1100     case M_PROPERTY_SET:
1101         if (!arg)
1102             return M_PROPERTY_ERROR;
1103         M_PROPERTY_CLAMP(prop, *(int *) arg);
1104         result = vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1105         return (result == CONTROL_OK) ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE;
1106     case M_PROPERTY_STEP_UP:
1107     case M_PROPERTY_STEP_DOWN:
1108         vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace_old);
1109         deinterlace = !deinterlace_old;
1110         result = vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1111         if (result != CONTROL_OK) {
1112             deinterlace = deinterlace_old;
1113         }
1114         set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDDeinterlace,
1115             deinterlace ? MSGTR_Enabled : MSGTR_Disabled);
1116         return (result == CONTROL_OK) ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE;
1117     }
1118     return M_PROPERTY_NOT_IMPLEMENTED;
1119 }
1120
1121 static int mp_property_capture(m_option_t *prop, int action,
1122                                void *arg, MPContext *mpctx)
1123 {
1124     int ret;
1125     int capturing = mpctx->stream && mpctx->stream->capture_file;
1126
1127     if (!mpctx->stream)
1128         return M_PROPERTY_UNAVAILABLE;
1129     if (!capture_dump) {
1130         mp_msg(MSGT_GLOBAL, MSGL_ERR,
1131                "Capturing not enabled (forgot -capture parameter?)\n");
1132         return M_PROPERTY_ERROR;
1133     }
1134
1135     ret = m_property_flag(prop, action, arg, &capturing);
1136     if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1137         if (capturing) {
1138             mpctx->stream->capture_file = fopen(stream_dump_name, "ab");
1139             if (!mpctx->stream->capture_file) {
1140                 mp_msg(MSGT_GLOBAL, MSGL_ERR,
1141                        "Error opening capture file: %s\n", strerror(errno));
1142                 ret = M_PROPERTY_ERROR;
1143             }
1144         } else {
1145             fclose(mpctx->stream->capture_file);
1146             mpctx->stream->capture_file = NULL;
1147         }
1148     }
1149
1150     switch (ret) {
1151     case M_PROPERTY_ERROR:
1152         set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDCapturingFailure);
1153         break;
1154     case M_PROPERTY_OK:
1155         set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDCapturing,
1156                     mpctx->stream->capture_file ? MSGTR_Enabled : MSGTR_Disabled);
1157         break;
1158     default:
1159         break;
1160     }
1161
1162     return ret;
1163 }
1164
1165 /// Panscan (RW)
1166 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1167                                MPContext *mpctx)
1168 {
1169
1170     if (!mpctx->video_out
1171         || mpctx->video_out->control(VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1172         return M_PROPERTY_UNAVAILABLE;
1173
1174     switch (action) {
1175     case M_PROPERTY_SET:
1176         if (!arg)
1177             return M_PROPERTY_ERROR;
1178         M_PROPERTY_CLAMP(prop, *(float *) arg);
1179         vo_panscan = *(float *) arg;
1180         mpctx->video_out->control(VOCTRL_SET_PANSCAN, NULL);
1181         return M_PROPERTY_OK;
1182     case M_PROPERTY_STEP_UP:
1183     case M_PROPERTY_STEP_DOWN:
1184         vo_panscan += (arg ? *(float *) arg : 0.1) *
1185             (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1186         if (vo_panscan > 1)
1187             vo_panscan = 1;
1188         else if (vo_panscan < 0)
1189             vo_panscan = 0;
1190         mpctx->video_out->control(VOCTRL_SET_PANSCAN, NULL);
1191         return M_PROPERTY_OK;
1192     default:
1193         return m_property_float_range(prop, action, arg, &vo_panscan);
1194     }
1195 }
1196
1197 /// Helper to set vo flags.
1198 /** \ingroup PropertyImplHelper
1199  */
1200 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1201                                int vo_ctrl, int *vo_var, MPContext *mpctx)
1202 {
1203
1204     if (!mpctx->video_out)
1205         return M_PROPERTY_UNAVAILABLE;
1206
1207     switch (action) {
1208     case M_PROPERTY_SET:
1209         if (!arg)
1210             return M_PROPERTY_ERROR;
1211         M_PROPERTY_CLAMP(prop, *(int *) arg);
1212         if (*vo_var == !!*(int *) arg)
1213             return M_PROPERTY_OK;
1214     case M_PROPERTY_STEP_UP:
1215     case M_PROPERTY_STEP_DOWN:
1216         if (vo_config_count)
1217             mpctx->video_out->control(vo_ctrl, 0);
1218         return M_PROPERTY_OK;
1219     default:
1220         return m_property_flag(prop, action, arg, vo_var);
1221     }
1222 }
1223
1224 /// Window always on top (RW)
1225 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1226                              MPContext *mpctx)
1227 {
1228     return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP, &vo_ontop,
1229                                mpctx);
1230 }
1231
1232 /// Display in the root window (RW)
1233 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1234                                MPContext *mpctx)
1235 {
1236     return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1237                                &vo_rootwin, mpctx);
1238 }
1239
1240 /// Show window borders (RW)
1241 static int mp_property_border(m_option_t *prop, int action, void *arg,
1242                               MPContext *mpctx)
1243 {
1244     return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1245                                &vo_border, mpctx);
1246 }
1247
1248 /// Framedropping state (RW)
1249 static int mp_property_framedropping(m_option_t *prop, int action,
1250                                      void *arg, MPContext *mpctx)
1251 {
1252
1253     if (!mpctx->sh_video)
1254         return M_PROPERTY_UNAVAILABLE;
1255
1256     switch (action) {
1257     case M_PROPERTY_PRINT:
1258         if (!arg)
1259             return M_PROPERTY_ERROR;
1260         *(char **) arg = strdup(frame_dropping == 1 ? MSGTR_Enabled :
1261                                 (frame_dropping == 2 ? MSGTR_HardFrameDrop :
1262                                  MSGTR_Disabled));
1263         return M_PROPERTY_OK;
1264     default:
1265         return m_property_choice(prop, action, arg, &frame_dropping);
1266     }
1267 }
1268
1269 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1270 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1271                              MPContext *mpctx)
1272 {
1273     int *gamma = prop->priv, r, val;
1274
1275     if (!mpctx->sh_video)
1276         return M_PROPERTY_UNAVAILABLE;
1277
1278     if (gamma[0] == 1000) {
1279         gamma[0] = 0;
1280         get_video_colors(mpctx->sh_video, prop->name, gamma);
1281     }
1282
1283     switch (action) {
1284     case M_PROPERTY_SET:
1285         if (!arg)
1286             return M_PROPERTY_ERROR;
1287         M_PROPERTY_CLAMP(prop, *(int *) arg);
1288         *gamma = *(int *) arg;
1289         r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1290         if (r <= 0)
1291             break;
1292         return r;
1293     case M_PROPERTY_GET:
1294         if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1295             if (!arg)
1296                 return M_PROPERTY_ERROR;
1297             *(int *)arg = val;
1298             return M_PROPERTY_OK;
1299         }
1300         break;
1301     case M_PROPERTY_STEP_UP:
1302     case M_PROPERTY_STEP_DOWN:
1303         *gamma += (arg ? *(int *) arg : 1) *
1304             (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1305         M_PROPERTY_CLAMP(prop, *gamma);
1306         r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1307         if (r <= 0)
1308             break;
1309         return r;
1310     default:
1311         return M_PROPERTY_NOT_IMPLEMENTED;
1312     }
1313
1314 #ifdef CONFIG_TV
1315     if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1316         int l = strlen(prop->name);
1317         char tv_prop[3 + l + 1];
1318         sprintf(tv_prop, "tv_%s", prop->name);
1319         return mp_property_do(tv_prop, action, arg, mpctx);
1320     }
1321 #endif
1322
1323     return M_PROPERTY_UNAVAILABLE;
1324 }
1325
1326 /// VSync (RW)
1327 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1328                              MPContext *mpctx)
1329 {
1330     return m_property_flag(prop, action, arg, &vo_vsync);
1331 }
1332
1333 /// Video codec tag (RO)
1334 static int mp_property_video_format(m_option_t *prop, int action,
1335                                     void *arg, MPContext *mpctx)
1336 {
1337     char* meta;
1338     if (!mpctx->sh_video)
1339         return M_PROPERTY_UNAVAILABLE;
1340     switch(action) {
1341     case M_PROPERTY_PRINT:
1342         if (!arg)
1343             return M_PROPERTY_ERROR;
1344         switch(mpctx->sh_video->format) {
1345         case 0x10000001:
1346             meta = strdup ("mpeg1"); break;
1347         case 0x10000002:
1348             meta = strdup ("mpeg2"); break;
1349         case 0x10000004:
1350             meta = strdup ("mpeg4"); break;
1351         case 0x10000005:
1352             meta = strdup ("h264"); break;
1353         default:
1354             if(mpctx->sh_video->format >= 0x20202020) {
1355                 meta = malloc(5);
1356                 sprintf (meta, "%.4s", (char *) &mpctx->sh_video->format);
1357             } else   {
1358                 meta = malloc(20);
1359                 sprintf (meta, "0x%08X", mpctx->sh_video->format);
1360             }
1361         }
1362         *(char**)arg = meta;
1363         return M_PROPERTY_OK;
1364     }
1365     return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1366 }
1367
1368 /// Video codec name (RO)
1369 static int mp_property_video_codec(m_option_t *prop, int action,
1370                                    void *arg, MPContext *mpctx)
1371 {
1372     if (!mpctx->sh_video || !mpctx->sh_video->codec)
1373         return M_PROPERTY_UNAVAILABLE;
1374     return m_property_string_ro(prop, action, arg, mpctx->sh_video->codec->name);
1375 }
1376
1377
1378 /// Video bitrate (RO)
1379 static int mp_property_video_bitrate(m_option_t *prop, int action,
1380                                      void *arg, MPContext *mpctx)
1381 {
1382     if (!mpctx->sh_video)
1383         return M_PROPERTY_UNAVAILABLE;
1384     return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1385 }
1386
1387 /// Video display width (RO)
1388 static int mp_property_width(m_option_t *prop, int action, void *arg,
1389                              MPContext *mpctx)
1390 {
1391     if (!mpctx->sh_video)
1392         return M_PROPERTY_UNAVAILABLE;
1393     return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1394 }
1395
1396 /// Video display height (RO)
1397 static int mp_property_height(m_option_t *prop, int action, void *arg,
1398                               MPContext *mpctx)
1399 {
1400     if (!mpctx->sh_video)
1401         return M_PROPERTY_UNAVAILABLE;
1402     return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1403 }
1404
1405 /// Video fps (RO)
1406 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1407                            MPContext *mpctx)
1408 {
1409     if (!mpctx->sh_video)
1410         return M_PROPERTY_UNAVAILABLE;
1411     return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1412 }
1413
1414 /// Video aspect (RO)
1415 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1416                               MPContext *mpctx)
1417 {
1418     if (!mpctx->sh_video)
1419         return M_PROPERTY_UNAVAILABLE;
1420     return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1421 }
1422
1423 ///@}
1424
1425 /// \defgroup SubProprties Subtitles properties
1426 /// \ingroup Properties
1427 ///@{
1428
1429 /// Text subtitle position (RW)
1430 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1431                                MPContext *mpctx)
1432 {
1433     switch (action) {
1434     case M_PROPERTY_SET:
1435         if (!arg)
1436             return M_PROPERTY_ERROR;
1437     case M_PROPERTY_STEP_UP:
1438     case M_PROPERTY_STEP_DOWN:
1439         vo_osd_changed(OSDTYPE_SUBTITLE);
1440     default:
1441         return m_property_int_range(prop, action, arg, &sub_pos);
1442     }
1443 }
1444
1445 /// Selected subtitles (RW)
1446 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1447                            MPContext *mpctx)
1448 {
1449     demux_stream_t *const d_sub = mpctx->d_sub;
1450     int global_sub_size;
1451     int source = -1, reset_spu = 0;
1452     int source_pos = -1;
1453     double pts = 0;
1454     char *sub_name;
1455
1456     update_global_sub_size(mpctx);
1457     global_sub_size = mpctx->global_sub_size;
1458     if (global_sub_size <= 0)
1459         return M_PROPERTY_UNAVAILABLE;
1460
1461     switch (action) {
1462     case M_PROPERTY_GET:
1463         if (!arg)
1464             return M_PROPERTY_ERROR;
1465         *(int *) arg = mpctx->global_sub_pos;
1466         return M_PROPERTY_OK;
1467     case M_PROPERTY_PRINT:
1468         if (!arg)
1469             return M_PROPERTY_ERROR;
1470         *(char **) arg = malloc(64);
1471         (*(char **) arg)[63] = 0;
1472         sub_name = 0;
1473         if (subdata)
1474             sub_name = subdata->filename;
1475 #ifdef CONFIG_ASS
1476         if (ass_track && ass_track->name)
1477             sub_name = ass_track->name;
1478 #endif
1479         if (sub_name) {
1480             const char *tmp = mp_basename(sub_name);
1481
1482             snprintf(*(char **) arg, 63, "(%d) %s%s",
1483                      mpctx->set_of_sub_pos + 1,
1484                      strlen(tmp) < 20 ? "" : "...",
1485                      strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1486             return M_PROPERTY_OK;
1487         }
1488
1489         if (vo_vobsub && vobsub_id >= 0) {
1490             const char *language = MSGTR_Unknown;
1491             language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1492             snprintf(*(char **) arg, 63, "(%d) %s",
1493                      vobsub_id, language ? language : MSGTR_Unknown);
1494             return M_PROPERTY_OK;
1495         }
1496         if (dvdsub_id >= 0) {
1497             char lang[40] = MSGTR_Unknown;
1498             int id = dvdsub_id;
1499             // HACK: for DVDs sub->sh/id will be invalid until
1500             // we actually get the first packet
1501             if (d_sub && d_sub->sh)
1502                 id = d_sub->id;
1503             demuxer_sub_lang(mpctx->demuxer, id, lang, sizeof(lang));
1504             snprintf(*(char **) arg, 63, "(%d) %s", dvdsub_id, lang);
1505             return M_PROPERTY_OK;
1506         }
1507         snprintf(*(char **) arg, 63, MSGTR_Disabled);
1508         return M_PROPERTY_OK;
1509
1510     case M_PROPERTY_SET:
1511         if (!arg)
1512             return M_PROPERTY_ERROR;
1513         if (*(int *) arg < -1)
1514             *(int *) arg = -1;
1515         else if (*(int *) arg >= global_sub_size)
1516             *(int *) arg = global_sub_size - 1;
1517         mpctx->global_sub_pos = *(int *) arg;
1518         break;
1519     case M_PROPERTY_STEP_UP:
1520         mpctx->global_sub_pos += 2;
1521         mpctx->global_sub_pos =
1522             (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1523         break;
1524     case M_PROPERTY_STEP_DOWN:
1525         mpctx->global_sub_pos += global_sub_size + 1;
1526         mpctx->global_sub_pos =
1527             (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1528         break;
1529     default:
1530         return M_PROPERTY_NOT_IMPLEMENTED;
1531     }
1532
1533     if (mpctx->global_sub_pos >= 0) {
1534         source = sub_source(mpctx);
1535         source_pos = sub_source_pos(mpctx);
1536     }
1537
1538     mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1539            "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1540            global_sub_size,
1541            mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1542            mpctx->sub_counts[SUB_SOURCE_SUBS],
1543            mpctx->sub_counts[SUB_SOURCE_DEMUX],
1544            mpctx->global_sub_pos, source);
1545
1546     mpctx->set_of_sub_pos = -1;
1547     subdata = NULL;
1548
1549     vobsub_id = -1;
1550     dvdsub_id = -1;
1551     if (d_sub) {
1552         if (d_sub->id > -2)
1553             reset_spu = 1;
1554         d_sub->id = -2;
1555     }
1556 #ifdef CONFIG_ASS
1557     ass_track = 0;
1558 #endif
1559
1560     if (source == SUB_SOURCE_VOBSUB) {
1561         vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1562     } else if (source == SUB_SOURCE_SUBS) {
1563         mpctx->set_of_sub_pos = source_pos;
1564 #ifdef CONFIG_ASS
1565         if (ass_enabled && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos])
1566             ass_track = mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
1567         else
1568 #endif
1569         {
1570             subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1571             vo_osd_changed(OSDTYPE_SUBTITLE);
1572         }
1573     } else if (source == SUB_SOURCE_DEMUX) {
1574         dvdsub_id = source_pos;
1575         if (d_sub && dvdsub_id < MAX_S_STREAMS) {
1576             int i = 0;
1577             // default: assume 1:1 mapping of sid and stream id
1578             d_sub->id = dvdsub_id;
1579             d_sub->sh = mpctx->demuxer->s_streams[d_sub->id];
1580             ds_free_packs(d_sub);
1581             for (i = 0; i < MAX_S_STREAMS; i++) {
1582                 sh_sub_t *sh = mpctx->demuxer->s_streams[i];
1583                 if (sh && sh->sid == dvdsub_id) {
1584                     d_sub->id = i;
1585                     d_sub->sh = sh;
1586                     break;
1587                 }
1588             }
1589             if (d_sub->sh && d_sub->id >= 0) {
1590                 sh_sub_t *sh = d_sub->sh;
1591                 if (sh->type == 'v')
1592                     init_vo_spudec(mpctx->stream, mpctx->sh_video, sh);
1593 #ifdef CONFIG_ASS
1594                 else if (ass_enabled)
1595                     ass_track = sh->ass_track;
1596 #endif
1597             } else {
1598               d_sub->id = -2;
1599               d_sub->sh = NULL;
1600             }
1601         }
1602     }
1603 #ifdef CONFIG_DVDREAD
1604     if (vo_spudec
1605         && (IS_STREAMTYPE(DVD)
1606             || IS_STREAMTYPE(DVDNAV))
1607         && dvdsub_id < 0 && reset_spu) {
1608         d_sub->id = -2;
1609         d_sub->sh = NULL;
1610     }
1611 #endif
1612     if (mpctx->sh_audio)
1613         pts = mpctx->sh_audio->pts;
1614     if (mpctx->sh_video)
1615         pts = mpctx->sh_video->pts;
1616     update_subtitles(mpctx->sh_video, pts, d_sub, 1);
1617
1618     return M_PROPERTY_OK;
1619 }
1620
1621 /// Selected sub source (RW)
1622 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1623                                   MPContext *mpctx)
1624 {
1625     int source;
1626     update_global_sub_size(mpctx);
1627     if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1628         return M_PROPERTY_UNAVAILABLE;
1629
1630     switch (action) {
1631     case M_PROPERTY_GET:
1632         if (!arg)
1633             return M_PROPERTY_ERROR;
1634         *(int *) arg = sub_source(mpctx);
1635         return M_PROPERTY_OK;
1636     case M_PROPERTY_PRINT:
1637         if (!arg)
1638             return M_PROPERTY_ERROR;
1639         *(char **) arg = malloc(64);
1640         (*(char **) arg)[63] = 0;
1641         switch (sub_source(mpctx))
1642         {
1643         case SUB_SOURCE_SUBS:
1644             snprintf(*(char **) arg, 63, MSGTR_SubSourceFile);
1645             break;
1646         case SUB_SOURCE_VOBSUB:
1647             snprintf(*(char **) arg, 63, MSGTR_SubSourceVobsub);
1648             break;
1649         case SUB_SOURCE_DEMUX:
1650             snprintf(*(char **) arg, 63, MSGTR_SubSourceDemux);
1651             break;
1652         default:
1653             snprintf(*(char **) arg, 63, MSGTR_Disabled);
1654         }
1655         return M_PROPERTY_OK;
1656     case M_PROPERTY_SET:
1657         if (!arg)
1658             return M_PROPERTY_ERROR;
1659         M_PROPERTY_CLAMP(prop, *(int*)arg);
1660         if (*(int *) arg < 0)
1661             mpctx->global_sub_pos = -1;
1662         else if (*(int *) arg != sub_source(mpctx)) {
1663             int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1664             if (new_pos == -1)
1665                 return M_PROPERTY_UNAVAILABLE;
1666             mpctx->global_sub_pos = new_pos;
1667         }
1668         break;
1669     case M_PROPERTY_STEP_UP:
1670     case M_PROPERTY_STEP_DOWN: {
1671         int step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
1672                        * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1673         int step = (step_all > 0) ? 1 : -1;
1674         int cur_source = sub_source(mpctx);
1675         source = cur_source;
1676         while (step_all) {
1677             source += step;
1678             if (source >= SUB_SOURCES)
1679                 source = -1;
1680             else if (source < -1)
1681                 source = SUB_SOURCES - 1;
1682             if (source == cur_source || source == -1 ||
1683                     mpctx->sub_counts[source])
1684                 step_all -= step;
1685         }
1686         if (source == cur_source)
1687             return M_PROPERTY_OK;
1688         if (source == -1)
1689             mpctx->global_sub_pos = -1;
1690         else
1691             mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1692         break;
1693     }
1694     default:
1695         return M_PROPERTY_NOT_IMPLEMENTED;
1696     }
1697     --mpctx->global_sub_pos;
1698     return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1699 }
1700
1701 /// Selected subtitles from specific source (RW)
1702 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1703                                    MPContext *mpctx)
1704 {
1705     int source, is_cur_source, offset, new_pos;
1706     update_global_sub_size(mpctx);
1707     if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1708         return M_PROPERTY_UNAVAILABLE;
1709
1710     if (!strcmp(prop->name, "sub_file"))
1711         source = SUB_SOURCE_SUBS;
1712     else if (!strcmp(prop->name, "sub_vob"))
1713         source = SUB_SOURCE_VOBSUB;
1714     else if (!strcmp(prop->name, "sub_demux"))
1715         source = SUB_SOURCE_DEMUX;
1716     else
1717         return M_PROPERTY_ERROR;
1718
1719     offset = sub_pos_by_source(mpctx, source);
1720     if (offset < 0)
1721         return M_PROPERTY_UNAVAILABLE;
1722
1723     is_cur_source = sub_source(mpctx) == source;
1724     new_pos = mpctx->global_sub_pos;
1725     switch (action) {
1726     case M_PROPERTY_GET:
1727         if (!arg)
1728             return M_PROPERTY_ERROR;
1729         if (is_cur_source) {
1730             *(int *) arg = sub_source_pos(mpctx);
1731             if (source == SUB_SOURCE_VOBSUB)
1732                 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1733         }
1734         else
1735             *(int *) arg = -1;
1736         return M_PROPERTY_OK;
1737     case M_PROPERTY_PRINT:
1738         if (!arg)
1739             return M_PROPERTY_ERROR;
1740         if (is_cur_source)
1741             return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1742         *(char **) arg = malloc(64);
1743         (*(char **) arg)[63] = 0;
1744         snprintf(*(char **) arg, 63, MSGTR_Disabled);
1745         return M_PROPERTY_OK;
1746     case M_PROPERTY_SET:
1747         if (!arg)
1748             return M_PROPERTY_ERROR;
1749         if (*(int *) arg >= 0) {
1750             int index = *(int *)arg;
1751             if (source == SUB_SOURCE_VOBSUB)
1752                 index = vobsub_get_index_by_id(vo_vobsub, index);
1753             new_pos = offset + index;
1754             if (index < 0 || index > mpctx->sub_counts[source]) {
1755                 new_pos = -1;
1756                 *(int *) arg = -1;
1757             }
1758         }
1759         else
1760             new_pos = -1;
1761         break;
1762     case M_PROPERTY_STEP_UP:
1763     case M_PROPERTY_STEP_DOWN: {
1764         int step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
1765                        * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1766         int step = (step_all > 0) ? 1 : -1;
1767         int max_sub_pos_for_source = -1;
1768         if (!is_cur_source)
1769             new_pos = -1;
1770         while (step_all) {
1771             if (new_pos == -1) {
1772                 if (step > 0)
1773                     new_pos = offset;
1774                 else if (max_sub_pos_for_source == -1) {
1775                     // Find max pos for specific source
1776                     new_pos = mpctx->global_sub_size - 1;
1777                     while (new_pos >= 0
1778                             && sub_source(mpctx) != source)
1779                         new_pos--;
1780                     // cache for next time
1781                     max_sub_pos_for_source = new_pos;
1782                 }
1783                 else
1784                     new_pos = max_sub_pos_for_source;
1785             }
1786             else {
1787                 new_pos += step;
1788                 if (new_pos < offset ||
1789                         new_pos >= mpctx->global_sub_size ||
1790                         sub_source(mpctx) != source)
1791                     new_pos = -1;
1792             }
1793             step_all -= step;
1794         }
1795         break;
1796     }
1797     default:
1798         return M_PROPERTY_NOT_IMPLEMENTED;
1799     }
1800     return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1801 }
1802
1803 /// Subtitle delay (RW)
1804 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1805                                  MPContext *mpctx)
1806 {
1807     if (!mpctx->sh_video)
1808         return M_PROPERTY_UNAVAILABLE;
1809     return m_property_delay(prop, action, arg, &sub_delay);
1810 }
1811
1812 /// Alignment of text subtitles (RW)
1813 static int mp_property_sub_alignment(m_option_t *prop, int action,
1814                                      void *arg, MPContext *mpctx)
1815 {
1816     char *name[] = { MSGTR_Top, MSGTR_Center, MSGTR_Bottom };
1817
1818     if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1819         || sub_source(mpctx) != SUB_SOURCE_SUBS)
1820         return M_PROPERTY_UNAVAILABLE;
1821
1822     switch (action) {
1823     case M_PROPERTY_PRINT:
1824         if (!arg)
1825             return M_PROPERTY_ERROR;
1826         M_PROPERTY_CLAMP(prop, sub_alignment);
1827         *(char **) arg = strdup(name[sub_alignment]);
1828         return M_PROPERTY_OK;
1829     case M_PROPERTY_SET:
1830         if (!arg)
1831             return M_PROPERTY_ERROR;
1832     case M_PROPERTY_STEP_UP:
1833     case M_PROPERTY_STEP_DOWN:
1834         vo_osd_changed(OSDTYPE_SUBTITLE);
1835     default:
1836         return m_property_choice(prop, action, arg, &sub_alignment);
1837     }
1838 }
1839
1840 /// Subtitle visibility (RW)
1841 static int mp_property_sub_visibility(m_option_t *prop, int action,
1842                                       void *arg, MPContext *mpctx)
1843 {
1844     if (!mpctx->sh_video)
1845         return M_PROPERTY_UNAVAILABLE;
1846
1847     switch (action) {
1848     case M_PROPERTY_SET:
1849         if (!arg)
1850             return M_PROPERTY_ERROR;
1851     case M_PROPERTY_STEP_UP:
1852     case M_PROPERTY_STEP_DOWN:
1853         vo_osd_changed(OSDTYPE_SUBTITLE);
1854         if (vo_spudec)
1855             vo_osd_changed(OSDTYPE_SPU);
1856     default:
1857         return m_property_flag(prop, action, arg, &sub_visibility);
1858     }
1859 }
1860
1861 #ifdef CONFIG_ASS
1862 /// Use margins for libass subtitles (RW)
1863 static int mp_property_ass_use_margins(m_option_t *prop, int action,
1864                                       void *arg, MPContext *mpctx)
1865 {
1866     if (!mpctx->sh_video)
1867         return M_PROPERTY_UNAVAILABLE;
1868
1869     switch (action) {
1870     case M_PROPERTY_SET:
1871         if (!arg)
1872             return M_PROPERTY_ERROR;
1873     case M_PROPERTY_STEP_UP:
1874     case M_PROPERTY_STEP_DOWN:
1875         ass_force_reload = 1;
1876     default:
1877         return m_property_flag(prop, action, arg, &ass_use_margins);
1878     }
1879 }
1880 #endif
1881
1882 /// Show only forced subtitles (RW)
1883 static int mp_property_sub_forced_only(m_option_t *prop, int action,
1884                                        void *arg, MPContext *mpctx)
1885 {
1886     if (!vo_spudec)
1887         return M_PROPERTY_UNAVAILABLE;
1888
1889     switch (action) {
1890     case M_PROPERTY_SET:
1891         if (!arg)
1892             return M_PROPERTY_ERROR;
1893     case M_PROPERTY_STEP_UP:
1894     case M_PROPERTY_STEP_DOWN:
1895         m_property_flag(prop, action, arg, &forced_subs_only);
1896         spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
1897         return M_PROPERTY_OK;
1898     default:
1899         return m_property_flag(prop, action, arg, &forced_subs_only);
1900     }
1901
1902 }
1903
1904 #ifdef CONFIG_FREETYPE
1905 /// Subtitle scale (RW)
1906 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
1907                               MPContext *mpctx)
1908 {
1909
1910     switch (action) {
1911         case M_PROPERTY_SET:
1912             if (!arg)
1913                 return M_PROPERTY_ERROR;
1914             M_PROPERTY_CLAMP(prop, *(float *) arg);
1915 #ifdef CONFIG_ASS
1916             if (ass_enabled) {
1917                 ass_font_scale = *(float *) arg;
1918                 ass_force_reload = 1;
1919             }
1920 #endif
1921             text_font_scale_factor = *(float *) arg;
1922             force_load_font = 1;
1923             vo_osd_changed(OSDTYPE_SUBTITLE);
1924             return M_PROPERTY_OK;
1925         case M_PROPERTY_STEP_UP:
1926         case M_PROPERTY_STEP_DOWN:
1927 #ifdef CONFIG_ASS
1928             if (ass_enabled) {
1929                 ass_font_scale += (arg ? *(float *) arg : 0.1)*
1930                   (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
1931                 M_PROPERTY_CLAMP(prop, ass_font_scale);
1932                 ass_force_reload = 1;
1933             }
1934 #endif
1935             text_font_scale_factor += (arg ? *(float *) arg : 0.1)*
1936               (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
1937             M_PROPERTY_CLAMP(prop, text_font_scale_factor);
1938             force_load_font = 1;
1939             vo_osd_changed(OSDTYPE_SUBTITLE);
1940             return M_PROPERTY_OK;
1941         default:
1942 #ifdef CONFIG_ASS
1943             if (ass_enabled)
1944                 return m_property_float_ro(prop, action, arg, ass_font_scale);
1945             else
1946 #endif
1947                 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
1948     }
1949 }
1950 #endif
1951
1952 ///@}
1953
1954 /// \defgroup TVProperties TV properties
1955 /// \ingroup Properties
1956 ///@{
1957
1958 #ifdef CONFIG_TV
1959
1960 /// TV color settings (RW)
1961 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
1962                                 MPContext *mpctx)
1963 {
1964     int r, val;
1965     tvi_handle_t *tvh = mpctx->demuxer->priv;
1966     if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
1967         return M_PROPERTY_UNAVAILABLE;
1968
1969     switch (action) {
1970     case M_PROPERTY_SET:
1971         if (!arg)
1972             return M_PROPERTY_ERROR;
1973         M_PROPERTY_CLAMP(prop, *(int *) arg);
1974         return tv_set_color_options(tvh, (intptr_t) prop->priv, *(int *) arg);
1975     case M_PROPERTY_GET:
1976         return tv_get_color_options(tvh, (intptr_t) prop->priv, arg);
1977     case M_PROPERTY_STEP_UP:
1978     case M_PROPERTY_STEP_DOWN:
1979         if ((r = tv_get_color_options(tvh, (intptr_t) prop->priv, &val)) >= 0) {
1980             if (!r)
1981                 return M_PROPERTY_ERROR;
1982             val += (arg ? *(int *) arg : 1) *
1983                 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1984             M_PROPERTY_CLAMP(prop, val);
1985             return tv_set_color_options(tvh, (intptr_t) prop->priv, val);
1986         }
1987         return M_PROPERTY_ERROR;
1988     }
1989     return M_PROPERTY_NOT_IMPLEMENTED;
1990 }
1991
1992 #endif
1993
1994 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
1995                   MPContext *mpctx)
1996 {
1997     int val,result;
1998     int base_ioctl=(intptr_t)prop->priv;
1999     /*
2000       for teletext's GET,SET,STEP ioctls this is not 0
2001       SET is GET+1
2002       STEP is GET+2
2003     */
2004     if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2005         return M_PROPERTY_UNAVAILABLE;
2006     if(!base_ioctl)
2007         return M_PROPERTY_ERROR;
2008
2009     switch (action) {
2010     case M_PROPERTY_GET:
2011         if (!arg)
2012             return M_PROPERTY_ERROR;
2013         result=teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2014         break;
2015     case M_PROPERTY_SET:
2016         if (!arg)
2017             return M_PROPERTY_ERROR;
2018         M_PROPERTY_CLAMP(prop, *(int *) arg);
2019         result=teletext_control(mpctx->demuxer->teletext, base_ioctl+1, arg);
2020         break;
2021     case M_PROPERTY_STEP_UP:
2022     case M_PROPERTY_STEP_DOWN:
2023         result=teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2024         val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2025         result=teletext_control(mpctx->demuxer->teletext, base_ioctl+1, &val);
2026         break;
2027     default:
2028         return M_PROPERTY_NOT_IMPLEMENTED;
2029     }
2030
2031     return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2032 }
2033
2034 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2035                   MPContext *mpctx)
2036 {
2037     int result;
2038     int val;
2039
2040     //with tvh==NULL will fail too
2041     result=mp_property_teletext_common(prop,action,arg,mpctx);
2042     if(result!=M_PROPERTY_OK)
2043         return result;
2044
2045     if(teletext_control(mpctx->demuxer->teletext,
2046                         (intptr_t)prop->priv, &val)==VBI_CONTROL_TRUE && val)
2047         mp_input_set_section("teletext");
2048     else
2049         mp_input_set_section("tv");
2050     return M_PROPERTY_OK;
2051 }
2052
2053 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2054                   MPContext *mpctx)
2055 {
2056     int result;
2057     int val;
2058     if (!mpctx->demuxer->teletext)
2059         return M_PROPERTY_UNAVAILABLE;
2060     switch(action){
2061     case M_PROPERTY_STEP_UP:
2062     case M_PROPERTY_STEP_DOWN:
2063         //This should be handled separately
2064         val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2065         result=teletext_control(mpctx->demuxer->teletext,
2066                                 TV_VBI_CONTROL_STEP_PAGE, &val);
2067         break;
2068     default:
2069         result=mp_property_teletext_common(prop,action,arg,mpctx);
2070     }
2071     return result;
2072 }
2073
2074 ///@}
2075
2076 /// All properties available in MPlayer.
2077 /** \ingroup Properties
2078  */
2079 static const m_option_t mp_properties[] = {
2080     // General
2081     { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2082      M_OPT_RANGE, 0, 3, NULL },
2083     { "loop", mp_property_loop, CONF_TYPE_INT,
2084      M_OPT_MIN, -1, 0, NULL },
2085     { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2086      M_OPT_RANGE, 0.01, 100.0, NULL },
2087     { "filename", mp_property_filename, CONF_TYPE_STRING,
2088      0, 0, 0, NULL },
2089     { "path", mp_property_path, CONF_TYPE_STRING,
2090      0, 0, 0, NULL },
2091     { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2092      0, 0, 0, NULL },
2093     { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2094      M_OPT_MIN, 0, 0, NULL },
2095     { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2096      M_OPT_MIN, 0, 0, NULL },
2097     { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2098      M_OPT_MIN, 0, 0, NULL },
2099     { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2100      M_OPT_MIN, 0, 0, NULL },
2101     { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2102      M_OPT_MIN, 0, 0, NULL },
2103     { "length", mp_property_length, CONF_TYPE_TIME,
2104      M_OPT_MIN, 0, 0, NULL },
2105     { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2106      M_OPT_RANGE, 0, 100, NULL },
2107     { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2108      M_OPT_MIN, 0, 0, NULL },
2109     { "chapter", mp_property_chapter, CONF_TYPE_INT,
2110      M_OPT_MIN, 0, 0, NULL },
2111     { "titles", mp_property_titles, CONF_TYPE_INT,
2112      0, 0, 0, NULL },
2113     { "chapters", mp_property_chapters, CONF_TYPE_INT,
2114      0, 0, 0, NULL },
2115     { "angle", mp_property_angle, CONF_TYPE_INT,
2116      CONF_RANGE, -2, 10, NULL },
2117     { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2118      0, 0, 0, NULL },
2119     { "pause", mp_property_pause, CONF_TYPE_FLAG,
2120      M_OPT_RANGE, 0, 1, NULL },
2121     { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2122      M_OPT_RANGE, 0, 1, NULL },
2123
2124     // Audio
2125     { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2126      M_OPT_RANGE, 0, 100, NULL },
2127     { "mute", mp_property_mute, CONF_TYPE_FLAG,
2128      M_OPT_RANGE, 0, 1, NULL },
2129     { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2130      M_OPT_RANGE, -100, 100, NULL },
2131     { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2132      0, 0, 0, NULL },
2133     { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2134      0, 0, 0, NULL },
2135     { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2136      0, 0, 0, NULL },
2137     { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2138      0, 0, 0, NULL },
2139     { "channels", mp_property_channels, CONF_TYPE_INT,
2140      0, 0, 0, NULL },
2141     { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2142      CONF_RANGE, -2, 65535, NULL },
2143     { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2144      M_OPT_RANGE, -1, 1, NULL },
2145
2146     // Video
2147     { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2148      M_OPT_RANGE, 0, 1, NULL },
2149     { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2150      M_OPT_RANGE, 0, 1, NULL },
2151     { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2152      M_OPT_RANGE, 0, 1, NULL },
2153     { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2154      M_OPT_RANGE, 0, 1, NULL },
2155     { "border", mp_property_border, CONF_TYPE_FLAG,
2156      M_OPT_RANGE, 0, 1, NULL },
2157     { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2158      M_OPT_RANGE, 0, 2, NULL },
2159     { "gamma", mp_property_gamma, CONF_TYPE_INT,
2160      M_OPT_RANGE, -100, 100, &vo_gamma_gamma },
2161     { "brightness", mp_property_gamma, CONF_TYPE_INT,
2162      M_OPT_RANGE, -100, 100, &vo_gamma_brightness },
2163     { "contrast", mp_property_gamma, CONF_TYPE_INT,
2164      M_OPT_RANGE, -100, 100, &vo_gamma_contrast },
2165     { "saturation", mp_property_gamma, CONF_TYPE_INT,
2166      M_OPT_RANGE, -100, 100, &vo_gamma_saturation },
2167     { "hue", mp_property_gamma, CONF_TYPE_INT,
2168      M_OPT_RANGE, -100, 100, &vo_gamma_hue },
2169     { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2170      M_OPT_RANGE, 0, 1, NULL },
2171     { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2172      M_OPT_RANGE, 0, 1, NULL },
2173     { "video_format", mp_property_video_format, CONF_TYPE_INT,
2174      0, 0, 0, NULL },
2175     { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2176      0, 0, 0, NULL },
2177     { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2178      0, 0, 0, NULL },
2179     { "width", mp_property_width, CONF_TYPE_INT,
2180      0, 0, 0, NULL },
2181     { "height", mp_property_height, CONF_TYPE_INT,
2182      0, 0, 0, NULL },
2183     { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2184      0, 0, 0, NULL },
2185     { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2186      0, 0, 0, NULL },
2187     { "switch_video", mp_property_video, CONF_TYPE_INT,
2188      CONF_RANGE, -2, 65535, NULL },
2189     { "switch_program", mp_property_program, CONF_TYPE_INT,
2190      CONF_RANGE, -1, 65535, NULL },
2191
2192     // Subs
2193     { "sub", mp_property_sub, CONF_TYPE_INT,
2194      M_OPT_MIN, -1, 0, NULL },
2195     { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2196      M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2197     { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2198      M_OPT_MIN, -1, 0, NULL },
2199     { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2200      M_OPT_MIN, -1, 0, NULL },
2201     { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2202      M_OPT_MIN, -1, 0, NULL },
2203     { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2204      0, 0, 0, NULL },
2205     { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2206      M_OPT_RANGE, 0, 100, NULL },
2207     { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2208      M_OPT_RANGE, 0, 2, NULL },
2209     { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2210      M_OPT_RANGE, 0, 1, NULL },
2211     { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2212      M_OPT_RANGE, 0, 1, NULL },
2213 #ifdef CONFIG_FREETYPE
2214     { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2215      M_OPT_RANGE, 0, 100, NULL },
2216 #endif
2217 #ifdef CONFIG_ASS
2218     { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2219      M_OPT_RANGE, 0, 1, NULL },
2220 #endif
2221
2222 #ifdef CONFIG_TV
2223     { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2224      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_BRIGHTNESS },
2225     { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2226      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_CONTRAST },
2227     { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2228      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_SATURATION },
2229     { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2230      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_HUE },
2231 #endif
2232     { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2233      M_OPT_RANGE, 100, 899,  (void*)TV_VBI_CONTROL_GET_PAGE },
2234     { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2235      M_OPT_RANGE, 0, 64, (void*)TV_VBI_CONTROL_GET_SUBPAGE },
2236     { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2237      M_OPT_RANGE, 0, 1, (void*)TV_VBI_CONTROL_GET_MODE },
2238     { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2239      M_OPT_RANGE, 0, 3, (void*)TV_VBI_CONTROL_GET_FORMAT },
2240     { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2241      M_OPT_RANGE, 0, 2, (void*)TV_VBI_CONTROL_GET_HALF_PAGE },
2242     { NULL, NULL, NULL, 0, 0, 0, NULL }
2243 };
2244
2245
2246 int mp_property_do(const char *name, int action, void *val, void *ctx)
2247 {
2248     return m_property_do(mp_properties, name, action, val, ctx);
2249 }
2250
2251 char* mp_property_print(const char *name, void* ctx)
2252 {
2253     char* ret = NULL;
2254     if(mp_property_do(name,M_PROPERTY_PRINT,&ret,ctx) <= 0)
2255         return NULL;
2256     return ret;
2257 }
2258
2259 char *property_expand_string(MPContext *mpctx, char *str)
2260 {
2261     return m_properties_expand_string(mp_properties, str, mpctx);
2262 }
2263
2264 void property_print_help(void)
2265 {
2266     m_properties_print_help_list(mp_properties);
2267 }
2268
2269
2270 ///@}
2271 // Properties group
2272
2273
2274 /**
2275  * \defgroup Command2Property Command to property bridge
2276  *
2277  * It is used to handle most commands that just set a property
2278  * and optionally display something on the OSD.
2279  * Two kinds of commands are handled: adjust or toggle.
2280  *
2281  * Adjust commands take 1 or 2 parameters: <value> <abs>
2282  * If <abs> is non-zero the property is set to the given value
2283  * otherwise it is adjusted.
2284  *
2285  * Toggle commands take 0 or 1 parameters. With no parameter
2286  * or a value less than the property minimum it just steps the
2287  * property to its next or previous value respectively.
2288  * Otherwise it sets it to the given value.
2289  *
2290  *@{
2291  */
2292
2293 /// List of the commands that can be handled by setting a property.
2294 static struct {
2295     /// property name
2296     const char *name;
2297     /// cmd id
2298     int cmd;
2299     /// set/adjust or toggle command
2300     int toggle;
2301     /// progressbar type
2302     int osd_progbar;
2303     /// osd msg id if it must be shared
2304     int osd_id;
2305     /// osd msg template
2306     const char *osd_msg;
2307 } set_prop_cmd[] = {
2308     // general
2309     { "loop", MP_CMD_LOOP, 0, 0, -1, MSGTR_LoopStatus },
2310     { "chapter", MP_CMD_SEEK_CHAPTER, 0, 0, -1, NULL },
2311     { "angle", MP_CMD_SWITCH_ANGLE, 0, 0, -1, NULL },
2312     { "capturing", MP_CMD_CAPTURING, 1, 0, -1, NULL },
2313     // audio
2314     { "volume", MP_CMD_VOLUME, 0, OSD_VOLUME, -1, MSGTR_Volume },
2315     { "mute", MP_CMD_MUTE, 1, 0, -1, MSGTR_MuteStatus },
2316     { "audio_delay", MP_CMD_AUDIO_DELAY, 0, 0, -1, MSGTR_AVDelayStatus },
2317     { "switch_audio", MP_CMD_SWITCH_AUDIO, 1, 0, -1, MSGTR_OSDAudio },
2318     { "balance", MP_CMD_BALANCE, 0, OSD_BALANCE, -1, MSGTR_Balance },
2319     // video
2320     { "fullscreen", MP_CMD_VO_FULLSCREEN, 1, 0, -1, NULL },
2321     { "panscan", MP_CMD_PANSCAN, 0, OSD_PANSCAN, -1, MSGTR_Panscan },
2322     { "ontop", MP_CMD_VO_ONTOP, 1, 0, -1, MSGTR_OnTopStatus },
2323     { "rootwin", MP_CMD_VO_ROOTWIN, 1, 0, -1, MSGTR_RootwinStatus },
2324     { "border", MP_CMD_VO_BORDER, 1, 0, -1, MSGTR_BorderStatus },
2325     { "framedropping", MP_CMD_FRAMEDROPPING, 1, 0, -1, MSGTR_FramedroppingStatus },
2326     { "gamma", MP_CMD_GAMMA, 0, OSD_BRIGHTNESS, -1, MSGTR_Gamma },
2327     { "brightness", MP_CMD_BRIGHTNESS, 0, OSD_BRIGHTNESS, -1, MSGTR_Brightness },
2328     { "contrast", MP_CMD_CONTRAST, 0, OSD_CONTRAST, -1, MSGTR_Contrast },
2329     { "saturation", MP_CMD_SATURATION, 0, OSD_SATURATION, -1, MSGTR_Saturation },
2330     { "hue", MP_CMD_HUE, 0, OSD_HUE, -1, MSGTR_Hue },
2331     { "vsync", MP_CMD_SWITCH_VSYNC, 1, 0, -1, MSGTR_VSyncStatus },
2332         // subs
2333     { "sub", MP_CMD_SUB_SELECT, 1, 0, -1, MSGTR_SubSelectStatus },
2334     { "sub_source", MP_CMD_SUB_SOURCE, 1, 0, -1, MSGTR_SubSourceStatus },
2335     { "sub_vob", MP_CMD_SUB_VOB, 1, 0, -1, MSGTR_SubSelectStatus },
2336     { "sub_demux", MP_CMD_SUB_DEMUX, 1, 0, -1, MSGTR_SubSelectStatus },
2337     { "sub_file", MP_CMD_SUB_FILE, 1, 0, -1, MSGTR_SubSelectStatus },
2338     { "sub_pos", MP_CMD_SUB_POS, 0, 0, -1, MSGTR_SubPosStatus },
2339     { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1, 0, -1, MSGTR_SubAlignStatus },
2340     { "sub_delay", MP_CMD_SUB_DELAY, 0, 0, OSD_MSG_SUB_DELAY, MSGTR_SubDelayStatus },
2341     { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1, 0, -1, MSGTR_SubVisibleStatus },
2342     { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1, 0, -1, MSGTR_SubForcedOnlyStatus },
2343 #ifdef CONFIG_FREETYPE
2344     { "sub_scale", MP_CMD_SUB_SCALE, 0, 0, -1, MSGTR_SubScale},
2345 #endif
2346 #ifdef CONFIG_ASS
2347     { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1, 0, -1, NULL },
2348 #endif
2349 #ifdef CONFIG_TV
2350     { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0, OSD_BRIGHTNESS, -1, MSGTR_Brightness },
2351     { "tv_hue", MP_CMD_TV_SET_HUE, 0, OSD_HUE, -1, MSGTR_Hue },
2352     { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0, OSD_SATURATION, -1, MSGTR_Saturation },
2353     { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0, OSD_CONTRAST, -1, MSGTR_Contrast },
2354 #endif
2355     { NULL, 0, 0, 0, -1, NULL }
2356 };
2357
2358
2359 /// Handle commands that set a property.
2360 static int set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2361 {
2362     int i, r;
2363     m_option_t *prop;
2364     const char *pname;
2365
2366     // look for the command
2367     for (i = 0; set_prop_cmd[i].name; i++)
2368         if (set_prop_cmd[i].cmd == cmd->id)
2369             break;
2370     if (!(pname = set_prop_cmd[i].name))
2371         return 0;
2372
2373     if (mp_property_do(pname,M_PROPERTY_GET_TYPE,&prop,mpctx) <= 0 || !prop)
2374         return 0;
2375
2376     // toggle command
2377     if (set_prop_cmd[i].toggle) {
2378         // set to value
2379         if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2380             r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2381         else if (cmd->nargs > 0)
2382             r = mp_property_do(pname, M_PROPERTY_STEP_DOWN, NULL, mpctx);
2383         else
2384             r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2385     } else if (cmd->args[1].v.i)        //set
2386         r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2387     else                        // adjust
2388         r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2389
2390     if (r <= 0)
2391         return 1;
2392
2393     if (set_prop_cmd[i].osd_progbar) {
2394         if (prop->type == CONF_TYPE_INT) {
2395             if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2396                 set_osd_bar(set_prop_cmd[i].osd_progbar,
2397                             set_prop_cmd[i].osd_msg, prop->min, prop->max, r);
2398         } else if (prop->type == CONF_TYPE_FLOAT) {
2399             float f;
2400             if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2401                 set_osd_bar(set_prop_cmd[i].osd_progbar,
2402                             set_prop_cmd[i].osd_msg, prop->min, prop->max, f);
2403         } else
2404             mp_msg(MSGT_CPLAYER, MSGL_ERR,
2405                    "Property use an unsupported type.\n");
2406         return 1;
2407     }
2408
2409     if (set_prop_cmd[i].osd_msg) {
2410         char *val = mp_property_print(pname, mpctx);
2411         if (val) {
2412             set_osd_msg(set_prop_cmd[i].osd_id >=
2413                         0 ? set_prop_cmd[i].osd_id : OSD_MSG_PROPERTY + i,
2414                         1, osd_duration, set_prop_cmd[i].osd_msg, val);
2415             free(val);
2416         }
2417     }
2418     return 1;
2419 }
2420
2421 #ifdef CONFIG_DVDNAV
2422 static const struct {
2423   const char *name;
2424   const mp_command_type cmd;
2425 } mp_dvdnav_bindings[] = {
2426   { "up",       MP_CMD_DVDNAV_UP              },
2427   { "down",     MP_CMD_DVDNAV_DOWN            },
2428   { "left",     MP_CMD_DVDNAV_LEFT            },
2429   { "right",    MP_CMD_DVDNAV_RIGHT           },
2430   { "menu",     MP_CMD_DVDNAV_MENU            },
2431   { "select",   MP_CMD_DVDNAV_SELECT          },
2432   { "prev",     MP_CMD_DVDNAV_PREVMENU        },
2433   { "mouse",    MP_CMD_DVDNAV_MOUSECLICK      },
2434
2435   /*
2436    * keep old dvdnav sub-command options for a while in order not to
2437    *  break slave-mode API too suddenly.
2438    */
2439   { "1",        MP_CMD_DVDNAV_UP              },
2440   { "2",        MP_CMD_DVDNAV_DOWN            },
2441   { "3",        MP_CMD_DVDNAV_LEFT            },
2442   { "4",        MP_CMD_DVDNAV_RIGHT           },
2443   { "5",        MP_CMD_DVDNAV_MENU            },
2444   { "6",        MP_CMD_DVDNAV_SELECT          },
2445   { "7",        MP_CMD_DVDNAV_PREVMENU        },
2446   { "8",        MP_CMD_DVDNAV_MOUSECLICK      },
2447   { NULL,       0                             }
2448 };
2449 #endif
2450
2451 static const char *property_error_string(int error_value)
2452 {
2453     switch (error_value) {
2454     case M_PROPERTY_ERROR:
2455         return "ERROR";
2456     case M_PROPERTY_UNAVAILABLE:
2457         return "PROPERTY_UNAVAILABLE";
2458     case M_PROPERTY_NOT_IMPLEMENTED:
2459         return "NOT_IMPLEMENTED";
2460     case M_PROPERTY_UNKNOWN:
2461         return "PROPERTY_UNKNOWN";
2462     case M_PROPERTY_DISABLED:
2463         return "DISABLED";
2464     }
2465     return "UNKNOWN";
2466 }
2467 ///@}
2468
2469 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2470 {
2471     int idx;
2472     int end = start + count;
2473     int after = mpctx->set_of_sub_size - end;
2474     sub_data **subs = mpctx->set_of_subtitles;
2475 #ifdef CONFIG_ASS
2476     ASS_Track **ass_tracks = mpctx->set_of_ass_tracks;
2477 #endif
2478     if (count < 0 || count > mpctx->set_of_sub_size ||
2479         start < 0 || start > mpctx->set_of_sub_size - count) {
2480         mp_msg(MSGT_CPLAYER, MSGL_ERR,
2481                "Cannot remove invalid subtitle range %i +%i\n", start, count);
2482         return;
2483     }
2484     for (idx = start; idx < end; idx++) {
2485         sub_data *subd = subs[idx];
2486         mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2487                MSGTR_RemovedSubtitleFile, idx + 1,
2488                filename_recode(subd->filename));
2489         sub_free(subd);
2490         subs[idx] = NULL;
2491 #ifdef CONFIG_ASS
2492         if (ass_tracks[idx])
2493             ass_free_track(ass_tracks[idx]);
2494         ass_tracks[idx] = NULL;
2495 #endif
2496     }
2497
2498     mpctx->global_sub_size -= count;
2499     mpctx->set_of_sub_size -= count;
2500     if (mpctx->set_of_sub_size <= 0)
2501         mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2502
2503     memmove(subs + start, subs + end, after * sizeof(*subs));
2504     memset(subs + start + after, 0, count * sizeof(*subs));
2505 #ifdef CONFIG_ASS
2506     memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2507     memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2508 #endif
2509
2510     if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2511         mpctx->global_sub_pos = -2;
2512         subdata = NULL;
2513 #ifdef CONFIG_ASS
2514         ass_track = NULL;
2515 #endif
2516         mp_input_queue_cmd(mp_input_parse_cmd("sub_select"));
2517     } else if (mpctx->set_of_sub_pos >= end) {
2518         mpctx->set_of_sub_pos -= count;
2519         mpctx->global_sub_pos -= count;
2520     }
2521 }
2522
2523 static int overlay_source_registered = 0;
2524 static struct mp_eosd_source overlay_source = {
2525     .z_index = 5,
2526 };
2527
2528 static void overlay_add(char *file, int id, int x, int y, unsigned col)
2529 {
2530     FILE *f;
2531     int w, h, bpp, maxval;
2532     uint8_t *data;
2533     struct mp_eosd_image *img;
2534
2535     f = fopen(file, "rb");
2536     if (!f) {
2537         mp_msg(MSGT_CPLAYER, MSGL_ERR, "overlay_add: unable to open file.\n");
2538         return;
2539     }
2540     data = read_pnm(f, &w, &h, &bpp, &maxval);
2541     fclose(f);
2542     if (!data) {
2543         mp_msg(MSGT_CPLAYER, MSGL_ERR, "overlay_add: unable to load file.\n");
2544         return;
2545     }
2546     if (bpp != 1 || maxval != 255) {
2547         mp_msg(MSGT_CPLAYER, MSGL_ERR,
2548                "overlay_add: file format not supported.\n");
2549         return;
2550     }
2551     if (!overlay_source_registered) {
2552         eosd_register(&overlay_source);
2553         eosd_image_remove_all(&overlay_source);
2554         overlay_source_registered = 1;
2555     }
2556     img = eosd_image_alloc();
2557     img->w      = w;
2558     img->h      = h;
2559     img->stride = w;
2560     img->bitmap = data;
2561     img->color  = col ^ 0xFF; /* col is RGBA, img->color is RGBT */
2562     img->dst_x  = x;
2563     img->dst_y  = y;
2564     img->opaque = (void *)(intptr_t)id;
2565     eosd_image_append(&overlay_source, img);
2566     overlay_source.changed = EOSD_CHANGED_BITMAP;
2567 }
2568
2569 static void overlay_remove(int id)
2570 {
2571     struct mp_eosd_image *img, **prev, *next;
2572     prev = &overlay_source.images;
2573     img  = overlay_source.images;
2574     while (img) {
2575         next = img->next;
2576         if ((intptr_t)img->opaque == id) {
2577             free(img->bitmap);
2578             eosd_image_remove(&overlay_source, img, prev);
2579             overlay_source.changed = EOSD_CHANGED_BITMAP;
2580         } else {
2581             prev = &img->next;
2582         }
2583         img  = next;
2584     }
2585 }
2586
2587 int run_command(MPContext *mpctx, mp_cmd_t *cmd)
2588 {
2589     sh_audio_t * const sh_audio = mpctx->sh_audio;
2590     sh_video_t * const sh_video = mpctx->sh_video;
2591     int brk_cmd = 0;
2592     if (!set_property_command(mpctx, cmd))
2593         switch (cmd->id) {
2594         case MP_CMD_SEEK:{
2595                 float v;
2596                 int abs;
2597                 if (sh_video)
2598                     mpctx->osd_show_percentage = sh_video->fps;
2599                 v = cmd->args[0].v.f;
2600                 abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2601                 if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */
2602                     abs_seek_pos = SEEK_ABSOLUTE;
2603                     if (sh_video)
2604                         mpctx->osd_function =
2605                             (v > sh_video->pts) ? OSD_FFW : OSD_REW;
2606                     rel_seek_secs = v;
2607                 } else if (abs) {       /* Absolute seek by percentage */
2608                     abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR;
2609                     if (sh_video)
2610                         mpctx->osd_function = OSD_FFW;  // Direction isn't set correctly
2611                     rel_seek_secs = v / 100.0;
2612                 } else {
2613                     rel_seek_secs += v;
2614                     mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2615                 }
2616                 brk_cmd = 1;
2617             }
2618             break;
2619
2620         case MP_CMD_SET_PROPERTY:{
2621                 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2622                                        cmd->args[1].v.s, mpctx);
2623                 if (r == M_PROPERTY_UNKNOWN)
2624                     mp_msg(MSGT_CPLAYER, MSGL_WARN,
2625                            "Unknown property: '%s'\n", cmd->args[0].v.s);
2626                 else if (r <= 0)
2627                     mp_msg(MSGT_CPLAYER, MSGL_WARN,
2628                            "Failed to set property '%s' to '%s'.\n",
2629                            cmd->args[0].v.s, cmd->args[1].v.s);
2630                 if (r <= 0)
2631                     mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2632             }
2633             break;
2634
2635         case MP_CMD_STEP_PROPERTY:{
2636                 void* arg = NULL;
2637                 int r,i;
2638                 double d;
2639                 off_t o;
2640                 if (cmd->args[1].v.f) {
2641                     m_option_t *prop;
2642                     if((r = mp_property_do(cmd->args[0].v.s,
2643                                            M_PROPERTY_GET_TYPE,
2644                                            &prop, mpctx)) <= 0)
2645                         goto step_prop_err;
2646                     if(prop->type == CONF_TYPE_INT ||
2647                        prop->type == CONF_TYPE_FLAG)
2648                         i = cmd->args[1].v.f, arg = &i;
2649                     else if(prop->type == CONF_TYPE_FLOAT)
2650                         arg = &cmd->args[1].v.f;
2651                     else if(prop->type == CONF_TYPE_DOUBLE ||
2652                             prop->type == CONF_TYPE_TIME)
2653                         d = cmd->args[1].v.f, arg = &d;
2654                     else if(prop->type == CONF_TYPE_POSITION)
2655                         o = cmd->args[1].v.f, arg = &o;
2656                     else
2657                         mp_msg(MSGT_CPLAYER, MSGL_WARN,
2658                                "Ignoring step size stepping property '%s'.\n",
2659                                cmd->args[0].v.s);
2660                 }
2661                 r = mp_property_do(cmd->args[0].v.s,
2662                                    cmd->args[2].v.i < 0 ?
2663                                    M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2664                                    arg, mpctx);
2665             step_prop_err:
2666                 if (r == M_PROPERTY_UNKNOWN)
2667                     mp_msg(MSGT_CPLAYER, MSGL_WARN,
2668                            "Unknown property: '%s'\n", cmd->args[0].v.s);
2669                 else if (r <= 0)
2670                     mp_msg(MSGT_CPLAYER, MSGL_WARN,
2671                            "Failed to increment property '%s' by %f.\n",
2672                            cmd->args[0].v.s, cmd->args[1].v.f);
2673                 if (r <= 0)
2674                     mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2675             }
2676             break;
2677
2678         case MP_CMD_GET_PROPERTY:{
2679                 char *tmp;
2680                 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2681                                        &tmp, mpctx);
2682                 if (r <= 0) {
2683                     mp_msg(MSGT_CPLAYER, MSGL_WARN,
2684                            "Failed to get value of property '%s'.\n",
2685                            cmd->args[0].v.s);
2686                     mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2687                     break;
2688                 }
2689                 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2690                        cmd->args[0].v.s, tmp);
2691                 free(tmp);
2692             }
2693             break;
2694
2695         case MP_CMD_EDL_MARK:
2696             if (edl_fd) {
2697                 float v = sh_video ? sh_video->pts :
2698                     playing_audio_pts(sh_audio, mpctx->d_audio,
2699                                       mpctx->audio_out);
2700
2701                 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2702                     mpctx->begin_skip = v;
2703                     mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_EdloutStartSkip);
2704                 } else {
2705                     if (mpctx->begin_skip > v)
2706                         mp_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_EdloutBadStop);
2707                     else {
2708                         double pts = edl_start_pts ? start_pts : 0;
2709                         fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip - pts, v - pts, 0);
2710                         mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_EdloutEndSkip);
2711                     }
2712                     mpctx->begin_skip = MP_NOPTS_VALUE;
2713                 }
2714             }
2715             break;
2716
2717         case MP_CMD_SWITCH_RATIO:
2718             if (!sh_video)
2719                 break;
2720             if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2721                 movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2722             else
2723                 movie_aspect = cmd->args[0].v.f;
2724             mpcodecs_config_vo(sh_video, sh_video->disp_w, sh_video->disp_h, 0);
2725             break;
2726
2727         case MP_CMD_SPEED_INCR:{
2728                 float v = cmd->args[0].v.f;
2729                 playback_speed += v;
2730                 reinit_audio_chain();
2731                 set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDSpeed,
2732                             playback_speed);
2733             } break;
2734
2735         case MP_CMD_SPEED_MULT:{
2736                 float v = cmd->args[0].v.f;
2737                 playback_speed *= v;
2738                 reinit_audio_chain();
2739                 set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDSpeed,
2740                             playback_speed);
2741             } break;
2742
2743         case MP_CMD_SPEED_SET:{
2744                 float v = cmd->args[0].v.f;
2745                 playback_speed = v;
2746                 reinit_audio_chain();
2747                 set_osd_msg(OSD_MSG_SPEED, 1, osd_duration, MSGTR_OSDSpeed,
2748                             playback_speed);
2749             } break;
2750
2751         case MP_CMD_FRAME_STEP:
2752         case MP_CMD_PAUSE:
2753             cmd->pausing = 1;
2754             brk_cmd = 1;
2755             break;
2756
2757         case MP_CMD_FILE_FILTER:
2758             file_filter = cmd->args[0].v.i;
2759             break;
2760
2761         case MP_CMD_QUIT:
2762             exit_player_with_rc(EXIT_QUIT,
2763                                 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2764
2765         case MP_CMD_PLAY_TREE_STEP:{
2766                 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2767                 int force = cmd->args[1].v.i;
2768
2769 #ifdef CONFIG_GUI
2770                 if (use_gui) {
2771                     int i = 0;
2772                     if (n > 0)
2773                         for (i = 0; i < n; i++)
2774                             gui(GUI_RUN_COMMAND, (void *)MP_CMD_PLAY_TREE_STEP);
2775                     else
2776                         for (i = 0; i < -1 * n; i++)
2777                             gui(GUI_RUN_COMMAND, (void *)-MP_CMD_PLAY_TREE_STEP);
2778                 } else
2779 #endif
2780                 {
2781                     if (!force && mpctx->playtree_iter) {
2782                         play_tree_iter_t *i =
2783                             play_tree_iter_new_copy(mpctx->playtree_iter);
2784                         if (play_tree_iter_step(i, n, 0) ==
2785                             PLAY_TREE_ITER_ENTRY)
2786                             mpctx->eof =
2787                                 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2788                         play_tree_iter_free(i);
2789                     } else
2790                         mpctx->eof = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2791                     if (mpctx->eof)
2792                         mpctx->play_tree_step = n;
2793                     brk_cmd = 1;
2794                 }
2795             }
2796             break;
2797
2798         case MP_CMD_PLAY_TREE_UP_STEP:{
2799                 int n = cmd->args[0].v.i > 0 ? 1 : -1;
2800                 int force = cmd->args[1].v.i;
2801
2802                 if (!force && mpctx->playtree_iter) {
2803                     play_tree_iter_t *i =
2804                         play_tree_iter_new_copy(mpctx->playtree_iter);
2805                     if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
2806                         mpctx->eof = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2807                     play_tree_iter_free(i);
2808                 } else
2809                     mpctx->eof = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2810                 brk_cmd = 1;
2811             }
2812             break;
2813
2814         case MP_CMD_PLAY_ALT_SRC_STEP:
2815             if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
2816                 int v = cmd->args[0].v.i;
2817                 if (v > 0
2818                     && mpctx->playtree_iter->file <
2819                     mpctx->playtree_iter->num_files)
2820                     mpctx->eof = PT_NEXT_SRC;
2821                 else if (v < 0 && mpctx->playtree_iter->file > 1)
2822                     mpctx->eof = PT_PREV_SRC;
2823             }
2824             brk_cmd = 1;
2825             break;
2826
2827         case MP_CMD_SUB_STEP:
2828             if (sh_video) {
2829                 int movement = cmd->args[0].v.i;
2830                 step_sub(subdata, sh_video->pts, movement);
2831 #ifdef CONFIG_ASS
2832                 if (ass_track)
2833                     sub_delay +=
2834                         ass_step_sub(ass_track,
2835                                      (sh_video->pts +
2836                                       sub_delay) * 1000 + .5, movement) / 1000.;
2837 #endif
2838                 set_osd_msg(OSD_MSG_SUB_DELAY, 1, osd_duration,
2839                             MSGTR_OSDSubDelay, ROUND(sub_delay * 1000));
2840             }
2841             break;
2842
2843         case MP_CMD_SUB_LOG:
2844             log_sub();
2845             break;
2846
2847         case MP_CMD_OSD:{
2848                 int v = cmd->args[0].v.i;
2849                 int max = (term_osd
2850                            && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
2851                 if (osd_level > max)
2852                     osd_level = max;
2853                 if (v < 0)
2854                     osd_level = (osd_level + 1) % (max + 1);
2855                 else
2856                     osd_level = v > max ? max : v;
2857                 /* Show OSD state when disabled, but not when an explicit
2858                    argument is given to the OSD command, i.e. in slave mode. */
2859                 if (v == -1 && osd_level <= 1)
2860                     set_osd_msg(OSD_MSG_OSD_STATUS, 0, osd_duration,
2861                                 MSGTR_OSDosd,
2862                                 osd_level ? MSGTR_OSDenabled :
2863                                 MSGTR_OSDdisabled);
2864                 else
2865                     rm_osd_msg(OSD_MSG_OSD_STATUS);
2866             }
2867             break;
2868
2869         case MP_CMD_OSD_SHOW_TEXT:
2870             set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2871                         (cmd->args[1].v.i <
2872                          0 ? osd_duration : cmd->args[1].v.i),
2873                         "%-.63s", cmd->args[0].v.s);
2874             break;
2875
2876         case MP_CMD_OSD_SHOW_PROPERTY_TEXT:{
2877                 char *txt = m_properties_expand_string(mp_properties,
2878                                                        cmd->args[0].v.s,
2879                                                        mpctx);
2880                 /* if no argument supplied take default osd_duration, else <arg> ms. */
2881                 if (txt) {
2882                     set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2883                                 (cmd->args[1].v.i <
2884                                  0 ? osd_duration : cmd->args[1].v.i),
2885                                 "%-.63s", txt);
2886                     free(txt);
2887                 }
2888             }
2889             break;
2890
2891         case MP_CMD_LOADFILE:{
2892                 play_tree_t *e = play_tree_new();
2893                 play_tree_add_file(e, cmd->args[0].v.s);
2894
2895                 if (cmd->args[1].v.i)   // append
2896                     play_tree_append_entry(mpctx->playtree->child, e);
2897                 else {
2898                     // Go back to the starting point.
2899                     while (play_tree_iter_up_step
2900                            (mpctx->playtree_iter, 0, 1) != PLAY_TREE_ITER_END)
2901                         /* NOP */ ;
2902                     play_tree_free_list(mpctx->playtree->child, 1);
2903                     play_tree_set_child(mpctx->playtree, e);
2904                     pt_iter_goto_head(mpctx->playtree_iter);
2905                     mpctx->eof = PT_NEXT_SRC;
2906                 }
2907                 brk_cmd = 1;
2908             }
2909             break;
2910
2911         case MP_CMD_LOADLIST:{
2912                 play_tree_t *e = parse_playlist_file(cmd->args[0].v.s);
2913                 if (!e)
2914                     mp_msg(MSGT_CPLAYER, MSGL_ERR,
2915                            MSGTR_PlaylistLoadUnable, cmd->args[0].v.s);
2916                 else {
2917                     if (cmd->args[1].v.i)       // append
2918                         play_tree_append_entry(mpctx->playtree->child, e);
2919                     else {
2920                         // Go back to the starting point.
2921                         while (play_tree_iter_up_step
2922                                (mpctx->playtree_iter, 0, 1)
2923                                != PLAY_TREE_ITER_END)
2924                             /* NOP */ ;
2925                         play_tree_free_list(mpctx->playtree->child, 1);
2926                         play_tree_set_child(mpctx->playtree, e);
2927                         pt_iter_goto_head(mpctx->playtree_iter);
2928                         mpctx->eof = PT_NEXT_SRC;
2929                     }
2930                 }
2931                 brk_cmd = 1;
2932             }
2933             break;
2934
2935         case MP_CMD_STOP:
2936 #ifdef CONFIG_GUI
2937             // playtree_iter isn't used by the GUI
2938             if (use_gui)
2939                 gui(GUI_RUN_COMMAND, (void *)MP_CMD_STOP);
2940             else
2941 #endif
2942             // Go back to the starting point.
2943             while (play_tree_iter_up_step
2944                    (mpctx->playtree_iter, 0, 1) != PLAY_TREE_ITER_END)
2945                 /* NOP */ ;
2946             mpctx->eof = PT_STOP;
2947             brk_cmd = 1;
2948             break;
2949
2950         case MP_CMD_OSD_SHOW_PROGRESSION:{
2951                 int len = demuxer_get_time_length(mpctx->demuxer);
2952                 int pts = demuxer_get_current_time(mpctx->demuxer);
2953                 set_osd_bar(0, "Position", 0, 100, demuxer_get_percent_pos(mpctx->demuxer));
2954                 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
2955                             "%c %02d:%02d:%02d / %02d:%02d:%02d",
2956                             mpctx->osd_function, pts/3600, (pts/60)%60, pts%60,
2957                             len/3600, (len/60)%60, len%60);
2958             }
2959             break;
2960
2961 #ifdef CONFIG_RADIO
2962         case MP_CMD_RADIO_STEP_CHANNEL:
2963             if (IS_STREAMTYPE(RADIO)) {
2964                 int v = cmd->args[0].v.i;
2965                 if (v > 0)
2966                     radio_step_channel(mpctx->demuxer->stream,
2967                                        RADIO_CHANNEL_HIGHER);
2968                 else
2969                     radio_step_channel(mpctx->demuxer->stream,
2970                                        RADIO_CHANNEL_LOWER);
2971                 if (radio_get_channel_name(mpctx->demuxer->stream)) {
2972                     set_osd_msg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
2973                                 MSGTR_OSDChannel,
2974                                 radio_get_channel_name(mpctx->demuxer->stream));
2975                 }
2976             }
2977             break;
2978
2979         case MP_CMD_RADIO_SET_CHANNEL:
2980             if (IS_STREAMTYPE(RADIO)) {
2981                 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
2982                 if (radio_get_channel_name(mpctx->demuxer->stream)) {
2983                     set_osd_msg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
2984                                 MSGTR_OSDChannel,
2985                                 radio_get_channel_name(mpctx->demuxer->stream));
2986                 }
2987             }
2988             break;
2989
2990         case MP_CMD_RADIO_SET_FREQ:
2991             if (IS_STREAMTYPE(RADIO))
2992                 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
2993             break;
2994
2995         case MP_CMD_RADIO_STEP_FREQ:
2996             if (IS_STREAMTYPE(RADIO))
2997                 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
2998             break;
2999 #endif
3000
3001 #ifdef CONFIG_TV
3002         case MP_CMD_TV_START_SCAN:
3003             if (mpctx->file_format == DEMUXER_TYPE_TV)
3004                 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv),1);
3005             break;
3006         case MP_CMD_TV_SET_FREQ:
3007             if (mpctx->file_format == DEMUXER_TYPE_TV)
3008                 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3009                             cmd->args[0].v.f * 16.0);
3010 #ifdef CONFIG_PVR
3011             else if (IS_STREAMTYPE(PVR)) {
3012               pvr_set_freq (mpctx->stream, ROUND (cmd->args[0].v.f));
3013               set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3014                            pvr_get_current_channelname (mpctx->stream),
3015                            pvr_get_current_stationname (mpctx->stream));
3016             }
3017 #endif /* CONFIG_PVR */
3018             break;
3019
3020         case MP_CMD_TV_STEP_FREQ:
3021             if (mpctx->file_format == DEMUXER_TYPE_TV)
3022                 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3023                             cmd->args[0].v.f * 16.0);
3024 #ifdef CONFIG_PVR
3025             else if (IS_STREAMTYPE(PVR)) {
3026               pvr_force_freq_step (mpctx->stream, ROUND (cmd->args[0].v.f));
3027               set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3028                            pvr_get_current_channelname (mpctx->stream),
3029                            pvr_get_current_frequency (mpctx->stream));
3030             }
3031 #endif /* CONFIG_PVR */
3032             break;
3033
3034         case MP_CMD_TV_SET_NORM:
3035             if (mpctx->file_format == DEMUXER_TYPE_TV)
3036                 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3037                             cmd->args[0].v.s);
3038             break;
3039
3040         case MP_CMD_TV_STEP_CHANNEL:{
3041                 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3042                     int v = cmd->args[0].v.i;
3043                     if (v > 0) {
3044                         tv_step_channel((tvi_handle_t *) (mpctx->
3045                                                           demuxer->priv),
3046                                         TV_CHANNEL_HIGHER);
3047                     } else {
3048                         tv_step_channel((tvi_handle_t *) (mpctx->
3049                                                           demuxer->priv),
3050                                         TV_CHANNEL_LOWER);
3051                     }
3052                     if (tv_channel_list) {
3053                         set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3054                                     MSGTR_OSDChannel, tv_channel_current->name);
3055                         //vo_osd_changed(OSDTYPE_SUBTITLE);
3056                     }
3057                 }
3058 #ifdef CONFIG_PVR
3059                 else if (IS_STREAMTYPE(PVR)) {
3060                   pvr_set_channel_step (mpctx->stream, cmd->args[0].v.i);
3061                   set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3062                                pvr_get_current_channelname (mpctx->stream),
3063                                pvr_get_current_stationname (mpctx->stream));
3064                 }
3065 #endif /* CONFIG_PVR */
3066             }
3067 #ifdef CONFIG_DVBIN
3068             if (IS_STREAMTYPE(DVB)) {
3069                     int dir;
3070                     int v = cmd->args[0].v.i;
3071
3072                     mpctx->last_dvb_step = v;
3073                     if (v > 0)
3074                         dir = DVB_CHANNEL_HIGHER;
3075                     else
3076                         dir = DVB_CHANNEL_LOWER;
3077
3078
3079                     if (dvb_step_channel(mpctx->stream, dir))
3080                         mpctx->eof = mpctx->dvbin_reopen = 1;
3081             }
3082 #endif /* CONFIG_DVBIN */
3083             break;
3084
3085         case MP_CMD_TV_SET_CHANNEL:
3086             if (mpctx->file_format == DEMUXER_TYPE_TV) {
3087                 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3088                                cmd->args[0].v.s);
3089                 if (tv_channel_list) {
3090                     set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3091                                 MSGTR_OSDChannel, tv_channel_current->name);
3092                     //vo_osd_changed(OSDTYPE_SUBTITLE);
3093                 }
3094             }
3095 #ifdef CONFIG_PVR
3096             else if (IS_STREAMTYPE(PVR)) {
3097               pvr_set_channel (mpctx->stream, cmd->args[0].v.s);
3098               set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3099                            pvr_get_current_channelname (mpctx->stream),
3100                            pvr_get_current_stationname (mpctx->stream));
3101             }
3102 #endif /* CONFIG_PVR */
3103             break;
3104
3105 #ifdef CONFIG_DVBIN
3106         case MP_CMD_DVB_SET_CHANNEL:
3107             if (IS_STREAMTYPE(DVB)) {
3108                         mpctx->last_dvb_step = 1;
3109
3110                     if (dvb_set_channel
3111                         (mpctx->stream, cmd->args[1].v.i, cmd->args[0].v.i))
3112                         mpctx->eof = mpctx->dvbin_reopen = 1;
3113             }
3114             break;
3115 #endif /* CONFIG_DVBIN */
3116
3117         case MP_CMD_TV_LAST_CHANNEL:
3118             if (mpctx->file_format == DEMUXER_TYPE_TV) {
3119                 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3120                 if (tv_channel_list) {
3121                     set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3122                                 MSGTR_OSDChannel, tv_channel_current->name);
3123                     //vo_osd_changed(OSDTYPE_SUBTITLE);
3124                 }
3125             }
3126 #ifdef CONFIG_PVR
3127             else if (IS_STREAMTYPE(PVR)) {
3128