vo_gl: add new vo name "gl_sdl" to make SDL+GL mode available
[mplayer.git] / command.c
blob789b60186c254509d754bea77bcd42f2070e7f40
1 /*
2 * This file is part of MPlayer.
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.
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.
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.
19 #include <stdlib.h>
20 #include <inttypes.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <stdbool.h>
25 #include "config.h"
26 #include "talloc.h"
27 #include "command.h"
28 #include "input/input.h"
29 #include "stream/stream.h"
30 #include "libmpdemux/demuxer.h"
31 #include "libmpdemux/stheader.h"
32 #include "codec-cfg.h"
33 #include "mplayer.h"
34 #include "sub/sub.h"
35 #include "sub/dec_sub.h"
36 #include "m_option.h"
37 #include "m_property.h"
38 #include "m_config.h"
39 #include "metadata.h"
40 #include "libmpcodecs/vf.h"
41 #include "libmpcodecs/vd.h"
42 #include "mp_osd.h"
43 #include "libvo/video_out.h"
44 #include "libvo/csputils.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_audio.h"
52 #include "libmpcodecs/dec_teletext.h"
53 #include "osdep/strsep.h"
54 #include "sub/vobsub.h"
55 #include "sub/spudec.h"
56 #include "path.h"
57 #include "sub/ass_mp.h"
58 #include "stream/tv.h"
59 #include "stream/stream_radio.h"
60 #include "stream/pvr.h"
61 #ifdef CONFIG_DVBIN
62 #include "stream/dvbin.h"
63 #endif
64 #ifdef CONFIG_DVDREAD
65 #include "stream/stream_dvd.h"
66 #endif
67 #include "stream/stream_dvdnav.h"
68 #include "m_struct.h"
69 #include "libmenu/menu.h"
71 #include "mp_core.h"
72 #include "mp_fifo.h"
73 #include "libavutil/avstring.h"
75 extern int use_menu;
77 static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
78 double *dx, double *dy)
80 struct MPOpts *opts = &mpctx->opts;
81 struct vo *vo = mpctx->video_out;
82 //remove the borders, if any, and rescale to the range [0,1],[0,1]
83 if (vo_fs) { //we are in full-screen mode
84 if (opts->vo_screenwidth > vo->dwidth)
85 // there are borders along the x axis
86 ix -= (opts->vo_screenwidth - vo->dwidth) / 2;
87 if (opts->vo_screenheight > vo->dheight)
88 // there are borders along the y axis (usual way)
89 iy -= (opts->vo_screenheight - vo->dheight) / 2;
91 if (ix < 0 || ix > vo->dwidth) {
92 *dx = *dy = -1.0;
93 return;
94 } //we are on one of the borders
95 if (iy < 0 || iy > vo->dheight) {
96 *dx = *dy = -1.0;
97 return;
98 } //we are on one of the borders
101 *dx = (double) ix / (double) vo->dwidth;
102 *dy = (double) iy / (double) vo->dheight;
104 mp_msg(MSGT_CPLAYER, MSGL_V,
105 "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
106 *dx, *dy, opts->vo_screenwidth, opts->vo_screenheight, vo->dwidth,
107 vo->dheight, vo_fs);
110 static int sub_pos_by_source(MPContext *mpctx, int src)
112 int i, cnt = 0;
113 if (src >= SUB_SOURCES || mpctx->sub_counts[src] == 0)
114 return -1;
115 for (i = 0; i < src; i++)
116 cnt += mpctx->sub_counts[i];
117 return cnt;
120 static int sub_source_and_index_by_pos(MPContext *mpctx, int *pos)
122 int start = 0;
123 int i;
124 for (i = 0; i < SUB_SOURCES; i++) {
125 int cnt = mpctx->sub_counts[i];
126 if (*pos >= start && *pos < start + cnt) {
127 *pos -= start;
128 return i;
130 start += cnt;
132 *pos = -1;
133 return -1;
136 static int sub_source_by_pos(MPContext *mpctx, int pos)
138 return sub_source_and_index_by_pos(mpctx, &pos);
141 static int sub_source_pos(MPContext *mpctx)
143 int pos = mpctx->global_sub_pos;
144 sub_source_and_index_by_pos(mpctx, &pos);
145 return pos;
148 static int sub_source(MPContext *mpctx)
150 return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
153 static void update_global_sub_size(MPContext *mpctx)
155 struct MPOpts *opts = &mpctx->opts;
156 int i;
157 int cnt = 0;
159 // update number of demuxer sub streams
160 for (i = 0; i < MAX_S_STREAMS; i++)
161 if (mpctx->d_sub->demuxer->s_streams[i])
162 cnt++;
163 if (cnt > mpctx->sub_counts[SUB_SOURCE_DEMUX])
164 mpctx->sub_counts[SUB_SOURCE_DEMUX] = cnt;
166 // update global size
167 mpctx->global_sub_size = 0;
168 for (i = 0; i < SUB_SOURCES; i++)
169 mpctx->global_sub_size += mpctx->sub_counts[i];
171 // update global_sub_pos if we auto-detected a demuxer sub
172 if (mpctx->global_sub_pos == -1) {
173 int sub_id = -1;
174 if (mpctx->demuxer->sub)
175 sub_id = mpctx->demuxer->sub->id;
176 if (sub_id < 0)
177 sub_id = opts->sub_id;
178 if (sub_id >= 0 && sub_id < mpctx->sub_counts[SUB_SOURCE_DEMUX])
179 mpctx->global_sub_pos = sub_pos_by_source(mpctx, SUB_SOURCE_DEMUX) +
180 sub_id;
185 * \brief Log the currently displayed subtitle to a file
187 * Logs the current or last displayed subtitle together with filename
188 * and time information to ~/.mplayer/subtitle_log
190 * Intended purpose is to allow convenient marking of bogus subtitles
191 * which need to be fixed while watching the movie.
194 static void log_sub(struct MPContext *mpctx)
196 char *fname;
197 FILE *f;
198 int i;
199 struct subtitle *vo_sub_last = mpctx->vo_sub_last;
201 if (mpctx->subdata == NULL || vo_sub_last == NULL)
202 return;
203 fname = get_path("subtitle_log");
204 f = fopen(fname, "a");
205 if (!f)
206 return;
207 fprintf(f, "----------------------------------------------------------\n");
208 if (mpctx->subdata->sub_uses_time) {
209 fprintf(f,
210 "N: %s S: %02ld:%02ld:%02ld.%02ld E: %02ld:%02ld:%02ld.%02ld\n",
211 mpctx->filename, vo_sub_last->start / 360000,
212 (vo_sub_last->start / 6000) % 60,
213 (vo_sub_last->start / 100) % 60, vo_sub_last->start % 100,
214 vo_sub_last->end / 360000, (vo_sub_last->end / 6000) % 60,
215 (vo_sub_last->end / 100) % 60, vo_sub_last->end % 100);
216 } else {
217 fprintf(f, "N: %s S: %ld E: %ld\n", mpctx->filename,
218 vo_sub_last->start, vo_sub_last->end);
220 for (i = 0; i < vo_sub_last->lines; i++)
221 fprintf(f, "%s\n", vo_sub_last->text[i]);
222 fclose(f);
226 static int mp_property_generic_option(struct m_option *prop, int action,
227 void *arg, MPContext *mpctx)
229 char *optname = prop->priv;
230 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
231 bstr(optname));
232 void *valptr = m_option_get_ptr(opt, &mpctx->opts);
234 switch (action) {
235 case M_PROPERTY_GET_TYPE:
236 *(const struct m_option **)arg = opt;
237 return M_PROPERTY_OK;
238 case M_PROPERTY_GET:
239 m_option_copy(opt, arg, valptr);
240 return M_PROPERTY_OK;
241 case M_PROPERTY_SET:
242 m_option_copy(opt, valptr, arg);
243 return M_PROPERTY_OK;
244 case M_PROPERTY_STEP_UP:
245 if (opt->type == &m_option_type_choice) {
246 int v = *(int *) valptr;
247 int best = v;
248 struct m_opt_choice_alternatives *alt;
249 for (alt = opt->priv; alt->name; alt++)
250 if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1)
251 best = alt->value;
252 *(int *) valptr = best;
253 return M_PROPERTY_OK;
255 break;
257 return M_PROPERTY_NOT_IMPLEMENTED;
260 /// OSD level (RW)
261 static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
262 MPContext *mpctx)
264 return m_property_choice(prop, action, arg, &mpctx->opts.osd_level);
267 /// Loop (RW)
268 static int mp_property_loop(m_option_t *prop, int action, void *arg,
269 MPContext *mpctx)
271 struct MPOpts *opts = &mpctx->opts;
272 switch (action) {
273 case M_PROPERTY_PRINT:
274 if (!arg)
275 return M_PROPERTY_ERROR;
276 if (opts->loop_times < 0)
277 *(char **)arg = talloc_strdup(NULL, "off");
278 else if (opts->loop_times == 0)
279 *(char **)arg = talloc_strdup(NULL, "inf");
280 else
281 break;
282 return M_PROPERTY_OK;
284 return m_property_int_range(prop, action, arg, &opts->loop_times);
287 /// Playback speed (RW)
288 static int mp_property_playback_speed(m_option_t *prop, int action,
289 void *arg, MPContext *mpctx)
291 struct MPOpts *opts = &mpctx->opts;
292 switch (action) {
293 case M_PROPERTY_SET:
294 if (!arg)
295 return M_PROPERTY_ERROR;
296 M_PROPERTY_CLAMP(prop, *(float *) arg);
297 opts->playback_speed = *(float *) arg;
298 reinit_audio_chain(mpctx);
299 return M_PROPERTY_OK;
300 case M_PROPERTY_STEP_UP:
301 case M_PROPERTY_STEP_DOWN:
302 opts->playback_speed += (arg ? *(float *) arg : 0.1) *
303 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
304 M_PROPERTY_CLAMP(prop, opts->playback_speed);
305 reinit_audio_chain(mpctx);
306 return M_PROPERTY_OK;
308 return m_property_float_range(prop, action, arg, &opts->playback_speed);
311 /// filename with path (RO)
312 static int mp_property_path(m_option_t *prop, int action, void *arg,
313 MPContext *mpctx)
315 return m_property_string_ro(prop, action, arg, mpctx->filename);
318 /// filename without path (RO)
319 static int mp_property_filename(m_option_t *prop, int action, void *arg,
320 MPContext *mpctx)
322 char *f;
323 if (!mpctx->filename)
324 return M_PROPERTY_UNAVAILABLE;
325 f = (char *)mp_basename(mpctx->filename);
326 if (!*f)
327 f = mpctx->filename;
328 return m_property_string_ro(prop, action, arg, f);
331 /// Demuxer name (RO)
332 static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
333 MPContext *mpctx)
335 if (!mpctx->demuxer)
336 return M_PROPERTY_UNAVAILABLE;
337 return m_property_string_ro(prop, action, arg,
338 (char *) mpctx->demuxer->desc->name);
341 /// Position in the stream (RW)
342 static int mp_property_stream_pos(m_option_t *prop, int action, void *arg,
343 MPContext *mpctx)
345 if (!mpctx->demuxer || !mpctx->demuxer->stream)
346 return M_PROPERTY_UNAVAILABLE;
347 if (!arg)
348 return M_PROPERTY_ERROR;
349 switch (action) {
350 case M_PROPERTY_GET:
351 *(off_t *) arg = stream_tell(mpctx->demuxer->stream);
352 return M_PROPERTY_OK;
353 case M_PROPERTY_SET:
354 M_PROPERTY_CLAMP(prop, *(off_t *) arg);
355 stream_seek(mpctx->demuxer->stream, *(off_t *) arg);
356 return M_PROPERTY_OK;
358 return M_PROPERTY_NOT_IMPLEMENTED;
361 /// Stream start offset (RO)
362 static int mp_property_stream_start(m_option_t *prop, int action,
363 void *arg, MPContext *mpctx)
365 if (!mpctx->demuxer || !mpctx->demuxer->stream)
366 return M_PROPERTY_UNAVAILABLE;
367 switch (action) {
368 case M_PROPERTY_GET:
369 *(off_t *) arg = mpctx->demuxer->stream->start_pos;
370 return M_PROPERTY_OK;
372 return M_PROPERTY_NOT_IMPLEMENTED;
375 /// Stream end offset (RO)
376 static int mp_property_stream_end(m_option_t *prop, int action, void *arg,
377 MPContext *mpctx)
379 if (!mpctx->demuxer || !mpctx->demuxer->stream)
380 return M_PROPERTY_UNAVAILABLE;
381 switch (action) {
382 case M_PROPERTY_GET:
383 *(off_t *) arg = mpctx->demuxer->stream->end_pos;
384 return M_PROPERTY_OK;
386 return M_PROPERTY_NOT_IMPLEMENTED;
389 /// Stream length (RO)
390 static int mp_property_stream_length(m_option_t *prop, int action,
391 void *arg, MPContext *mpctx)
393 if (!mpctx->demuxer || !mpctx->demuxer->stream)
394 return M_PROPERTY_UNAVAILABLE;
395 switch (action) {
396 case M_PROPERTY_GET:
397 *(off_t *) arg =
398 mpctx->demuxer->stream->end_pos - mpctx->demuxer->stream->start_pos;
399 return M_PROPERTY_OK;
401 return M_PROPERTY_NOT_IMPLEMENTED;
404 /// Current stream position in seconds (RO)
405 static int mp_property_stream_time_pos(m_option_t *prop, int action,
406 void *arg, MPContext *mpctx)
408 if (!mpctx->demuxer || mpctx->demuxer->stream_pts == MP_NOPTS_VALUE)
409 return M_PROPERTY_UNAVAILABLE;
411 return m_property_time_ro(prop, action, arg, mpctx->demuxer->stream_pts);
415 /// Media length in seconds (RO)
416 static int mp_property_length(m_option_t *prop, int action, void *arg,
417 MPContext *mpctx)
419 double len;
421 if (!mpctx->demuxer ||
422 !(int) (len = get_time_length(mpctx)))
423 return M_PROPERTY_UNAVAILABLE;
425 return m_property_time_ro(prop, action, arg, len);
428 /// Current position in percent (RW)
429 static int mp_property_percent_pos(m_option_t *prop, int action,
430 void *arg, MPContext *mpctx)
432 int pos;
434 if (!mpctx->demuxer)
435 return M_PROPERTY_UNAVAILABLE;
437 switch (action) {
438 case M_PROPERTY_SET:
439 if (!arg)
440 return M_PROPERTY_ERROR;
441 M_PROPERTY_CLAMP(prop, *(int *)arg);
442 pos = *(int *)arg;
443 break;
444 case M_PROPERTY_STEP_UP:
445 case M_PROPERTY_STEP_DOWN:
446 pos = get_percent_pos(mpctx);
447 pos += (arg ? *(int *)arg : 10) *
448 (action == M_PROPERTY_STEP_UP ? 1 : -1);
449 M_PROPERTY_CLAMP(prop, pos);
450 break;
451 default:
452 return m_property_int_ro(prop, action, arg, get_percent_pos(mpctx));
455 queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0);
456 return M_PROPERTY_OK;
459 /// Current position in seconds (RW)
460 static int mp_property_time_pos(m_option_t *prop, int action,
461 void *arg, MPContext *mpctx)
463 if (!(mpctx->sh_video || mpctx->sh_audio))
464 return M_PROPERTY_UNAVAILABLE;
466 switch (action) {
467 case M_PROPERTY_SET:
468 if (!arg)
469 return M_PROPERTY_ERROR;
470 M_PROPERTY_CLAMP(prop, *(double *)arg);
471 queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, 0);
472 return M_PROPERTY_OK;
473 case M_PROPERTY_STEP_UP:
474 case M_PROPERTY_STEP_DOWN:
475 queue_seek(mpctx, MPSEEK_RELATIVE, (arg ? *(double *)arg : 10.0) *
476 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0), 0);
477 return M_PROPERTY_OK;
479 return m_property_time_ro(prop, action, arg, get_current_time(mpctx));
482 /// Current chapter (RW)
483 static int mp_property_chapter(m_option_t *prop, int action, void *arg,
484 MPContext *mpctx)
486 struct MPOpts *opts = &mpctx->opts;
487 int chapter = -1;
488 int step_all;
489 char *chapter_name = NULL;
491 if (mpctx->demuxer)
492 chapter = get_current_chapter(mpctx);
493 if (chapter < -1)
494 return M_PROPERTY_UNAVAILABLE;
496 switch (action) {
497 case M_PROPERTY_GET:
498 if (!arg)
499 return M_PROPERTY_ERROR;
500 *(int *) arg = chapter;
501 return M_PROPERTY_OK;
502 case M_PROPERTY_PRINT: {
503 if (!arg)
504 return M_PROPERTY_ERROR;
505 chapter_name = chapter_display_name(mpctx, chapter);
506 if (!chapter_name)
507 return M_PROPERTY_UNAVAILABLE;
508 *(char **) arg = chapter_name;
509 return M_PROPERTY_OK;
511 case M_PROPERTY_SET:
512 if (!arg)
513 return M_PROPERTY_ERROR;
514 M_PROPERTY_CLAMP(prop, *(int *)arg);
515 step_all = *(int *)arg - chapter;
516 chapter += step_all;
517 break;
518 case M_PROPERTY_STEP_UP:
519 case M_PROPERTY_STEP_DOWN: {
520 step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
521 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
522 chapter += step_all;
523 if (chapter < 0)
524 chapter = 0;
525 break;
527 default:
528 return M_PROPERTY_NOT_IMPLEMENTED;
531 double next_pts = 0;
532 queue_seek(mpctx, MPSEEK_NONE, 0, 0);
533 chapter = seek_chapter(mpctx, chapter, &next_pts, &chapter_name);
534 if (chapter >= 0) {
535 if (next_pts > -1.0)
536 queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
537 if (chapter_name)
538 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
539 "Chapter: (%d) %s", chapter + 1, chapter_name);
540 } else if (step_all > 0)
541 queue_seek(mpctx, MPSEEK_RELATIVE, 1000000000, 0);
542 else
543 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
544 "Chapter: (%d) %s", 0, mp_gtext("unknown"));
545 talloc_free(chapter_name);
546 return M_PROPERTY_OK;
549 /// Number of chapters in file
550 static int mp_property_chapters(m_option_t *prop, int action, void *arg,
551 MPContext *mpctx)
553 if (!mpctx->demuxer)
554 return M_PROPERTY_UNAVAILABLE;
555 if (mpctx->demuxer->num_chapters == 0)
556 stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS,
557 &mpctx->demuxer->num_chapters);
558 return m_property_int_ro(prop, action, arg, mpctx->demuxer->num_chapters);
561 /// Current dvd angle (RW)
562 static int mp_property_angle(m_option_t *prop, int action, void *arg,
563 MPContext *mpctx)
565 struct MPOpts *opts = &mpctx->opts;
566 int angle = -1;
567 int angles;
569 if (mpctx->demuxer)
570 angle = demuxer_get_current_angle(mpctx->demuxer);
571 if (angle < 0)
572 return M_PROPERTY_UNAVAILABLE;
573 angles = demuxer_angles_count(mpctx->demuxer);
574 if (angles <= 1)
575 return M_PROPERTY_UNAVAILABLE;
577 switch (action) {
578 case M_PROPERTY_GET:
579 if (!arg)
580 return M_PROPERTY_ERROR;
581 *(int *) arg = angle;
582 return M_PROPERTY_OK;
583 case M_PROPERTY_PRINT: {
584 if (!arg)
585 return M_PROPERTY_ERROR;
586 *(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
587 return M_PROPERTY_OK;
589 case M_PROPERTY_SET:
590 if (!arg)
591 return M_PROPERTY_ERROR;
592 angle = *(int *)arg;
593 M_PROPERTY_CLAMP(prop, angle);
594 break;
595 case M_PROPERTY_STEP_UP:
596 case M_PROPERTY_STEP_DOWN: {
597 int step = 0;
598 if (arg)
599 step = *(int *)arg;
600 if (!step)
601 step = 1;
602 step *= (action == M_PROPERTY_STEP_UP ? 1 : -1);
603 angle += step;
604 if (angle < 1) //cycle
605 angle = angles;
606 else if (angle > angles)
607 angle = 1;
608 break;
610 default:
611 return M_PROPERTY_NOT_IMPLEMENTED;
613 angle = demuxer_set_angle(mpctx->demuxer, angle);
614 if (angle >= 0) {
615 struct sh_video *sh_video = mpctx->demuxer->video->sh;
616 if (sh_video)
617 resync_video_stream(sh_video);
619 struct sh_audio *sh_audio = mpctx->demuxer->audio->sh;
620 if (sh_audio)
621 resync_audio_stream(sh_audio);
624 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
625 "Angle: %d/%d", angle, angles);
626 return M_PROPERTY_OK;
629 /// Demuxer meta data
630 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
631 MPContext *mpctx)
633 m_property_action_t *ka;
634 char *meta;
635 static const m_option_t key_type =
637 "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL
639 if (!mpctx->demuxer)
640 return M_PROPERTY_UNAVAILABLE;
642 switch (action) {
643 case M_PROPERTY_GET:
644 if (!arg)
645 return M_PROPERTY_ERROR;
646 *(char ***)arg = mpctx->demuxer->info;
647 return M_PROPERTY_OK;
648 case M_PROPERTY_KEY_ACTION:
649 if (!arg)
650 return M_PROPERTY_ERROR;
651 ka = arg;
652 if (!(meta = demux_info_get(mpctx->demuxer, ka->key)))
653 return M_PROPERTY_UNKNOWN;
654 switch (ka->action) {
655 case M_PROPERTY_GET:
656 if (!ka->arg)
657 return M_PROPERTY_ERROR;
658 *(char **)ka->arg = meta;
659 return M_PROPERTY_OK;
660 case M_PROPERTY_GET_TYPE:
661 if (!ka->arg)
662 return M_PROPERTY_ERROR;
663 *(const m_option_t **)ka->arg = &key_type;
664 return M_PROPERTY_OK;
667 return M_PROPERTY_NOT_IMPLEMENTED;
670 static int mp_property_pause(m_option_t *prop, int action, void *arg,
671 void *ctx)
673 MPContext *mpctx = ctx;
675 switch (action) {
676 case M_PROPERTY_SET:
677 if (!arg)
678 return M_PROPERTY_ERROR;
679 if (mpctx->paused == (bool) * (int *)arg)
680 return M_PROPERTY_OK;
681 case M_PROPERTY_STEP_UP:
682 case M_PROPERTY_STEP_DOWN:
683 if (mpctx->paused) {
684 unpause_player(mpctx);
685 mpctx->osd_function = OSD_PLAY;
686 } else {
687 pause_player(mpctx);
688 mpctx->osd_function = OSD_PAUSE;
690 return M_PROPERTY_OK;
691 default:
692 return m_property_flag(prop, action, arg, &mpctx->paused);
697 /// Volume (RW)
698 static int mp_property_volume(m_option_t *prop, int action, void *arg,
699 MPContext *mpctx)
702 if (!mpctx->sh_audio)
703 return M_PROPERTY_UNAVAILABLE;
705 switch (action) {
706 case M_PROPERTY_GET:
707 if (!arg)
708 return M_PROPERTY_ERROR;
709 mixer_getbothvolume(&mpctx->mixer, arg);
710 return M_PROPERTY_OK;
711 case M_PROPERTY_PRINT: {
712 float vol;
713 if (!arg)
714 return M_PROPERTY_ERROR;
715 mixer_getbothvolume(&mpctx->mixer, &vol);
716 return m_property_float_range(prop, action, arg, &vol);
718 case M_PROPERTY_STEP_UP:
719 case M_PROPERTY_STEP_DOWN:
720 case M_PROPERTY_SET:
721 break;
722 default:
723 return M_PROPERTY_NOT_IMPLEMENTED;
726 if (mpctx->edl_muted)
727 return M_PROPERTY_DISABLED;
728 mpctx->user_muted = 0;
730 switch (action) {
731 case M_PROPERTY_SET:
732 if (!arg)
733 return M_PROPERTY_ERROR;
734 M_PROPERTY_CLAMP(prop, *(float *) arg);
735 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
736 return M_PROPERTY_OK;
737 case M_PROPERTY_STEP_UP:
738 if (arg && *(float *) arg <= 0)
739 mixer_decvolume(&mpctx->mixer);
740 else
741 mixer_incvolume(&mpctx->mixer);
742 return M_PROPERTY_OK;
743 case M_PROPERTY_STEP_DOWN:
744 if (arg && *(float *) arg <= 0)
745 mixer_incvolume(&mpctx->mixer);
746 else
747 mixer_decvolume(&mpctx->mixer);
748 return M_PROPERTY_OK;
750 return M_PROPERTY_NOT_IMPLEMENTED;
753 /// Mute (RW)
754 static int mp_property_mute(m_option_t *prop, int action, void *arg,
755 MPContext *mpctx)
758 if (!mpctx->sh_audio)
759 return M_PROPERTY_UNAVAILABLE;
761 switch (action) {
762 case M_PROPERTY_SET:
763 if (mpctx->edl_muted)
764 return M_PROPERTY_DISABLED;
765 if (!arg)
766 return M_PROPERTY_ERROR;
767 if ((!!*(int *) arg) != mpctx->mixer.muted)
768 mixer_mute(&mpctx->mixer);
769 mpctx->user_muted = mpctx->mixer.muted;
770 return M_PROPERTY_OK;
771 case M_PROPERTY_STEP_UP:
772 case M_PROPERTY_STEP_DOWN:
773 if (mpctx->edl_muted)
774 return M_PROPERTY_DISABLED;
775 mixer_mute(&mpctx->mixer);
776 mpctx->user_muted = mpctx->mixer.muted;
777 return M_PROPERTY_OK;
778 case M_PROPERTY_PRINT:
779 if (!arg)
780 return M_PROPERTY_ERROR;
781 if (mpctx->edl_muted) {
782 *(char **) arg = talloc_strdup(NULL, mp_gtext("enabled (EDL)"));
783 return M_PROPERTY_OK;
785 default:
786 return m_property_flag(prop, action, arg, &mpctx->mixer.muted);
791 /// Audio delay (RW)
792 static int mp_property_audio_delay(m_option_t *prop, int action,
793 void *arg, MPContext *mpctx)
795 if (!(mpctx->sh_audio && mpctx->sh_video))
796 return M_PROPERTY_UNAVAILABLE;
797 switch (action) {
798 case M_PROPERTY_SET:
799 case M_PROPERTY_STEP_UP:
800 case M_PROPERTY_STEP_DOWN: {
801 int ret;
802 float delay = audio_delay;
803 ret = m_property_delay(prop, action, arg, &audio_delay);
804 if (ret != M_PROPERTY_OK)
805 return ret;
806 if (mpctx->sh_audio)
807 mpctx->delay -= audio_delay - delay;
809 return M_PROPERTY_OK;
810 default:
811 return m_property_delay(prop, action, arg, &audio_delay);
815 /// Audio codec tag (RO)
816 static int mp_property_audio_format(m_option_t *prop, int action,
817 void *arg, MPContext *mpctx)
819 if (!mpctx->sh_audio)
820 return M_PROPERTY_UNAVAILABLE;
821 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
824 /// Audio codec name (RO)
825 static int mp_property_audio_codec(m_option_t *prop, int action,
826 void *arg, MPContext *mpctx)
828 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
829 return M_PROPERTY_UNAVAILABLE;
830 return m_property_string_ro(prop, action, arg,
831 mpctx->sh_audio->codec->name);
834 /// Audio bitrate (RO)
835 static int mp_property_audio_bitrate(m_option_t *prop, int action,
836 void *arg, MPContext *mpctx)
838 if (!mpctx->sh_audio)
839 return M_PROPERTY_UNAVAILABLE;
840 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
843 /// Samplerate (RO)
844 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
845 MPContext *mpctx)
847 if (!mpctx->sh_audio)
848 return M_PROPERTY_UNAVAILABLE;
849 switch (action) {
850 case M_PROPERTY_PRINT:
851 if (!arg)
852 return M_PROPERTY_ERROR;
853 *(char **)arg = talloc_asprintf(NULL, "%d kHz",
854 mpctx->sh_audio->samplerate / 1000);
855 return M_PROPERTY_OK;
857 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
860 /// Number of channels (RO)
861 static int mp_property_channels(m_option_t *prop, int action, void *arg,
862 MPContext *mpctx)
864 if (!mpctx->sh_audio)
865 return M_PROPERTY_UNAVAILABLE;
866 switch (action) {
867 case M_PROPERTY_PRINT:
868 if (!arg)
869 return M_PROPERTY_ERROR;
870 switch (mpctx->sh_audio->channels) {
871 case 1:
872 *(char **) arg = talloc_strdup(NULL, "mono");
873 break;
874 case 2:
875 *(char **) arg = talloc_strdup(NULL, "stereo");
876 break;
877 default:
878 *(char **) arg = talloc_asprintf(NULL, "%d channels",
879 mpctx->sh_audio->channels);
881 return M_PROPERTY_OK;
883 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
886 /// Balance (RW)
887 static int mp_property_balance(m_option_t *prop, int action, void *arg,
888 MPContext *mpctx)
890 float bal;
892 if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2)
893 return M_PROPERTY_UNAVAILABLE;
895 switch (action) {
896 case M_PROPERTY_GET:
897 if (!arg)
898 return M_PROPERTY_ERROR;
899 mixer_getbalance(&mpctx->mixer, arg);
900 return M_PROPERTY_OK;
901 case M_PROPERTY_PRINT: {
902 char **str = arg;
903 if (!arg)
904 return M_PROPERTY_ERROR;
905 mixer_getbalance(&mpctx->mixer, &bal);
906 if (bal == 0.f)
907 *str = talloc_strdup(NULL, "center");
908 else if (bal == -1.f)
909 *str = talloc_strdup(NULL, "left only");
910 else if (bal == 1.f)
911 *str = talloc_strdup(NULL, "right only");
912 else {
913 unsigned right = (bal + 1.f) / 2.f * 100.f;
914 *str = talloc_asprintf(NULL, "left %d%%, right %d%%",
915 100 - right, right);
917 return M_PROPERTY_OK;
919 case M_PROPERTY_STEP_UP:
920 case M_PROPERTY_STEP_DOWN:
921 mixer_getbalance(&mpctx->mixer, &bal);
922 bal += (arg ? *(float *)arg : .1f) *
923 (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
924 M_PROPERTY_CLAMP(prop, bal);
925 mixer_setbalance(&mpctx->mixer, bal);
926 return M_PROPERTY_OK;
927 case M_PROPERTY_SET:
928 if (!arg)
929 return M_PROPERTY_ERROR;
930 M_PROPERTY_CLAMP(prop, *(float *)arg);
931 mixer_setbalance(&mpctx->mixer, *(float *)arg);
932 return M_PROPERTY_OK;
934 return M_PROPERTY_NOT_IMPLEMENTED;
937 /// Selected audio id (RW)
938 static int mp_property_audio(m_option_t *prop, int action, void *arg,
939 MPContext *mpctx)
941 int current_id, tmp;
942 if (!mpctx->demuxer || !mpctx->d_audio)
943 return M_PROPERTY_UNAVAILABLE;
944 struct sh_audio *sh = mpctx->sh_audio;
945 current_id = sh ? sh->aid : -2;
947 switch (action) {
948 case M_PROPERTY_GET:
949 if (!arg)
950 return M_PROPERTY_ERROR;
951 *(int *) arg = current_id;
952 return M_PROPERTY_OK;
953 case M_PROPERTY_PRINT:
954 if (!arg)
955 return M_PROPERTY_ERROR;
957 if (current_id < 0)
958 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
959 else if (sh && (sh->lang || sh->title)) {
960 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
961 if (sh->title)
962 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
963 current_id, lang, sh->title);
964 else
965 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
966 lang);
967 } else {
968 char lang[40];
969 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
970 if (0) ;
971 #ifdef CONFIG_DVDREAD
972 else if (mpctx->stream->type == STREAMTYPE_DVD) {
973 int code = dvd_lang_from_aid(mpctx->stream, current_id);
974 if (code) {
975 lang[0] = code >> 8;
976 lang[1] = code;
977 lang[2] = 0;
980 #endif
982 #ifdef CONFIG_DVDNAV
983 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
984 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
985 #endif
986 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, lang);
988 return M_PROPERTY_OK;
990 case M_PROPERTY_STEP_UP:
991 case M_PROPERTY_SET:
992 if (action == M_PROPERTY_SET && arg)
993 tmp = *((int *) arg);
994 else
995 tmp = -1;
996 int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
997 if (new_id != current_id)
998 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
999 if (new_id != current_id && new_id >= 0) {
1000 sh_audio_t *sh2;
1001 sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
1002 sh2->ds = mpctx->d_audio;
1003 mpctx->sh_audio = sh2;
1004 reinit_audio_chain(mpctx);
1006 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
1007 return M_PROPERTY_OK;
1008 default:
1009 return M_PROPERTY_NOT_IMPLEMENTED;
1014 /// Selected video id (RW)
1015 static int mp_property_video(m_option_t *prop, int action, void *arg,
1016 MPContext *mpctx)
1018 struct MPOpts *opts = &mpctx->opts;
1019 int current_id, tmp;
1020 if (!mpctx->demuxer || !mpctx->d_video)
1021 return M_PROPERTY_UNAVAILABLE;
1022 current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
1024 switch (action) {
1025 case M_PROPERTY_GET:
1026 if (!arg)
1027 return M_PROPERTY_ERROR;
1028 *(int *) arg = current_id;
1029 return M_PROPERTY_OK;
1030 case M_PROPERTY_PRINT:
1031 if (!arg)
1032 return M_PROPERTY_ERROR;
1034 if (current_id < 0)
1035 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1036 else {
1037 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
1038 mp_gtext("unknown"));
1040 return M_PROPERTY_OK;
1042 case M_PROPERTY_STEP_UP:
1043 case M_PROPERTY_SET:
1044 if (action == M_PROPERTY_SET && arg)
1045 tmp = *((int *) arg);
1046 else
1047 tmp = -1;
1048 int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
1049 if (new_id != current_id)
1050 uninit_player(mpctx, INITIALIZED_VCODEC |
1051 (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
1052 if (new_id != current_id && new_id >= 0) {
1053 sh_video_t *sh2;
1054 sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
1055 sh2->ds = mpctx->d_video;
1056 mpctx->sh_video = sh2;
1057 reinit_video_chain(mpctx);
1059 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
1060 return M_PROPERTY_OK;
1062 default:
1063 return M_PROPERTY_NOT_IMPLEMENTED;
1067 static int mp_property_program(m_option_t *prop, int action, void *arg,
1068 MPContext *mpctx)
1070 demux_program_t prog;
1072 switch (action) {
1073 case M_PROPERTY_STEP_UP:
1074 case M_PROPERTY_SET:
1075 if (action == M_PROPERTY_SET && arg)
1076 prog.progid = *((int *) arg);
1077 else
1078 prog.progid = -1;
1079 if (demux_control(mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1080 &prog) == DEMUXER_CTRL_NOTIMPL)
1081 return M_PROPERTY_ERROR;
1083 if (prog.aid < 0 && prog.vid < 0) {
1084 mp_msg(MSGT_CPLAYER, MSGL_ERR,
1085 "Selected program contains no audio or video streams!\n");
1086 return M_PROPERTY_ERROR;
1088 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1089 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1090 return M_PROPERTY_OK;
1092 default:
1093 return M_PROPERTY_NOT_IMPLEMENTED;
1098 /// Fullscreen state (RW)
1099 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1100 MPContext *mpctx)
1103 if (!mpctx->video_out)
1104 return M_PROPERTY_UNAVAILABLE;
1106 switch (action) {
1107 case M_PROPERTY_SET:
1108 if (!arg)
1109 return M_PROPERTY_ERROR;
1110 M_PROPERTY_CLAMP(prop, *(int *) arg);
1111 if (vo_fs == !!*(int *) arg)
1112 return M_PROPERTY_OK;
1113 case M_PROPERTY_STEP_UP:
1114 case M_PROPERTY_STEP_DOWN:
1115 if (mpctx->video_out->config_ok)
1116 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1117 mpctx->opts.fullscreen = vo_fs;
1118 return M_PROPERTY_OK;
1119 default:
1120 return m_property_flag(prop, action, arg, &vo_fs);
1124 static int mp_property_deinterlace(m_option_t *prop, int action,
1125 void *arg, MPContext *mpctx)
1127 int deinterlace;
1128 vf_instance_t *vf;
1129 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1130 return M_PROPERTY_UNAVAILABLE;
1131 vf = mpctx->sh_video->vfilter;
1132 switch (action) {
1133 case M_PROPERTY_GET:
1134 if (!arg)
1135 return M_PROPERTY_ERROR;
1136 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1137 return M_PROPERTY_OK;
1138 case M_PROPERTY_SET:
1139 if (!arg)
1140 return M_PROPERTY_ERROR;
1141 M_PROPERTY_CLAMP(prop, *(int *) arg);
1142 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1143 return M_PROPERTY_OK;
1144 case M_PROPERTY_STEP_UP:
1145 case M_PROPERTY_STEP_DOWN:
1146 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1147 deinterlace = !deinterlace;
1148 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1149 return M_PROPERTY_OK;
1151 int value = 0;
1152 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1153 return m_property_flag_ro(prop, action, arg, value);
1156 static int colormatrix_property_helper(m_option_t *prop, int action,
1157 void *arg, MPContext *mpctx)
1159 int r = mp_property_generic_option(prop, action, arg, mpctx);
1160 // testing for an actual change is too much effort
1161 switch (action) {
1162 case M_PROPERTY_SET:
1163 case M_PROPERTY_STEP_UP:
1164 case M_PROPERTY_STEP_DOWN:
1165 if (mpctx->sh_video)
1166 set_video_colorspace(mpctx->sh_video);
1167 break;
1169 return r;
1172 static int mp_property_colormatrix(m_option_t *prop, int action, void *arg,
1173 MPContext *mpctx)
1175 struct MPOpts *opts = &mpctx->opts;
1176 switch (action) {
1177 case M_PROPERTY_PRINT:
1178 if (!arg)
1179 return M_PROPERTY_ERROR;
1180 struct mp_csp_details actual = { .format = -1 };
1181 char *req_csp = mp_csp_names[opts->requested_colorspace];
1182 char *real_csp = NULL;
1183 if (mpctx->sh_video) {
1184 struct vf_instance *vf = mpctx->sh_video->vfilter;
1185 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1186 real_csp = mp_csp_names[actual.format];
1187 } else {
1188 real_csp = "Unknown";
1191 char *res;
1192 if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) {
1193 // Caveat: doesn't handle the case when the autodetected colorspace
1194 // is different from the actual colorspace as used by the
1195 // VO - the OSD will display the VO colorspace without
1196 // indication that it doesn't match the requested colorspace.
1197 res = talloc_asprintf(NULL, "Auto (%s)", real_csp);
1198 } else if (opts->requested_colorspace == actual.format || !real_csp) {
1199 res = talloc_strdup(NULL, req_csp);
1200 } else
1201 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1202 req_csp, real_csp);
1203 *(char **)arg = res;
1204 return M_PROPERTY_OK;
1205 default:;
1206 return colormatrix_property_helper(prop, action, arg, mpctx);
1210 static int levels_property_helper(int offset, m_option_t *prop, int action,
1211 void *arg, MPContext *mpctx)
1213 char *optname = prop->priv;
1214 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
1215 bstr(optname));
1216 int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts);
1218 switch (action) {
1219 case M_PROPERTY_PRINT:
1220 if (!arg)
1221 return M_PROPERTY_ERROR;
1222 struct mp_csp_details actual = {0};
1223 int actual_level = -1;
1224 char *req_level = m_option_print(opt, valptr);
1225 char *real_level = NULL;
1226 if (mpctx->sh_video) {
1227 struct vf_instance *vf = mpctx->sh_video->vfilter;
1228 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1229 actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset);
1230 real_level = m_option_print(opt, &actual_level);
1231 } else {
1232 real_level = talloc_strdup(NULL, "Unknown");
1235 char *res;
1236 if (*valptr == MP_CSP_LEVELS_AUTO && real_level) {
1237 res = talloc_asprintf(NULL, "Auto (%s)", real_level);
1238 } else if (*valptr == actual_level || !real_level) {
1239 res = talloc_strdup(NULL, real_level);
1240 } else
1241 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1242 req_level, real_level);
1243 talloc_free(req_level);
1244 talloc_free(real_level);
1245 *(char **)arg = res;
1246 return M_PROPERTY_OK;
1247 default:;
1248 return colormatrix_property_helper(prop, action, arg, mpctx);
1252 static int mp_property_colormatrix_input_range(m_option_t *prop, int action,
1253 void *arg, MPContext *mpctx)
1255 return levels_property_helper(offsetof(struct mp_csp_details, levels_in),
1256 prop, action, arg, mpctx);
1259 static int mp_property_colormatrix_output_range(m_option_t *prop, int action,
1260 void *arg, MPContext *mpctx)
1262 return levels_property_helper(offsetof(struct mp_csp_details, levels_out),
1263 prop, action, arg, mpctx);
1266 static int mp_property_capture(m_option_t *prop, int action,
1267 void *arg, MPContext *mpctx)
1269 struct MPOpts *opts = &mpctx->opts;
1271 if (!mpctx->stream)
1272 return M_PROPERTY_UNAVAILABLE;
1274 if (!opts->capture_dump) {
1275 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1276 "Capturing not enabled (forgot -capture parameter?)\n");
1277 return M_PROPERTY_ERROR;
1280 int capturing = !!mpctx->stream->capture_file;
1282 int ret = m_property_flag(prop, action, arg, &capturing);
1283 if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1284 if (capturing) {
1285 mpctx->stream->capture_file = fopen(opts->stream_dump_name, "wb");
1286 if (!mpctx->stream->capture_file) {
1287 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1288 "Error opening capture file: %s\n", strerror(errno));
1289 ret = M_PROPERTY_ERROR;
1291 } else {
1292 fclose(mpctx->stream->capture_file);
1293 mpctx->stream->capture_file = NULL;
1297 return ret;
1300 /// Panscan (RW)
1301 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1302 MPContext *mpctx)
1305 if (!mpctx->video_out
1306 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1307 return M_PROPERTY_UNAVAILABLE;
1309 switch (action) {
1310 case M_PROPERTY_SET:
1311 if (!arg)
1312 return M_PROPERTY_ERROR;
1313 M_PROPERTY_CLAMP(prop, *(float *) arg);
1314 vo_panscan = *(float *) arg;
1315 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1316 return M_PROPERTY_OK;
1317 case M_PROPERTY_STEP_UP:
1318 case M_PROPERTY_STEP_DOWN:
1319 vo_panscan += (arg ? *(float *) arg : 0.1) *
1320 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1321 if (vo_panscan > 1)
1322 vo_panscan = 1;
1323 else if (vo_panscan < 0)
1324 vo_panscan = 0;
1325 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1326 return M_PROPERTY_OK;
1327 default:
1328 return m_property_float_range(prop, action, arg, &vo_panscan);
1332 /// Helper to set vo flags.
1333 /** \ingroup PropertyImplHelper
1335 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1336 int vo_ctrl, int *vo_var, MPContext *mpctx)
1339 if (!mpctx->video_out)
1340 return M_PROPERTY_UNAVAILABLE;
1342 switch (action) {
1343 case M_PROPERTY_SET:
1344 if (!arg)
1345 return M_PROPERTY_ERROR;
1346 M_PROPERTY_CLAMP(prop, *(int *) arg);
1347 if (*vo_var == !!*(int *) arg)
1348 return M_PROPERTY_OK;
1349 case M_PROPERTY_STEP_UP:
1350 case M_PROPERTY_STEP_DOWN:
1351 if (mpctx->video_out->config_ok)
1352 vo_control(mpctx->video_out, vo_ctrl, 0);
1353 return M_PROPERTY_OK;
1354 default:
1355 return m_property_flag(prop, action, arg, vo_var);
1359 /// Window always on top (RW)
1360 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1361 MPContext *mpctx)
1363 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1364 &mpctx->opts.vo_ontop, mpctx);
1367 /// Display in the root window (RW)
1368 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1369 MPContext *mpctx)
1371 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1372 &vo_rootwin, mpctx);
1375 /// Show window borders (RW)
1376 static int mp_property_border(m_option_t *prop, int action, void *arg,
1377 MPContext *mpctx)
1379 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1380 &vo_border, mpctx);
1383 /// Framedropping state (RW)
1384 static int mp_property_framedropping(m_option_t *prop, int action,
1385 void *arg, MPContext *mpctx)
1388 if (!mpctx->sh_video)
1389 return M_PROPERTY_UNAVAILABLE;
1391 switch (action) {
1392 case M_PROPERTY_PRINT:
1393 if (!arg)
1394 return M_PROPERTY_ERROR;
1395 *(char **) arg = talloc_strdup(NULL, frame_dropping == 1 ?
1396 mp_gtext("enabled") :
1397 (frame_dropping == 2 ? mp_gtext("hard") :
1398 mp_gtext("disabled")));
1399 return M_PROPERTY_OK;
1400 default:
1401 return m_property_choice(prop, action, arg, &frame_dropping);
1405 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1406 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1407 MPContext *mpctx)
1409 int *gamma = (int *)((char *)&mpctx->opts + prop->offset);
1410 int r, val;
1412 if (!mpctx->sh_video)
1413 return M_PROPERTY_UNAVAILABLE;
1415 if (gamma[0] == 1000) {
1416 gamma[0] = 0;
1417 get_video_colors(mpctx->sh_video, prop->name, gamma);
1420 switch (action) {
1421 case M_PROPERTY_SET:
1422 if (!arg)
1423 return M_PROPERTY_ERROR;
1424 M_PROPERTY_CLAMP(prop, *(int *) arg);
1425 *gamma = *(int *) arg;
1426 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1427 if (r <= 0)
1428 break;
1429 return r;
1430 case M_PROPERTY_GET:
1431 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1432 if (!arg)
1433 return M_PROPERTY_ERROR;
1434 *(int *)arg = val;
1435 return M_PROPERTY_OK;
1437 break;
1438 case M_PROPERTY_STEP_UP:
1439 case M_PROPERTY_STEP_DOWN:
1440 *gamma += (arg ? *(int *) arg : 1) *
1441 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1442 M_PROPERTY_CLAMP(prop, *gamma);
1443 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1444 if (r <= 0)
1445 break;
1446 return r;
1447 default:
1448 return M_PROPERTY_NOT_IMPLEMENTED;
1451 #ifdef CONFIG_TV
1452 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1453 int l = strlen(prop->name);
1454 char tv_prop[3 + l + 1];
1455 sprintf(tv_prop, "tv_%s", prop->name);
1456 return mp_property_do(tv_prop, action, arg, mpctx);
1458 #endif
1460 return M_PROPERTY_UNAVAILABLE;
1463 /// VSync (RW)
1464 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1465 MPContext *mpctx)
1467 return m_property_flag(prop, action, arg, &vo_vsync);
1470 /// Video codec tag (RO)
1471 static int mp_property_video_format(m_option_t *prop, int action,
1472 void *arg, MPContext *mpctx)
1474 char *meta;
1475 if (!mpctx->sh_video)
1476 return M_PROPERTY_UNAVAILABLE;
1477 switch (action) {
1478 case M_PROPERTY_PRINT:
1479 if (!arg)
1480 return M_PROPERTY_ERROR;
1481 switch (mpctx->sh_video->format) {
1482 case 0x10000001:
1483 meta = talloc_strdup(NULL, "mpeg1");
1484 break;
1485 case 0x10000002:
1486 meta = talloc_strdup(NULL, "mpeg2");
1487 break;
1488 case 0x10000004:
1489 meta = talloc_strdup(NULL, "mpeg4");
1490 break;
1491 case 0x10000005:
1492 meta = talloc_strdup(NULL, "h264");
1493 break;
1494 default:
1495 if (mpctx->sh_video->format >= 0x20202020) {
1496 meta = talloc_asprintf(NULL, "%.4s",
1497 (char *) &mpctx->sh_video->format);
1498 } else
1499 meta = talloc_asprintf(NULL, "0x%08X", mpctx->sh_video->format);
1501 *(char **)arg = meta;
1502 return M_PROPERTY_OK;
1504 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1507 /// Video codec name (RO)
1508 static int mp_property_video_codec(m_option_t *prop, int action,
1509 void *arg, MPContext *mpctx)
1511 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1512 return M_PROPERTY_UNAVAILABLE;
1513 return m_property_string_ro(prop, action, arg,
1514 mpctx->sh_video->codec->name);
1518 /// Video bitrate (RO)
1519 static int mp_property_video_bitrate(m_option_t *prop, int action,
1520 void *arg, MPContext *mpctx)
1522 if (!mpctx->sh_video)
1523 return M_PROPERTY_UNAVAILABLE;
1524 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1527 /// Video display width (RO)
1528 static int mp_property_width(m_option_t *prop, int action, void *arg,
1529 MPContext *mpctx)
1531 if (!mpctx->sh_video)
1532 return M_PROPERTY_UNAVAILABLE;
1533 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1536 /// Video display height (RO)
1537 static int mp_property_height(m_option_t *prop, int action, void *arg,
1538 MPContext *mpctx)
1540 if (!mpctx->sh_video)
1541 return M_PROPERTY_UNAVAILABLE;
1542 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1545 /// Video fps (RO)
1546 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1547 MPContext *mpctx)
1549 if (!mpctx->sh_video)
1550 return M_PROPERTY_UNAVAILABLE;
1551 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1554 /// Video aspect (RO)
1555 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1556 MPContext *mpctx)
1558 if (!mpctx->sh_video)
1559 return M_PROPERTY_UNAVAILABLE;
1560 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1564 /// Text subtitle position (RW)
1565 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1566 MPContext *mpctx)
1568 switch (action) {
1569 case M_PROPERTY_SET:
1570 if (!arg)
1571 return M_PROPERTY_ERROR;
1572 case M_PROPERTY_STEP_UP:
1573 case M_PROPERTY_STEP_DOWN:
1574 vo_osd_changed(OSDTYPE_SUBTITLE);
1575 default:
1576 return m_property_int_range(prop, action, arg, &sub_pos);
1580 /// Selected subtitles (RW)
1581 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1582 MPContext *mpctx)
1584 struct MPOpts *opts = &mpctx->opts;
1585 demux_stream_t *const d_sub = mpctx->d_sub;
1586 int source = -1, reset_spu = 0;
1587 int source_pos = -1;
1589 update_global_sub_size(mpctx);
1590 const int global_sub_size = mpctx->global_sub_size;
1592 if (global_sub_size <= 0)
1593 return M_PROPERTY_UNAVAILABLE;
1595 switch (action) {
1596 case M_PROPERTY_GET:
1597 if (!arg)
1598 return M_PROPERTY_ERROR;
1599 *(int *) arg = mpctx->global_sub_pos;
1600 return M_PROPERTY_OK;
1601 case M_PROPERTY_PRINT:
1602 if (!arg)
1603 return M_PROPERTY_ERROR;
1604 char *sub_name = NULL;
1605 if (mpctx->subdata)
1606 sub_name = mpctx->subdata->filename;
1607 #ifdef CONFIG_ASS
1608 if (mpctx->osd->ass_track)
1609 sub_name = mpctx->osd->ass_track->name;
1610 #endif
1611 if (!sub_name && mpctx->subdata)
1612 sub_name = mpctx->subdata->filename;
1613 if (sub_name) {
1614 const char *tmp = mp_basename(sub_name);
1616 *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
1617 mpctx->set_of_sub_pos + 1,
1618 strlen(tmp) < 20 ? "" : "...",
1619 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1620 return M_PROPERTY_OK;
1622 #ifdef CONFIG_DVDNAV
1623 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1624 if (vo_spudec && opts->sub_id >= 0) {
1625 unsigned char lang[3];
1626 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id,
1627 lang)) {
1628 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1629 opts->sub_id, lang);
1630 return M_PROPERTY_OK;
1634 #endif
1636 if ((d_sub->demuxer->type == DEMUXER_TYPE_MATROSKA
1637 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF
1638 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1639 || d_sub->demuxer->type == DEMUXER_TYPE_OGG)
1640 && d_sub->sh && opts->sub_id >= 0) {
1641 struct sh_sub *sh = d_sub->sh;
1642 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
1643 if (sh->title)
1644 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
1645 opts->sub_id, lang, sh->title);
1646 else
1647 *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
1648 opts->sub_id, lang);
1649 return M_PROPERTY_OK;
1652 if (vo_vobsub && vobsub_id >= 0) {
1653 const char *language = mp_gtext("unknown");
1654 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1655 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1656 vobsub_id, language ? language : mp_gtext("unknown"));
1657 return M_PROPERTY_OK;
1659 #ifdef CONFIG_DVDREAD
1660 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1661 && opts->sub_id >= 0) {
1662 char lang[3];
1663 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1664 lang[0] = code >> 8;
1665 lang[1] = code;
1666 lang[2] = 0;
1667 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1668 opts->sub_id, lang);
1669 return M_PROPERTY_OK;
1671 #endif
1672 if (opts->sub_id >= 0) {
1673 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
1674 mp_gtext("unknown"));
1675 return M_PROPERTY_OK;
1677 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1678 return M_PROPERTY_OK;
1680 case M_PROPERTY_SET:
1681 if (!arg)
1682 return M_PROPERTY_ERROR;
1683 if (*(int *) arg < -1)
1684 *(int *) arg = -1;
1685 else if (*(int *) arg >= global_sub_size)
1686 *(int *) arg = global_sub_size - 1;
1687 mpctx->global_sub_pos = *(int *) arg;
1688 break;
1689 case M_PROPERTY_STEP_UP:
1690 mpctx->global_sub_pos += 2;
1691 mpctx->global_sub_pos =
1692 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1693 break;
1694 case M_PROPERTY_STEP_DOWN:
1695 mpctx->global_sub_pos += global_sub_size + 1;
1696 mpctx->global_sub_pos =
1697 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1698 break;
1699 default:
1700 return M_PROPERTY_NOT_IMPLEMENTED;
1703 if (mpctx->global_sub_pos >= 0) {
1704 source = sub_source(mpctx);
1705 source_pos = sub_source_pos(mpctx);
1708 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1709 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1710 global_sub_size,
1711 mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1712 mpctx->sub_counts[SUB_SOURCE_SUBS],
1713 mpctx->sub_counts[SUB_SOURCE_DEMUX],
1714 mpctx->global_sub_pos, source);
1716 mpctx->set_of_sub_pos = -1;
1717 mpctx->subdata = NULL;
1719 vobsub_id = -1;
1720 opts->sub_id = -1;
1721 if (d_sub) {
1722 if (d_sub->id > -2)
1723 reset_spu = 1;
1724 d_sub->id = -2;
1726 mpctx->osd->ass_track = NULL;
1727 uninit_player(mpctx, INITIALIZED_SUB);
1729 if (source == SUB_SOURCE_VOBSUB)
1730 vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1731 else if (source == SUB_SOURCE_SUBS) {
1732 mpctx->set_of_sub_pos = source_pos;
1733 #ifdef CONFIG_ASS
1734 if (opts->ass_enabled
1735 && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
1736 mpctx->osd->ass_track =
1737 mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
1738 mpctx->osd->ass_track_changed = true;
1739 mpctx->osd->vsfilter_aspect =
1740 mpctx->track_was_native_ass[mpctx->set_of_sub_pos];
1741 } else
1742 #endif
1744 mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1745 vo_osd_changed(OSDTYPE_SUBTITLE);
1747 } else if (source == SUB_SOURCE_DEMUX) {
1748 opts->sub_id = source_pos;
1749 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1750 int i = 0;
1751 // default: assume 1:1 mapping of sid and stream id
1752 d_sub->id = opts->sub_id;
1753 d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
1754 ds_free_packs(d_sub);
1755 for (i = 0; i < MAX_S_STREAMS; i++) {
1756 sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
1757 if (sh && sh->sid == opts->sub_id) {
1758 d_sub->id = i;
1759 d_sub->sh = sh;
1760 break;
1763 if (d_sub->sh && d_sub->id >= 0) {
1764 sh_sub_t *sh = d_sub->sh;
1765 if (sh->type == 'v')
1766 init_vo_spudec(mpctx);
1767 else {
1768 sub_init(sh, mpctx->osd);
1769 mpctx->initialized_flags |= INITIALIZED_SUB;
1771 } else {
1772 d_sub->id = -2;
1773 d_sub->sh = NULL;
1777 #ifdef CONFIG_DVDREAD
1778 if (vo_spudec
1779 && (mpctx->stream->type == STREAMTYPE_DVD
1780 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1781 && opts->sub_id < 0 && reset_spu) {
1782 d_sub->id = -2;
1783 d_sub->sh = NULL;
1785 #endif
1787 update_subtitles(mpctx, 0, 0, true);
1789 return M_PROPERTY_OK;
1792 /// Selected sub source (RW)
1793 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1794 MPContext *mpctx)
1796 int source;
1797 update_global_sub_size(mpctx);
1798 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1799 return M_PROPERTY_UNAVAILABLE;
1801 switch (action) {
1802 case M_PROPERTY_GET:
1803 if (!arg)
1804 return M_PROPERTY_ERROR;
1805 *(int *) arg = sub_source(mpctx);
1806 return M_PROPERTY_OK;
1807 case M_PROPERTY_PRINT:
1808 if (!arg)
1809 return M_PROPERTY_ERROR;
1810 char *sourcename;
1811 switch (sub_source(mpctx)) {
1812 case SUB_SOURCE_SUBS:
1813 sourcename = mp_gtext("file");
1814 break;
1815 case SUB_SOURCE_VOBSUB:
1816 sourcename = mp_gtext("vobsub");
1817 break;
1818 case SUB_SOURCE_DEMUX:
1819 sourcename = mp_gtext("embedded");
1820 break;
1821 default:
1822 sourcename = mp_gtext("disabled");
1824 *(char **)arg = talloc_strdup(NULL, sourcename);
1825 return M_PROPERTY_OK;
1826 case M_PROPERTY_SET:
1827 if (!arg)
1828 return M_PROPERTY_ERROR;
1829 M_PROPERTY_CLAMP(prop, *(int *)arg);
1830 if (*(int *) arg < 0)
1831 mpctx->global_sub_pos = -1;
1832 else if (*(int *) arg != sub_source(mpctx)) {
1833 int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1834 if (new_pos == -1)
1835 return M_PROPERTY_UNAVAILABLE;
1836 mpctx->global_sub_pos = new_pos;
1838 break;
1839 case M_PROPERTY_STEP_UP:
1840 case M_PROPERTY_STEP_DOWN: {
1841 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1842 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1843 int step = (step_all > 0) ? 1 : -1;
1844 int cur_source = sub_source(mpctx);
1845 source = cur_source;
1846 while (step_all) {
1847 source += step;
1848 if (source >= SUB_SOURCES)
1849 source = -1;
1850 else if (source < -1)
1851 source = SUB_SOURCES - 1;
1852 if (source == cur_source || source == -1 ||
1853 mpctx->sub_counts[source])
1854 step_all -= step;
1856 if (source == cur_source)
1857 return M_PROPERTY_OK;
1858 if (source == -1)
1859 mpctx->global_sub_pos = -1;
1860 else
1861 mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1862 break;
1864 default:
1865 return M_PROPERTY_NOT_IMPLEMENTED;
1867 --mpctx->global_sub_pos;
1868 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1871 /// Selected subtitles from specific source (RW)
1872 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1873 MPContext *mpctx)
1875 int source, is_cur_source, offset, new_pos;
1876 update_global_sub_size(mpctx);
1877 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1878 return M_PROPERTY_UNAVAILABLE;
1880 if (!strcmp(prop->name, "sub_file"))
1881 source = SUB_SOURCE_SUBS;
1882 else if (!strcmp(prop->name, "sub_vob"))
1883 source = SUB_SOURCE_VOBSUB;
1884 else if (!strcmp(prop->name, "sub_demux"))
1885 source = SUB_SOURCE_DEMUX;
1886 else
1887 return M_PROPERTY_ERROR;
1889 offset = sub_pos_by_source(mpctx, source);
1890 if (offset < 0)
1891 return M_PROPERTY_UNAVAILABLE;
1893 is_cur_source = sub_source(mpctx) == source;
1894 new_pos = mpctx->global_sub_pos;
1895 switch (action) {
1896 case M_PROPERTY_GET:
1897 if (!arg)
1898 return M_PROPERTY_ERROR;
1899 if (is_cur_source) {
1900 *(int *) arg = sub_source_pos(mpctx);
1901 if (source == SUB_SOURCE_VOBSUB)
1902 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1903 } else
1904 *(int *) arg = -1;
1905 return M_PROPERTY_OK;
1906 case M_PROPERTY_PRINT:
1907 if (!arg)
1908 return M_PROPERTY_ERROR;
1909 if (is_cur_source)
1910 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1911 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1912 return M_PROPERTY_OK;
1913 case M_PROPERTY_SET:
1914 if (!arg)
1915 return M_PROPERTY_ERROR;
1916 if (*(int *) arg >= 0) {
1917 int index = *(int *)arg;
1918 if (source == SUB_SOURCE_VOBSUB)
1919 index = vobsub_get_index_by_id(vo_vobsub, index);
1920 new_pos = offset + index;
1921 if (index < 0 || index > mpctx->sub_counts[source]) {
1922 new_pos = -1;
1923 *(int *) arg = -1;
1925 } else
1926 new_pos = -1;
1927 break;
1928 case M_PROPERTY_STEP_UP:
1929 case M_PROPERTY_STEP_DOWN: {
1930 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1931 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1932 int step = (step_all > 0) ? 1 : -1;
1933 int max_sub_pos_for_source = -1;
1934 if (!is_cur_source)
1935 new_pos = -1;
1936 while (step_all) {
1937 if (new_pos == -1) {
1938 if (step > 0)
1939 new_pos = offset;
1940 else if (max_sub_pos_for_source == -1) {
1941 // Find max pos for specific source
1942 new_pos = mpctx->global_sub_size - 1;
1943 while (new_pos >= 0 && sub_source(mpctx) != source)
1944 new_pos--;
1945 } else
1946 new_pos = max_sub_pos_for_source;
1947 } else {
1948 new_pos += step;
1949 if (new_pos < offset ||
1950 new_pos >= mpctx->global_sub_size ||
1951 sub_source(mpctx) != source)
1952 new_pos = -1;
1954 step_all -= step;
1956 break;
1958 default:
1959 return M_PROPERTY_NOT_IMPLEMENTED;
1961 return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1964 /// Subtitle delay (RW)
1965 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1966 MPContext *mpctx)
1968 if (!mpctx->sh_video)
1969 return M_PROPERTY_UNAVAILABLE;
1970 return m_property_delay(prop, action, arg, &sub_delay);
1973 /// Alignment of text subtitles (RW)
1974 static int mp_property_sub_alignment(m_option_t *prop, int action,
1975 void *arg, MPContext *mpctx)
1977 char *name[] = {
1978 _("top"), _("center"), _("bottom")
1981 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1982 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1983 return M_PROPERTY_UNAVAILABLE;
1985 switch (action) {
1986 case M_PROPERTY_PRINT:
1987 if (!arg)
1988 return M_PROPERTY_ERROR;
1989 M_PROPERTY_CLAMP(prop, sub_alignment);
1990 *(char **) arg = talloc_strdup(NULL, mp_gtext(name[sub_alignment]));
1991 return M_PROPERTY_OK;
1992 case M_PROPERTY_SET:
1993 if (!arg)
1994 return M_PROPERTY_ERROR;
1995 case M_PROPERTY_STEP_UP:
1996 case M_PROPERTY_STEP_DOWN:
1997 vo_osd_changed(OSDTYPE_SUBTITLE);
1998 default:
1999 return m_property_choice(prop, action, arg, &sub_alignment);
2003 /// Subtitle visibility (RW)
2004 static int mp_property_sub_visibility(m_option_t *prop, int action,
2005 void *arg, MPContext *mpctx)
2007 if (!mpctx->sh_video)
2008 return M_PROPERTY_UNAVAILABLE;
2010 switch (action) {
2011 case M_PROPERTY_SET:
2012 if (!arg)
2013 return M_PROPERTY_ERROR;
2014 case M_PROPERTY_STEP_UP:
2015 case M_PROPERTY_STEP_DOWN:
2016 vo_osd_changed(OSDTYPE_SUBTITLE);
2017 if (vo_spudec)
2018 vo_osd_changed(OSDTYPE_SPU);
2019 default:
2020 return m_property_flag(prop, action, arg, &sub_visibility);
2024 #ifdef CONFIG_ASS
2025 /// Use margins for libass subtitles (RW)
2026 static int mp_property_ass_use_margins(m_option_t *prop, int action,
2027 void *arg, MPContext *mpctx)
2029 struct MPOpts *opts = &mpctx->opts;
2030 if (!mpctx->sh_video)
2031 return M_PROPERTY_UNAVAILABLE;
2033 switch (action) {
2034 case M_PROPERTY_SET:
2035 if (!arg)
2036 return M_PROPERTY_ERROR;
2037 case M_PROPERTY_STEP_UP:
2038 case M_PROPERTY_STEP_DOWN:
2039 mpctx->osd->ass_force_reload = true;
2040 default:
2041 return m_property_flag(prop, action, arg, &opts->ass_use_margins);
2045 static int mp_property_ass_vsfilter_aspect_compat(m_option_t *prop, int action,
2046 void *arg, MPContext *mpctx)
2048 if (!mpctx->sh_video)
2049 return M_PROPERTY_UNAVAILABLE;
2051 switch (action) {
2052 case M_PROPERTY_SET:
2053 if (!arg)
2054 return M_PROPERTY_ERROR;
2055 case M_PROPERTY_STEP_UP:
2056 case M_PROPERTY_STEP_DOWN:
2057 //has to re-render subs with new aspect ratio
2058 mpctx->osd->ass_force_reload = 1;
2059 default:
2060 return m_property_flag(prop, action, arg,
2061 &mpctx->opts.ass_vsfilter_aspect_compat);
2065 #endif
2067 /// Show only forced subtitles (RW)
2068 static int mp_property_sub_forced_only(m_option_t *prop, int action,
2069 void *arg, MPContext *mpctx)
2071 if (!vo_spudec)
2072 return M_PROPERTY_UNAVAILABLE;
2074 switch (action) {
2075 case M_PROPERTY_SET:
2076 if (!arg)
2077 return M_PROPERTY_ERROR;
2078 case M_PROPERTY_STEP_UP:
2079 case M_PROPERTY_STEP_DOWN:
2080 m_property_flag(prop, action, arg, &forced_subs_only);
2081 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
2082 return M_PROPERTY_OK;
2083 default:
2084 return m_property_flag(prop, action, arg, &forced_subs_only);
2089 #ifdef CONFIG_FREETYPE
2090 /// Subtitle scale (RW)
2091 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
2092 MPContext *mpctx)
2094 struct MPOpts *opts = &mpctx->opts;
2096 switch (action) {
2097 case M_PROPERTY_SET:
2098 if (!arg)
2099 return M_PROPERTY_ERROR;
2100 M_PROPERTY_CLAMP(prop, *(float *) arg);
2101 #ifdef CONFIG_ASS
2102 if (opts->ass_enabled) {
2103 opts->ass_font_scale = *(float *) arg;
2104 mpctx->osd->ass_force_reload = true;
2106 #endif
2107 text_font_scale_factor = *(float *) arg;
2108 force_load_font = 1;
2109 vo_osd_changed(OSDTYPE_SUBTITLE);
2110 return M_PROPERTY_OK;
2111 case M_PROPERTY_STEP_UP:
2112 case M_PROPERTY_STEP_DOWN:
2113 #ifdef CONFIG_ASS
2114 if (opts->ass_enabled) {
2115 opts->ass_font_scale += (arg ? *(float *) arg : 0.1) *
2116 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2117 M_PROPERTY_CLAMP(prop, opts->ass_font_scale);
2118 mpctx->osd->ass_force_reload = true;
2120 #endif
2121 text_font_scale_factor += (arg ? *(float *) arg : 0.1) *
2122 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2123 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
2124 force_load_font = 1;
2125 vo_osd_changed(OSDTYPE_SUBTITLE);
2126 return M_PROPERTY_OK;
2127 default:
2128 #ifdef CONFIG_ASS
2129 if (opts->ass_enabled)
2130 return m_property_float_ro(prop, action, arg, opts->ass_font_scale);
2131 else
2132 #endif
2133 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
2136 #endif
2139 #ifdef CONFIG_TV
2141 /// TV color settings (RW)
2142 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
2143 MPContext *mpctx)
2145 int r, val;
2146 tvi_handle_t *tvh = mpctx->demuxer->priv;
2147 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
2148 return M_PROPERTY_UNAVAILABLE;
2150 switch (action) {
2151 case M_PROPERTY_SET:
2152 if (!arg)
2153 return M_PROPERTY_ERROR;
2154 M_PROPERTY_CLAMP(prop, *(int *) arg);
2155 return tv_set_color_options(tvh, prop->offset, *(int *) arg);
2156 case M_PROPERTY_GET:
2157 return tv_get_color_options(tvh, prop->offset, arg);
2158 case M_PROPERTY_STEP_UP:
2159 case M_PROPERTY_STEP_DOWN:
2160 if ((r = tv_get_color_options(tvh, prop->offset, &val)) >= 0) {
2161 if (!r)
2162 return M_PROPERTY_ERROR;
2163 val += (arg ? *(int *) arg : 1) *
2164 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2165 M_PROPERTY_CLAMP(prop, val);
2166 return tv_set_color_options(tvh, prop->offset, val);
2168 return M_PROPERTY_ERROR;
2170 return M_PROPERTY_NOT_IMPLEMENTED;
2173 #endif
2175 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
2176 MPContext *mpctx)
2178 int val, result;
2179 int base_ioctl = prop->offset;
2181 for teletext's GET,SET,STEP ioctls this is not 0
2182 SET is GET+1
2183 STEP is GET+2
2185 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2186 return M_PROPERTY_UNAVAILABLE;
2187 if (!base_ioctl)
2188 return M_PROPERTY_ERROR;
2190 switch (action) {
2191 case M_PROPERTY_GET:
2192 if (!arg)
2193 return M_PROPERTY_ERROR;
2194 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2195 break;
2196 case M_PROPERTY_SET:
2197 if (!arg)
2198 return M_PROPERTY_ERROR;
2199 M_PROPERTY_CLAMP(prop, *(int *) arg);
2200 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2201 arg);
2202 break;
2203 case M_PROPERTY_STEP_UP:
2204 case M_PROPERTY_STEP_DOWN:
2205 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2206 val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2207 -1 : 1);
2208 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2209 &val);
2210 break;
2211 default:
2212 return M_PROPERTY_NOT_IMPLEMENTED;
2215 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2218 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2219 MPContext *mpctx)
2221 int result;
2222 int val;
2224 //with tvh==NULL will fail too
2225 result = mp_property_teletext_common(prop, action, arg, mpctx);
2226 if (result != M_PROPERTY_OK)
2227 return result;
2229 if (teletext_control(mpctx->demuxer->teletext,
2230 prop->offset, &val) == VBI_CONTROL_TRUE && val)
2231 mp_input_set_section(mpctx->input, "teletext");
2232 else
2233 mp_input_set_section(mpctx->input, "tv");
2234 return M_PROPERTY_OK;
2237 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2238 MPContext *mpctx)
2240 int result;
2241 int val;
2242 if (!mpctx->demuxer->teletext)
2243 return M_PROPERTY_UNAVAILABLE;
2244 switch (action) {
2245 case M_PROPERTY_STEP_UP:
2246 case M_PROPERTY_STEP_DOWN:
2247 //This should be handled separately
2248 val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2249 -1 : 1);
2250 result = teletext_control(mpctx->demuxer->teletext,
2251 TV_VBI_CONTROL_STEP_PAGE, &val);
2252 break;
2253 default:
2254 result = mp_property_teletext_common(prop, action, arg, mpctx);
2256 return result;
2260 /// All properties available in MPlayer.
2261 /** \ingroup Properties
2263 static const m_option_t mp_properties[] = {
2264 // General
2265 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2266 M_OPT_RANGE, 0, 3, NULL },
2267 { "loop", mp_property_loop, CONF_TYPE_INT,
2268 M_OPT_MIN, -1, 0, NULL },
2269 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2270 M_OPT_RANGE, 0.01, 100.0, NULL },
2271 { "filename", mp_property_filename, CONF_TYPE_STRING,
2272 0, 0, 0, NULL },
2273 { "path", mp_property_path, CONF_TYPE_STRING,
2274 0, 0, 0, NULL },
2275 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2276 0, 0, 0, NULL },
2277 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2278 M_OPT_MIN, 0, 0, NULL },
2279 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2280 M_OPT_MIN, 0, 0, NULL },
2281 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2282 M_OPT_MIN, 0, 0, NULL },
2283 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2284 M_OPT_MIN, 0, 0, NULL },
2285 { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2286 M_OPT_MIN, 0, 0, NULL },
2287 { "length", mp_property_length, CONF_TYPE_TIME,
2288 M_OPT_MIN, 0, 0, NULL },
2289 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2290 M_OPT_RANGE, 0, 100, NULL },
2291 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2292 M_OPT_MIN, 0, 0, NULL },
2293 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2294 M_OPT_MIN, 0, 0, NULL },
2295 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2296 0, 0, 0, NULL },
2297 { "angle", mp_property_angle, CONF_TYPE_INT,
2298 CONF_RANGE, -2, 10, NULL },
2299 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2300 0, 0, 0, NULL },
2301 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2302 M_OPT_RANGE, 0, 1, NULL },
2303 { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2304 M_OPT_RANGE, 0, 1, NULL },
2305 { "pts_association_mode", mp_property_generic_option, &m_option_type_choice,
2306 0, 0, 0, "pts-association-mode" },
2307 { "hr_seek", mp_property_generic_option, &m_option_type_choice,
2308 0, 0, 0, "hr-seek" },
2310 // Audio
2311 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2312 M_OPT_RANGE, 0, 100, NULL },
2313 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2314 M_OPT_RANGE, 0, 1, NULL },
2315 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2316 M_OPT_RANGE, -100, 100, NULL },
2317 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2318 0, 0, 0, NULL },
2319 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2320 0, 0, 0, NULL },
2321 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2322 0, 0, 0, NULL },
2323 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2324 0, 0, 0, NULL },
2325 { "channels", mp_property_channels, CONF_TYPE_INT,
2326 0, 0, 0, NULL },
2327 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2328 CONF_RANGE, -2, 65535, NULL },
2329 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2330 M_OPT_RANGE, -1, 1, NULL },
2332 // Video
2333 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2334 M_OPT_RANGE, 0, 1, NULL },
2335 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2336 M_OPT_RANGE, 0, 1, NULL },
2337 { "colormatrix", mp_property_colormatrix, &m_option_type_choice,
2338 0, 0, 0, "colormatrix" },
2339 { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice,
2340 0, 0, 0, "colormatrix-input-range" },
2341 { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice,
2342 0, 0, 0, "colormatrix-output-range" },
2343 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2344 M_OPT_RANGE, 0, 1, NULL },
2345 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2346 M_OPT_RANGE, 0, 1, NULL },
2347 { "border", mp_property_border, CONF_TYPE_FLAG,
2348 M_OPT_RANGE, 0, 1, NULL },
2349 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2350 M_OPT_RANGE, 0, 2, NULL },
2351 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2352 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_gamma)},
2353 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2354 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_brightness) },
2355 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2356 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_contrast) },
2357 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2358 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_saturation) },
2359 { "hue", mp_property_gamma, CONF_TYPE_INT,
2360 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_hue) },
2361 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2362 M_OPT_RANGE, 0, 1, NULL },
2363 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2364 M_OPT_RANGE, 0, 1, NULL },
2365 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2366 0, 0, 0, NULL },
2367 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2368 0, 0, 0, NULL },
2369 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2370 0, 0, 0, NULL },
2371 { "width", mp_property_width, CONF_TYPE_INT,
2372 0, 0, 0, NULL },
2373 { "height", mp_property_height, CONF_TYPE_INT,
2374 0, 0, 0, NULL },
2375 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2376 0, 0, 0, NULL },
2377 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2378 0, 0, 0, NULL },
2379 { "switch_video", mp_property_video, CONF_TYPE_INT,
2380 CONF_RANGE, -2, 65535, NULL },
2381 { "switch_program", mp_property_program, CONF_TYPE_INT,
2382 CONF_RANGE, -1, 65535, NULL },
2384 // Subs
2385 { "sub", mp_property_sub, CONF_TYPE_INT,
2386 M_OPT_MIN, -1, 0, NULL },
2387 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2388 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2389 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2390 M_OPT_MIN, -1, 0, NULL },
2391 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2392 M_OPT_MIN, -1, 0, NULL },
2393 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2394 M_OPT_MIN, -1, 0, NULL },
2395 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2396 0, 0, 0, NULL },
2397 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2398 M_OPT_RANGE, 0, 100, NULL },
2399 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2400 M_OPT_RANGE, 0, 2, NULL },
2401 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2402 M_OPT_RANGE, 0, 1, NULL },
2403 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2404 M_OPT_RANGE, 0, 1, NULL },
2405 #ifdef CONFIG_FREETYPE
2406 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2407 M_OPT_RANGE, 0, 100, NULL },
2408 #endif
2409 #ifdef CONFIG_ASS
2410 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2411 M_OPT_RANGE, 0, 1, NULL },
2412 { "ass_vsfilter_aspect_compat", mp_property_ass_vsfilter_aspect_compat,
2413 CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL },
2414 #endif
2416 #ifdef CONFIG_TV
2417 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2418 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS },
2419 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2420 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST },
2421 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2422 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION },
2423 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2424 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
2425 #endif
2426 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2427 M_OPT_RANGE, 100, 899, .offset = TV_VBI_CONTROL_GET_PAGE },
2428 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2429 M_OPT_RANGE, 0, 64, .offset = TV_VBI_CONTROL_GET_SUBPAGE },
2430 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2431 M_OPT_RANGE, 0, 1, .offset = TV_VBI_CONTROL_GET_MODE },
2432 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2433 M_OPT_RANGE, 0, 3, .offset = TV_VBI_CONTROL_GET_FORMAT },
2434 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2435 M_OPT_RANGE, 0, 2, .offset = TV_VBI_CONTROL_GET_HALF_PAGE },
2436 { NULL, NULL, NULL, 0, 0, 0, NULL }
2440 int mp_property_do(const char *name, int action, void *val, void *ctx)
2442 return m_property_do(mp_properties, name, action, val, ctx);
2445 char *mp_property_print(const char *name, void *ctx)
2447 char *ret = NULL;
2448 if (mp_property_do(name, M_PROPERTY_PRINT, &ret, ctx) <= 0)
2449 return NULL;
2450 return ret;
2453 char *property_expand_string(MPContext *mpctx, char *str)
2455 return m_properties_expand_string(mp_properties, str, mpctx);
2458 void property_print_help(void)
2460 m_properties_print_help_list(mp_properties);
2464 /* List of default ways to show a property on OSD.
2466 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2467 * a bar showing the current position between min/max values of the
2468 * property. In this case osd_msg is only used for terminal output
2469 * if there is no video; it'll be a label shown together with percentage.
2471 * Otherwise setting osd_msg will show the string on OSD, formatted with
2472 * the text value of the property as argument.
2474 static struct property_osd_display {
2475 /// property name
2476 const char *name;
2477 /// progressbar type
2478 int osd_progbar; // -1 is special value for seek indicators
2479 /// osd msg id if it must be shared
2480 int osd_id;
2481 /// osd msg template
2482 const char *osd_msg;
2483 } property_osd_display[] = {
2484 // general
2485 { "loop", 0, -1, _("Loop: %s") },
2486 { "chapter", -1, -1, NULL },
2487 { "capturing", 0, -1, _("Capturing: %s") },
2488 { "pts_association_mode", 0, -1, "PTS association mode: %s" },
2489 { "hr_seek", 0, -1, "hr-seek: %s" },
2490 { "speed", 0, -1, _("Speed: x %6s") },
2491 // audio
2492 { "volume", OSD_VOLUME, -1, _("Volume") },
2493 { "mute", 0, -1, _("Mute: %s") },
2494 { "audio_delay", 0, -1, _("A-V delay: %s") },
2495 { "switch_audio", 0, -1, _("Audio: %s") },
2496 { "balance", OSD_BALANCE, -1, _("Balance") },
2497 // video
2498 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2499 { "ontop", 0, -1, _("Stay on top: %s") },
2500 { "rootwin", 0, -1, _("Rootwin: %s") },
2501 { "border", 0, -1, _("Border: %s") },
2502 { "framedropping", 0, -1, _("Framedropping: %s") },
2503 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2504 { "colormatrix", 0, -1, _("YUV colormatrix: %s") },
2505 { "colormatrix_input_range", 0, -1, _("YUV input range: %s") },
2506 { "colormatrix_output_range", 0, -1, _("RGB output range: %s") },
2507 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2508 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2509 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2510 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2511 { "hue", OSD_HUE, -1, _("Hue") },
2512 { "vsync", 0, -1, _("VSync: %s") },
2513 // subs
2514 { "sub", 0, -1, _("Subtitles: %s") },
2515 { "sub_source", 0, -1, _("Sub source: %s") },
2516 { "sub_vob", 0, -1, _("Subtitles: %s") },
2517 { "sub_demux", 0, -1, _("Subtitles: %s") },
2518 { "sub_file", 0, -1, _("Subtitles: %s") },
2519 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2520 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2521 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2522 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2523 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2524 #ifdef CONFIG_FREETYPE
2525 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2526 #endif
2527 { "ass_vsfilter_aspect_compat", 0, -1,
2528 _("Subtitle VSFilter aspect compat: %s")},
2529 #ifdef CONFIG_TV
2530 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2531 { "tv_hue", OSD_HUE, -1, _("Hue") },
2532 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2533 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2534 #endif
2538 static int show_property_osd(MPContext *mpctx, const char *pname)
2540 struct MPOpts *opts = &mpctx->opts;
2541 int r;
2542 m_option_t *prop;
2543 struct property_osd_display *p;
2545 // look for the command
2546 for (p = property_osd_display; p->name; p++)
2547 if (!strcmp(p->name, pname))
2548 break;
2550 if (!p->name)
2551 return -1;
2553 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2554 return -1;
2556 if (p->osd_progbar == -1)
2557 mpctx->add_osd_seek_info = true;
2558 else if (p->osd_progbar) {
2559 if (prop->type == CONF_TYPE_INT) {
2560 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2561 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2562 prop->min, prop->max, r);
2563 } else if (prop->type == CONF_TYPE_FLOAT) {
2564 float f;
2565 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2566 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2567 prop->min, prop->max, f);
2568 } else {
2569 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2570 "Property use an unsupported type.\n");
2571 return -1;
2573 return 0;
2576 if (p->osd_msg) {
2577 char *val = mp_property_print(pname, mpctx);
2578 if (val) {
2579 int index = p - property_osd_display;
2580 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2581 1, opts->osd_duration, p->osd_msg, val);
2582 talloc_free(val);
2585 return 0;
2590 * Command to property bridge
2592 * It is used to handle most commands that just set a property
2593 * and optionally display something on the OSD.
2594 * Two kinds of commands are handled: adjust or toggle.
2596 * Adjust commands take 1 or 2 parameters: <value> <abs>
2597 * If <abs> is non-zero the property is set to the given value
2598 * otherwise it is adjusted.
2600 * Toggle commands take 0 or 1 parameters. With no parameter
2601 * or a value less than the property minimum it just steps the
2602 * property to its next or previous value respectively.
2603 * Otherwise it sets it to the given value.
2606 /// List of the commands that can be handled by setting a property.
2607 static struct {
2608 /// property name
2609 const char *name;
2610 /// cmd id
2611 int cmd;
2612 /// set/adjust or toggle command
2613 int toggle;
2614 } set_prop_cmd[] = {
2615 // general
2616 { "loop", MP_CMD_LOOP, 0},
2617 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2618 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2619 { "pause", MP_CMD_PAUSE, 0},
2620 { "capturing", MP_CMD_CAPTURING, 1},
2621 // audio
2622 { "volume", MP_CMD_VOLUME, 0},
2623 { "mute", MP_CMD_MUTE, 1},
2624 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2625 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2626 { "balance", MP_CMD_BALANCE, 0},
2627 // video
2628 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2629 { "panscan", MP_CMD_PANSCAN, 0},
2630 { "ontop", MP_CMD_VO_ONTOP, 1},
2631 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2632 { "border", MP_CMD_VO_BORDER, 1},
2633 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2634 { "gamma", MP_CMD_GAMMA, 0},
2635 { "brightness", MP_CMD_BRIGHTNESS, 0},
2636 { "contrast", MP_CMD_CONTRAST, 0},
2637 { "saturation", MP_CMD_SATURATION, 0},
2638 { "hue", MP_CMD_HUE, 0},
2639 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2640 // subs
2641 { "sub", MP_CMD_SUB_SELECT, 1},
2642 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2643 { "sub_vob", MP_CMD_SUB_VOB, 1},
2644 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2645 { "sub_file", MP_CMD_SUB_FILE, 1},
2646 { "sub_pos", MP_CMD_SUB_POS, 0},
2647 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2648 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2649 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2650 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2651 #ifdef CONFIG_FREETYPE
2652 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2653 #endif
2654 #ifdef CONFIG_ASS
2655 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2656 #endif
2657 #ifdef CONFIG_TV
2658 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2659 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2660 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2661 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2662 #endif
2666 /// Handle commands that set a property.
2667 static bool set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2669 int i, r;
2670 m_option_t *prop;
2671 const char *pname;
2673 // look for the command
2674 for (i = 0; set_prop_cmd[i].name; i++)
2675 if (set_prop_cmd[i].cmd == cmd->id)
2676 break;
2677 if (!(pname = set_prop_cmd[i].name))
2678 return 0;
2680 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2681 return 0;
2683 // toggle command
2684 if (set_prop_cmd[i].toggle) {
2685 // set to value
2686 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2687 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2688 else if (cmd->nargs > 0)
2689 r = mp_property_do(pname, M_PROPERTY_STEP_DOWN, NULL, mpctx);
2690 else
2691 r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2692 } else if (cmd->args[1].v.i) //set
2693 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2694 else // adjust
2695 r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2697 if (r <= 0)
2698 return 1;
2700 show_property_osd(mpctx, pname);
2702 return 1;
2705 #ifdef CONFIG_DVDNAV
2706 static const struct {
2707 const char *name;
2708 const enum mp_command_type cmd;
2709 } mp_dvdnav_bindings[] = {
2710 { "up", MP_CMD_DVDNAV_UP },
2711 { "down", MP_CMD_DVDNAV_DOWN },
2712 { "left", MP_CMD_DVDNAV_LEFT },
2713 { "right", MP_CMD_DVDNAV_RIGHT },
2714 { "menu", MP_CMD_DVDNAV_MENU },
2715 { "select", MP_CMD_DVDNAV_SELECT },
2716 { "prev", MP_CMD_DVDNAV_PREVMENU },
2717 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2720 * keep old dvdnav sub-command options for a while in order not to
2721 * break slave-mode API too suddenly.
2723 { "1", MP_CMD_DVDNAV_UP },
2724 { "2", MP_CMD_DVDNAV_DOWN },
2725 { "3", MP_CMD_DVDNAV_LEFT },
2726 { "4", MP_CMD_DVDNAV_RIGHT },
2727 { "5", MP_CMD_DVDNAV_MENU },
2728 { "6", MP_CMD_DVDNAV_SELECT },
2729 { "7", MP_CMD_DVDNAV_PREVMENU },
2730 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2731 { NULL, 0 }
2733 #endif
2735 static const char *property_error_string(int error_value)
2737 switch (error_value) {
2738 case M_PROPERTY_ERROR:
2739 return "ERROR";
2740 case M_PROPERTY_UNAVAILABLE:
2741 return "PROPERTY_UNAVAILABLE";
2742 case M_PROPERTY_NOT_IMPLEMENTED:
2743 return "NOT_IMPLEMENTED";
2744 case M_PROPERTY_UNKNOWN:
2745 return "PROPERTY_UNKNOWN";
2746 case M_PROPERTY_DISABLED:
2747 return "DISABLED";
2749 return "UNKNOWN";
2752 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2754 int idx;
2755 int end = start + count;
2756 int after = mpctx->set_of_sub_size - end;
2757 sub_data **subs = mpctx->set_of_subtitles;
2758 #ifdef CONFIG_ASS
2759 struct ass_track **ass_tracks = mpctx->set_of_ass_tracks;
2760 #endif
2761 if (count < 0 || count > mpctx->set_of_sub_size ||
2762 start < 0 || start > mpctx->set_of_sub_size - count) {
2763 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2764 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2765 return;
2767 for (idx = start; idx < end; idx++) {
2768 sub_data *subd = subs[idx];
2769 char *filename = "";
2770 if (subd)
2771 filename = subd->filename;
2772 #ifdef CONFIG_ASS
2773 if (!subd)
2774 filename = ass_tracks[idx]->name;
2775 #endif
2776 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2777 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2778 filename_recode(filename));
2779 sub_free(subd);
2780 subs[idx] = NULL;
2781 #ifdef CONFIG_ASS
2782 if (ass_tracks[idx])
2783 ass_free_track(ass_tracks[idx]);
2784 ass_tracks[idx] = NULL;
2785 #endif
2788 mpctx->global_sub_size -= count;
2789 mpctx->set_of_sub_size -= count;
2790 if (mpctx->set_of_sub_size <= 0)
2791 mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2793 memmove(subs + start, subs + end, after * sizeof(*subs));
2794 memset(subs + start + after, 0, count * sizeof(*subs));
2795 #ifdef CONFIG_ASS
2796 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2797 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2798 #endif
2800 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2801 mpctx->global_sub_pos = -2;
2802 mpctx->subdata = NULL;
2803 mpctx->osd->ass_track = NULL;
2804 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2805 } else if (mpctx->set_of_sub_pos >= end) {
2806 mpctx->set_of_sub_pos -= count;
2807 mpctx->global_sub_pos -= count;
2811 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2813 struct MPOpts *opts = &mpctx->opts;
2814 sh_audio_t *const sh_audio = mpctx->sh_audio;
2815 sh_video_t *const sh_video = mpctx->sh_video;
2816 int osd_duration = opts->osd_duration;
2817 int case_fallthrough_hack = 0;
2818 if (set_property_command(mpctx, cmd))
2819 goto old_pause_hack; // was handled already
2820 switch (cmd->id) {
2821 case MP_CMD_SEEK: {
2822 mpctx->add_osd_seek_info = true;
2823 float v = cmd->args[0].v.f;
2824 int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2825 int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0;
2826 if (abs == 2) { // Absolute seek to a timestamp in seconds
2827 queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact);
2828 mpctx->osd_function = v > get_current_time(mpctx) ?
2829 OSD_FFW : OSD_REW;
2830 } else if (abs) { /* Absolute seek by percentage */
2831 queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact);
2832 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2833 } else {
2834 queue_seek(mpctx, MPSEEK_RELATIVE, v, exact);
2835 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2837 break;
2840 case MP_CMD_SET_PROPERTY_OSD:
2841 case_fallthrough_hack = 1;
2843 case MP_CMD_SET_PROPERTY: {
2844 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2845 cmd->args[1].v.s, mpctx);
2846 if (r == M_PROPERTY_UNKNOWN)
2847 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2848 "Unknown property: '%s'\n", cmd->args[0].v.s);
2849 else if (r <= 0)
2850 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2851 "Failed to set property '%s' to '%s'.\n",
2852 cmd->args[0].v.s, cmd->args[1].v.s);
2853 else if (case_fallthrough_hack)
2854 show_property_osd(mpctx, cmd->args[0].v.s);
2855 if (r <= 0)
2856 mp_msg(MSGT_GLOBAL, MSGL_INFO,
2857 "ANS_ERROR=%s\n", property_error_string(r));
2858 break;
2861 case MP_CMD_STEP_PROPERTY_OSD:
2862 case_fallthrough_hack = 1;
2864 case MP_CMD_STEP_PROPERTY: {
2865 void *arg = NULL;
2866 int r, i;
2867 double d;
2868 off_t o;
2869 if (cmd->args[1].v.f) {
2870 m_option_t *prop;
2871 if ((r = mp_property_do(cmd->args[0].v.s,
2872 M_PROPERTY_GET_TYPE,
2873 &prop, mpctx)) <= 0)
2874 goto step_prop_err;
2875 if (prop->type == CONF_TYPE_INT ||
2876 prop->type == CONF_TYPE_FLAG)
2877 i = cmd->args[1].v.f, arg = &i;
2878 else if (prop->type == CONF_TYPE_FLOAT)
2879 arg = &cmd->args[1].v.f;
2880 else if (prop->type == CONF_TYPE_DOUBLE ||
2881 prop->type == CONF_TYPE_TIME)
2882 d = cmd->args[1].v.f, arg = &d;
2883 else if (prop->type == CONF_TYPE_POSITION)
2884 o = cmd->args[1].v.f, arg = &o;
2885 else
2886 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2887 "Ignoring step size stepping property '%s'.\n",
2888 cmd->args[0].v.s);
2890 r = mp_property_do(cmd->args[0].v.s,
2891 cmd->args[2].v.i < 0 ?
2892 M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2893 arg, mpctx);
2894 step_prop_err:
2895 if (r == M_PROPERTY_UNKNOWN)
2896 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2897 "Unknown property: '%s'\n", cmd->args[0].v.s);
2898 else if (r <= 0)
2899 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2900 "Failed to increment property '%s' by %f.\n",
2901 cmd->args[0].v.s, cmd->args[1].v.f);
2902 else if (case_fallthrough_hack)
2903 show_property_osd(mpctx, cmd->args[0].v.s);
2904 if (r <= 0)
2905 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2906 property_error_string(r));
2907 break;
2910 case MP_CMD_GET_PROPERTY: {
2911 char *tmp;
2912 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2913 &tmp, mpctx);
2914 if (r <= 0) {
2915 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2916 "Failed to get value of property '%s'.\n",
2917 cmd->args[0].v.s);
2918 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2919 property_error_string(r));
2920 break;
2922 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2923 cmd->args[0].v.s, tmp);
2924 talloc_free(tmp);
2925 break;
2928 case MP_CMD_EDL_MARK:
2929 if (edl_fd) {
2930 float v = get_current_time(mpctx);
2931 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2932 mpctx->begin_skip = v;
2933 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2934 "EDL skip start, press 'i' again to end block.\n");
2935 } else {
2936 if (mpctx->begin_skip > v)
2937 mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
2938 "EDL skip canceled, last start > stop\n");
2939 else {
2940 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2941 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2942 "EDL skip end, line written.\n");
2944 mpctx->begin_skip = MP_NOPTS_VALUE;
2947 break;
2949 case MP_CMD_SWITCH_RATIO:
2950 if (!sh_video)
2951 break;
2952 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2953 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2954 else
2955 opts->movie_aspect = cmd->args[0].v.f;
2956 mpcodecs_config_vo(sh_video, sh_video->disp_w, sh_video->disp_h, 0);
2957 break;
2959 case MP_CMD_SPEED_INCR: {
2960 float v = cmd->args[0].v.f;
2961 mp_property_do("speed", M_PROPERTY_STEP_UP, &v, mpctx);
2962 show_property_osd(mpctx, "speed");
2963 break;
2966 case MP_CMD_SPEED_MULT:
2967 case_fallthrough_hack = true;
2969 case MP_CMD_SPEED_SET: {
2970 float v = cmd->args[0].v.f;
2971 if (case_fallthrough_hack)
2972 v *= mpctx->opts.playback_speed;
2973 mp_property_do("speed", M_PROPERTY_SET, &v, mpctx);
2974 show_property_osd(mpctx, "speed");
2975 break;
2978 case MP_CMD_FRAME_STEP:
2979 add_step_frame(mpctx);
2980 break;
2982 case MP_CMD_FILE_FILTER:
2983 file_filter = cmd->args[0].v.i;
2984 break;
2986 case MP_CMD_QUIT:
2987 exit_player_with_rc(mpctx, EXIT_QUIT,
2988 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2990 case MP_CMD_PLAY_TREE_STEP: {
2991 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2992 int force = cmd->args[1].v.i;
2995 if (!force && mpctx->playtree_iter) {
2996 play_tree_iter_t *i =
2997 play_tree_iter_new_copy(mpctx->playtree_iter);
2998 if (play_tree_iter_step(i, n, 0) ==
2999 PLAY_TREE_ITER_ENTRY)
3000 mpctx->stop_play =
3001 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
3002 play_tree_iter_free(i);
3003 } else
3004 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
3005 if (mpctx->stop_play)
3006 mpctx->play_tree_step = n;
3008 break;
3011 case MP_CMD_PLAY_TREE_UP_STEP: {
3012 int n = cmd->args[0].v.i > 0 ? 1 : -1;
3013 int force = cmd->args[1].v.i;
3015 if (!force && mpctx->playtree_iter) {
3016 play_tree_iter_t *i =
3017 play_tree_iter_new_copy(mpctx->playtree_iter);
3018 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
3019 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3020 play_tree_iter_free(i);
3021 } else
3022 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3023 break;
3026 case MP_CMD_PLAY_ALT_SRC_STEP:
3027 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
3028 int v = cmd->args[0].v.i;
3029 if (v > 0
3030 && mpctx->playtree_iter->file <
3031 mpctx->playtree_iter->num_files)
3032 mpctx->stop_play = PT_NEXT_SRC;
3033 else if (v < 0 && mpctx->playtree_iter->file > 1)
3034 mpctx->stop_play = PT_PREV_SRC;
3036 break;
3038 case MP_CMD_SUB_STEP:
3039 if (sh_video) {
3040 int movement = cmd->args[0].v.i;
3041 step_sub(mpctx->subdata, mpctx->video_pts, movement);
3042 #ifdef CONFIG_ASS
3043 if (mpctx->osd->ass_track)
3044 sub_delay +=
3045 ass_step_sub(mpctx->osd->ass_track,
3046 (mpctx->video_pts +
3047 sub_delay) * 1000 + .5, movement) / 1000.;
3048 #endif
3049 set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration,
3050 "Sub delay: %d ms", ROUND(sub_delay * 1000));
3052 break;
3054 case MP_CMD_SUB_LOG:
3055 log_sub(mpctx);
3056 break;
3058 case MP_CMD_OSD: {
3059 int v = cmd->args[0].v.i;
3060 int max = (opts->term_osd
3061 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
3062 if (opts->osd_level > max)
3063 opts->osd_level = max;
3064 if (v < 0)
3065 opts->osd_level = (opts->osd_level + 1) % (max + 1);
3066 else
3067 opts->osd_level = v > max ? max : v;
3068 /* Show OSD state when disabled, but not when an explicit
3069 argument is given to the OSD command, i.e. in slave mode. */
3070 if (v == -1 && opts->osd_level <= 1)
3071 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
3072 "OSD: %s",
3073 opts->osd_level ? mp_gtext("enabled") :
3074 mp_gtext("disabled"));
3075 else
3076 rm_osd_msg(OSD_MSG_OSD_STATUS);
3077 break;
3080 case MP_CMD_OSD_SHOW_TEXT:
3081 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3082 (cmd->args[1].v.i <
3083 0 ? osd_duration : cmd->args[1].v.i),
3084 "%s", cmd->args[0].v.s);
3085 break;
3087 case MP_CMD_OSD_SHOW_PROPERTY_TEXT: {
3088 char *txt = m_properties_expand_string(mp_properties,
3089 cmd->args[0].v.s,
3090 mpctx);
3091 // if no argument supplied use default osd_duration, else <arg> ms.
3092 if (txt) {
3093 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3094 (cmd->args[1].v.i <
3095 0 ? osd_duration : cmd->args[1].v.i),
3096 "%s", txt);
3097 free(txt);
3099 break;
3102 case MP_CMD_LOADFILE: {
3103 play_tree_t *e = play_tree_new();
3104 play_tree_add_file(e, cmd->args[0].v.s);
3106 if (cmd->args[1].v.i) // append
3107 play_tree_append_entry(mpctx->playtree->child, e);
3108 else {
3109 // Go back to the starting point.
3110 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3111 != PLAY_TREE_ITER_END)
3112 /* NOP */;
3113 play_tree_free_list(mpctx->playtree->child, 1);
3114 play_tree_set_child(mpctx->playtree, e);
3115 pt_iter_goto_head(mpctx->playtree_iter);
3116 mpctx->stop_play = PT_NEXT_SRC;
3118 break;
3121 case MP_CMD_LOADLIST: {
3122 play_tree_t *e = parse_playlist_file(mpctx->mconfig,
3123 bstr(cmd->args[0].v.s));
3124 if (!e)
3125 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
3126 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
3127 else {
3128 if (cmd->args[1].v.i) // append
3129 play_tree_append_entry(mpctx->playtree->child, e);
3130 else {
3131 // Go back to the starting point.
3132 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3133 != PLAY_TREE_ITER_END)
3134 /* NOP */;
3135 play_tree_free_list(mpctx->playtree->child, 1);
3136 play_tree_set_child(mpctx->playtree, e);
3137 pt_iter_goto_head(mpctx->playtree_iter);
3138 mpctx->stop_play = PT_NEXT_SRC;
3141 break;
3144 case MP_CMD_STOP:
3145 // Go back to the starting point.
3146 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
3147 PLAY_TREE_ITER_END)
3148 /* NOP */;
3149 mpctx->stop_play = PT_STOP;
3150 break;
3152 case MP_CMD_OSD_SHOW_PROGRESSION: {
3153 int len = get_time_length(mpctx);
3154 int pts = get_current_time(mpctx);
3155 set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
3156 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3157 "%c %02d:%02d:%02d / %02d:%02d:%02d",
3158 mpctx->osd_function, pts / 3600, (pts / 60) % 60, pts % 60,
3159 len / 3600, (len / 60) % 60, len % 60);
3160 break;
3163 #ifdef CONFIG_RADIO
3164 case MP_CMD_RADIO_STEP_CHANNEL:
3165 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3166 int v = cmd->args[0].v.i;
3167 if (v > 0)
3168 radio_step_channel(mpctx->demuxer->stream,
3169 RADIO_CHANNEL_HIGHER);
3170 else
3171 radio_step_channel(mpctx->demuxer->stream,
3172 RADIO_CHANNEL_LOWER);
3173 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3174 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3175 "Channel: %s",
3176 radio_get_channel_name(mpctx->demuxer->stream));
3179 break;
3181 case MP_CMD_RADIO_SET_CHANNEL:
3182 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3183 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
3184 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3185 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3186 "Channel: %s",
3187 radio_get_channel_name(mpctx->demuxer->stream));
3190 break;
3192 case MP_CMD_RADIO_SET_FREQ:
3193 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3194 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3195 break;
3197 case MP_CMD_RADIO_STEP_FREQ:
3198 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3199 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3200 break;
3201 #endif
3203 #ifdef CONFIG_TV
3204 case MP_CMD_TV_START_SCAN:
3205 if (mpctx->file_format == DEMUXER_TYPE_TV)
3206 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv), 1);
3207 break;
3208 case MP_CMD_TV_SET_FREQ:
3209 if (mpctx->file_format == DEMUXER_TYPE_TV)
3210 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3211 cmd->args[0].v.f * 16.0);
3212 #ifdef CONFIG_PVR
3213 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3214 pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f));
3215 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3216 pvr_get_current_channelname(mpctx->stream),
3217 pvr_get_current_stationname(mpctx->stream));
3219 #endif /* CONFIG_PVR */
3220 break;
3222 case MP_CMD_TV_STEP_FREQ:
3223 if (mpctx->file_format == DEMUXER_TYPE_TV)
3224 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3225 cmd->args[0].v.f * 16.0);
3226 #ifdef CONFIG_PVR
3227 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3228 pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f));
3229 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3230 pvr_get_current_channelname(mpctx->stream),
3231 pvr_get_current_frequency(mpctx->stream));
3233 #endif /* CONFIG_PVR */
3234 break;
3236 case MP_CMD_TV_SET_NORM:
3237 if (mpctx->file_format == DEMUXER_TYPE_TV)
3238 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3239 cmd->args[0].v.s);
3240 break;
3242 case MP_CMD_TV_STEP_CHANNEL:
3243 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3244 int v = cmd->args[0].v.i;
3245 if (v > 0) {
3246 tv_step_channel((tvi_handle_t *) (mpctx->
3247 demuxer->priv),
3248 TV_CHANNEL_HIGHER);
3249 } else {
3250 tv_step_channel((tvi_handle_t *) (mpctx->
3251 demuxer->priv),
3252 TV_CHANNEL_LOWER);
3254 if (tv_channel_list) {
3255 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3256 "Channel: %s", tv_channel_current->name);
3257 //vo_osd_changed(OSDTYPE_SUBTITLE);
3260 #ifdef CONFIG_PVR
3261 else if (mpctx->stream &&
3262 mpctx->stream->type == STREAMTYPE_PVR) {
3263 pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i);
3264 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3265 pvr_get_current_channelname(mpctx->stream),
3266 pvr_get_current_stationname(mpctx->stream));
3268 #endif /* CONFIG_PVR */
3269 #ifdef CONFIG_DVBIN
3270 if (mpctx->stream->type == STREAMTYPE_DVB) {
3271 int dir;
3272 int v = cmd->args[0].v.i;
3274 mpctx->last_dvb_step = v;
3275 if (v > 0)
3276 dir = DVB_CHANNEL_HIGHER;
3277 else
3278 dir = DVB_CHANNEL_LOWER;
3281 if (dvb_step_channel(mpctx->stream, dir)) {
3282 mpctx->stop_play = PT_NEXT_ENTRY;
3283 mpctx->dvbin_reopen = 1;
3286 #endif /* CONFIG_DVBIN */
3287 break;
3289 case MP_CMD_TV_SET_CHANNEL:
3290 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3291 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3292 cmd->args[0].v.s);
3293 if (tv_channel_list) {
3294 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3295 "Channel: %s", tv_channel_current->name);
3296 //vo_osd_changed(OSDTYPE_SUBTITLE);
3299 #ifdef CONFIG_PVR
3300 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3301 pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
3302 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3303 pvr_get_current_channelname(mpctx->stream),
3304 pvr_get_current_stationname(mpctx->stream));
3306 #endif /* CONFIG_PVR */
3307 break;
3309 #ifdef CONFIG_DVBIN
3310 case MP_CMD_DVB_SET_CHANNEL:
3311 if (mpctx->stream->type == STREAMTYPE_DVB) {
3312 mpctx->last_dvb_step = 1;
3314 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3315 cmd->args[0].v.i)) {
3316 mpctx->stop_play = PT_NEXT_ENTRY;
3317 mpctx->dvbin_reopen = 1;
3320 break;
3321 #endif /* CONFIG_DVBIN */
3323 case MP_CMD_TV_LAST_CHANNEL:
3324 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3325 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3326 if (tv_channel_list) {
3327 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3328 "Channel: %s", tv_channel_current->name);
3329 //vo_osd_changed(OSDTYPE_SUBTITLE);
3332 #ifdef CONFIG_PVR
3333 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3334 pvr_set_lastchannel(mpctx->stream);
3335 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3336 pvr_get_current_channelname(mpctx->stream),
3337 pvr_get_current_stationname(mpctx->stream));
3339 #endif /* CONFIG_PVR */
3340 break;
3342 case MP_CMD_TV_STEP_NORM:
3343 if (mpctx->file_format == DEMUXER_TYPE_TV)
3344 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3345 break;
3347 case MP_CMD_TV_STEP_CHANNEL_LIST:
3348 if (mpctx->file_format == DEMUXER_TYPE_TV)
3349 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3350 break;
3351 #endif /* CONFIG_TV */
3352 case MP_CMD_TV_TELETEXT_ADD_DEC:
3353 if (mpctx->demuxer->teletext)
3354 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_ADD_DEC,
3355 &(cmd->args[0].v.s));
3356 break;
3357 case MP_CMD_TV_TELETEXT_GO_LINK:
3358 if (mpctx->demuxer->teletext)
3359 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_GO_LINK,
3360 &(cmd->args[0].v.i));
3361 break;
3363 case MP_CMD_SUB_LOAD:
3364 if (sh_video) {
3365 int n = mpctx->set_of_sub_size;
3366 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3367 if (n != mpctx->set_of_sub_size) {
3368 mpctx->sub_counts[SUB_SOURCE_SUBS]++;
3369 ++mpctx->global_sub_size;
3372 break;
3374 case MP_CMD_SUB_REMOVE:
3375 if (sh_video) {
3376 int v = cmd->args[0].v.i;
3377 if (v < 0)
3378 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3379 else if (v < mpctx->set_of_sub_size)
3380 remove_subtitle_range(mpctx, v, 1);
3382 break;
3384 case MP_CMD_GET_SUB_VISIBILITY:
3385 if (sh_video) {
3386 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3387 "ANS_SUB_VISIBILITY=%d\n", sub_visibility);
3389 break;
3391 case MP_CMD_SCREENSHOT:
3392 if (mpctx->video_out && mpctx->video_out->config_ok) {
3393 mp_msg(MSGT_CPLAYER, MSGL_INFO, "sending VFCTRL_SCREENSHOT!\n");
3394 if (CONTROL_OK !=
3395 ((vf_instance_t *) sh_video->vfilter)->
3396 control(sh_video->vfilter, VFCTRL_SCREENSHOT,
3397 &cmd->args[0].v.i))
3398 mp_msg(MSGT_CPLAYER, MSGL_INFO,
3399 "failed (forgot -vf screenshot?)\n");
3401 break;
3403 case MP_CMD_VF_CHANGE_RECTANGLE:
3404 if (!sh_video)
3405 break;
3406 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3407 break;
3409 case MP_CMD_GET_TIME_LENGTH:
3410 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3411 get_time_length(mpctx));
3412 break;
3414 case MP_CMD_GET_FILENAME: {
3415 char *inf = get_metadata(mpctx, META_NAME);
3416 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n", inf);
3417 talloc_free(inf);
3418 break;
3421 case MP_CMD_GET_VIDEO_CODEC: {
3422 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3423 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3424 talloc_free(inf);
3425 break;
3428 case MP_CMD_GET_VIDEO_BITRATE: {
3429 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3430 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3431 talloc_free(inf);
3432 break;
3435 case MP_CMD_GET_VIDEO_RESOLUTION: {
3436 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3437 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3438 talloc_free(inf);
3439 break;
3442 case MP_CMD_GET_AUDIO_CODEC: {
3443 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3444 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3445 talloc_free(inf);
3446 break;
3449 case MP_CMD_GET_AUDIO_BITRATE: {
3450 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3451 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3452 talloc_free(inf);
3453 break;
3456 case MP_CMD_GET_AUDIO_SAMPLES: {
3457 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3458 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3459 talloc_free(inf);
3460 break;
3463 case MP_CMD_GET_META_TITLE: {
3464 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3465 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3466 talloc_free(inf);
3467 break;
3470 case MP_CMD_GET_META_ARTIST: {
3471 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3472 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3473 talloc_free(inf);
3474 break;
3477 case MP_CMD_GET_META_ALBUM: {
3478 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3479 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3480 talloc_free(inf);
3481 break;
3484 case MP_CMD_GET_META_YEAR: {
3485 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3486 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3487 talloc_free(inf);
3488 break;
3491 case MP_CMD_GET_META_COMMENT: {
3492 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3493 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3494 talloc_free(inf);
3495 break;
3498 case MP_CMD_GET_META_TRACK: {
3499 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3500 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3501 talloc_free(inf);
3502 break;
3505 case MP_CMD_GET_META_GENRE: {
3506 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3507 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3508 talloc_free(inf);
3509 break;
3512 case MP_CMD_GET_VO_FULLSCREEN:
3513 if (mpctx->video_out && mpctx->video_out->config_ok)
3514 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3515 break;
3517 case MP_CMD_GET_PERCENT_POS:
3518 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3519 get_percent_pos(mpctx));
3520 break;
3522 case MP_CMD_GET_TIME_POS: {
3523 float pos = get_current_time(mpctx);
3524 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3525 break;
3528 case MP_CMD_RUN:
3529 #ifndef __MINGW32__
3530 if (!fork()) {
3531 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3532 exit(0);
3534 #endif
3535 break;
3537 case MP_CMD_KEYDOWN_EVENTS:
3538 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3539 break;
3541 case MP_CMD_SET_MOUSE_POS: {
3542 int pointer_x, pointer_y;
3543 double dx, dy;
3544 pointer_x = cmd->args[0].v.i;
3545 pointer_y = cmd->args[1].v.i;
3546 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3547 #ifdef CONFIG_DVDNAV
3548 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3549 && dx > 0.0 && dy > 0.0) {
3550 int button = -1;
3551 pointer_x = (int) (dx * (double) sh_video->disp_w);
3552 pointer_y = (int) (dy * (double) sh_video->disp_h);
3553 mp_dvdnav_update_mouse_pos(mpctx->stream,
3554 pointer_x, pointer_y, &button);
3555 if (opts->osd_level > 1 && button > 0)
3556 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3557 "Selected button number %d", button);
3559 #endif
3560 #ifdef CONFIG_MENU
3561 if (use_menu && dx >= 0.0 && dy >= 0.0)
3562 menu_update_mouse_pos(dx, dy);
3563 #endif
3564 break;
3567 #ifdef CONFIG_DVDNAV
3568 case MP_CMD_DVDNAV: {
3569 int button = -1;
3570 int i;
3571 enum mp_command_type command = 0;
3572 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3573 break;
3575 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3576 if (cmd->args[0].v.s &&
3577 !strcasecmp(cmd->args[0].v.s,
3578 mp_dvdnav_bindings[i].name))
3579 command = mp_dvdnav_bindings[i].cmd;
3581 mp_dvdnav_handle_input(mpctx->stream, command, &button);
3582 if (opts->osd_level > 1 && button > 0)
3583 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3584 "Selected button number %d", button);
3585 break;
3588 case MP_CMD_SWITCH_TITLE:
3589 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3590 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3591 break;
3593 #endif
3595 case MP_CMD_AF_SWITCH:
3596 if (sh_audio) {
3597 af_uninit(mpctx->mixer.afilter);
3598 af_init(mpctx->mixer.afilter);
3600 case MP_CMD_AF_ADD:
3601 case MP_CMD_AF_DEL: {
3602 if (!sh_audio)
3603 break;
3604 char *af_args = strdup(cmd->args[0].v.s);
3605 char *af_commands = af_args;
3606 char *af_command;
3607 af_instance_t *af;
3608 while ((af_command = strsep(&af_commands, ",")) != NULL) {
3609 if (cmd->id == MP_CMD_AF_DEL) {
3610 af = af_get(mpctx->mixer.afilter, af_command);
3611 if (af != NULL)
3612 af_remove(mpctx->mixer.afilter, af);
3613 } else
3614 af_add(mpctx->mixer.afilter, af_command);
3616 reinit_audio_chain(mpctx);
3617 free(af_args);
3618 break;
3620 case MP_CMD_AF_CLR:
3621 if (!sh_audio)
3622 break;
3623 af_uninit(mpctx->mixer.afilter);
3624 af_init(mpctx->mixer.afilter);
3625 reinit_audio_chain(mpctx);
3626 break;
3627 case MP_CMD_AF_CMDLINE:
3628 if (sh_audio) {
3629 af_instance_t *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
3630 if (!af) {
3631 mp_msg(MSGT_CPLAYER, MSGL_WARN,
3632 "Filter '%s' not found in chain.\n", cmd->args[0].v.s);
3633 break;
3635 af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
3636 af_reinit(sh_audio->afilter, af);
3638 break;
3640 default:
3641 mp_msg(MSGT_CPLAYER, MSGL_V,
3642 "Received unknown cmd %s\n", cmd->name);
3645 old_pause_hack:
3646 switch (cmd->pausing) {
3647 case 1: // "pausing"
3648 pause_player(mpctx);
3649 break;
3650 case 3: // "pausing_toggle"
3651 if (mpctx->paused)
3652 unpause_player(mpctx);
3653 else
3654 pause_player(mpctx);
3655 break;