osd: always display pause icon when frame stepping
[mplayer.git] / command.c
blob58dd68454f9eea7571daad0db55e06be56d3fe1d
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 "screenshot.h"
71 #include "mp_core.h"
72 #include "mp_fifo.h"
73 #include "libavutil/avstring.h"
75 static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
76 double *dx, double *dy)
78 struct MPOpts *opts = &mpctx->opts;
79 struct vo *vo = mpctx->video_out;
80 //remove the borders, if any, and rescale to the range [0,1],[0,1]
81 if (vo_fs) { //we are in full-screen mode
82 if (opts->vo_screenwidth > vo->dwidth)
83 // there are borders along the x axis
84 ix -= (opts->vo_screenwidth - vo->dwidth) / 2;
85 if (opts->vo_screenheight > vo->dheight)
86 // there are borders along the y axis (usual way)
87 iy -= (opts->vo_screenheight - vo->dheight) / 2;
89 if (ix < 0 || ix > vo->dwidth) {
90 *dx = *dy = -1.0;
91 return;
92 } //we are on one of the borders
93 if (iy < 0 || iy > vo->dheight) {
94 *dx = *dy = -1.0;
95 return;
96 } //we are on one of the borders
99 *dx = (double) ix / (double) vo->dwidth;
100 *dy = (double) iy / (double) vo->dheight;
102 mp_msg(MSGT_CPLAYER, MSGL_V,
103 "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
104 *dx, *dy, opts->vo_screenwidth, opts->vo_screenheight, vo->dwidth,
105 vo->dheight, vo_fs);
108 static int sub_pos_by_source(MPContext *mpctx, int src)
110 int i, cnt = 0;
111 if (src >= SUB_SOURCES || mpctx->sub_counts[src] == 0)
112 return -1;
113 for (i = 0; i < src; i++)
114 cnt += mpctx->sub_counts[i];
115 return cnt;
118 static int sub_source_and_index_by_pos(MPContext *mpctx, int *pos)
120 int start = 0;
121 int i;
122 for (i = 0; i < SUB_SOURCES; i++) {
123 int cnt = mpctx->sub_counts[i];
124 if (*pos >= start && *pos < start + cnt) {
125 *pos -= start;
126 return i;
128 start += cnt;
130 *pos = -1;
131 return -1;
134 static int sub_source_by_pos(MPContext *mpctx, int pos)
136 return sub_source_and_index_by_pos(mpctx, &pos);
139 static int sub_source_pos(MPContext *mpctx)
141 int pos = mpctx->global_sub_pos;
142 sub_source_and_index_by_pos(mpctx, &pos);
143 return pos;
146 static int sub_source(MPContext *mpctx)
148 return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
151 static void update_global_sub_size(MPContext *mpctx)
153 struct MPOpts *opts = &mpctx->opts;
154 int i;
155 int cnt = 0;
157 // update number of demuxer sub streams
158 for (i = 0; i < MAX_S_STREAMS; i++)
159 if (mpctx->d_sub->demuxer->s_streams[i])
160 cnt++;
161 if (cnt > mpctx->sub_counts[SUB_SOURCE_DEMUX])
162 mpctx->sub_counts[SUB_SOURCE_DEMUX] = cnt;
164 // update global size
165 mpctx->global_sub_size = 0;
166 for (i = 0; i < SUB_SOURCES; i++)
167 mpctx->global_sub_size += mpctx->sub_counts[i];
169 // update global_sub_pos if we auto-detected a demuxer sub
170 if (mpctx->global_sub_pos == -1) {
171 int sub_id = -1;
172 if (mpctx->demuxer->sub)
173 sub_id = mpctx->demuxer->sub->id;
174 if (sub_id < 0)
175 sub_id = opts->sub_id;
176 if (sub_id >= 0 && sub_id < mpctx->sub_counts[SUB_SOURCE_DEMUX])
177 mpctx->global_sub_pos = sub_pos_by_source(mpctx, SUB_SOURCE_DEMUX) +
178 sub_id;
183 * \brief Log the currently displayed subtitle to a file
185 * Logs the current or last displayed subtitle together with filename
186 * and time information to ~/.mplayer/subtitle_log
188 * Intended purpose is to allow convenient marking of bogus subtitles
189 * which need to be fixed while watching the movie.
192 static void log_sub(struct MPContext *mpctx)
194 char *fname;
195 FILE *f;
196 int i;
197 struct subtitle *vo_sub_last = mpctx->vo_sub_last;
199 if (mpctx->subdata == NULL || vo_sub_last == NULL)
200 return;
201 fname = get_path("subtitle_log");
202 f = fopen(fname, "a");
203 if (!f)
204 return;
205 fprintf(f, "----------------------------------------------------------\n");
206 if (mpctx->subdata->sub_uses_time) {
207 fprintf(f,
208 "N: %s S: %02ld:%02ld:%02ld.%02ld E: %02ld:%02ld:%02ld.%02ld\n",
209 mpctx->filename, vo_sub_last->start / 360000,
210 (vo_sub_last->start / 6000) % 60,
211 (vo_sub_last->start / 100) % 60, vo_sub_last->start % 100,
212 vo_sub_last->end / 360000, (vo_sub_last->end / 6000) % 60,
213 (vo_sub_last->end / 100) % 60, vo_sub_last->end % 100);
214 } else {
215 fprintf(f, "N: %s S: %ld E: %ld\n", mpctx->filename,
216 vo_sub_last->start, vo_sub_last->end);
218 for (i = 0; i < vo_sub_last->lines; i++)
219 fprintf(f, "%s\n", vo_sub_last->text[i]);
220 fclose(f);
224 static int mp_property_generic_option(struct m_option *prop, int action,
225 void *arg, MPContext *mpctx)
227 char *optname = prop->priv;
228 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
229 bstr(optname));
230 void *valptr = m_option_get_ptr(opt, &mpctx->opts);
232 switch (action) {
233 case M_PROPERTY_GET_TYPE:
234 *(const struct m_option **)arg = opt;
235 return M_PROPERTY_OK;
236 case M_PROPERTY_GET:
237 m_option_copy(opt, arg, valptr);
238 return M_PROPERTY_OK;
239 case M_PROPERTY_SET:
240 m_option_copy(opt, valptr, arg);
241 return M_PROPERTY_OK;
242 case M_PROPERTY_STEP_UP:
243 if (opt->type == &m_option_type_choice) {
244 int v = *(int *) valptr;
245 int best = v;
246 struct m_opt_choice_alternatives *alt;
247 for (alt = opt->priv; alt->name; alt++)
248 if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1)
249 best = alt->value;
250 *(int *) valptr = best;
251 return M_PROPERTY_OK;
253 break;
255 return M_PROPERTY_NOT_IMPLEMENTED;
258 /// OSD level (RW)
259 static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
260 MPContext *mpctx)
262 return m_property_choice(prop, action, arg, &mpctx->opts.osd_level);
265 /// Loop (RW)
266 static int mp_property_loop(m_option_t *prop, int action, void *arg,
267 MPContext *mpctx)
269 struct MPOpts *opts = &mpctx->opts;
270 switch (action) {
271 case M_PROPERTY_PRINT:
272 if (!arg)
273 return M_PROPERTY_ERROR;
274 if (opts->loop_times < 0)
275 *(char **)arg = talloc_strdup(NULL, "off");
276 else if (opts->loop_times == 0)
277 *(char **)arg = talloc_strdup(NULL, "inf");
278 else
279 break;
280 return M_PROPERTY_OK;
282 return m_property_int_range(prop, action, arg, &opts->loop_times);
285 /// Playback speed (RW)
286 static int mp_property_playback_speed(m_option_t *prop, int action,
287 void *arg, MPContext *mpctx)
289 struct MPOpts *opts = &mpctx->opts;
290 double orig_speed = opts->playback_speed;
291 switch (action) {
292 case M_PROPERTY_SET:
293 if (!arg)
294 return M_PROPERTY_ERROR;
295 opts->playback_speed = *(float *) arg;
296 goto set;
297 case M_PROPERTY_STEP_UP:
298 case M_PROPERTY_STEP_DOWN:
299 opts->playback_speed += (arg ? *(float *) arg : 0.1) *
300 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
301 set:
302 M_PROPERTY_CLAMP(prop, opts->playback_speed);
303 // Adjust time until next frame flip for nosound mode
304 mpctx->time_frame *= orig_speed / 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);
534 if (chapter >= 0) {
535 if (next_pts > -1.0)
536 queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
537 chapter_name = chapter_display_name(mpctx, chapter);
538 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
539 "Chapter: %s", 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 int count = get_chapter_count(mpctx);
556 return m_property_int_ro(prop, action, arg, count);
559 /// Current dvd angle (RW)
560 static int mp_property_angle(m_option_t *prop, int action, void *arg,
561 MPContext *mpctx)
563 struct MPOpts *opts = &mpctx->opts;
564 int angle = -1;
565 int angles;
567 if (mpctx->demuxer)
568 angle = demuxer_get_current_angle(mpctx->demuxer);
569 if (angle < 0)
570 return M_PROPERTY_UNAVAILABLE;
571 angles = demuxer_angles_count(mpctx->demuxer);
572 if (angles <= 1)
573 return M_PROPERTY_UNAVAILABLE;
575 switch (action) {
576 case M_PROPERTY_GET:
577 if (!arg)
578 return M_PROPERTY_ERROR;
579 *(int *) arg = angle;
580 return M_PROPERTY_OK;
581 case M_PROPERTY_PRINT: {
582 if (!arg)
583 return M_PROPERTY_ERROR;
584 *(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
585 return M_PROPERTY_OK;
587 case M_PROPERTY_SET:
588 if (!arg)
589 return M_PROPERTY_ERROR;
590 angle = *(int *)arg;
591 M_PROPERTY_CLAMP(prop, angle);
592 break;
593 case M_PROPERTY_STEP_UP:
594 case M_PROPERTY_STEP_DOWN: {
595 int step = 0;
596 if (arg)
597 step = *(int *)arg;
598 if (!step)
599 step = 1;
600 step *= (action == M_PROPERTY_STEP_UP ? 1 : -1);
601 angle += step;
602 if (angle < 1) //cycle
603 angle = angles;
604 else if (angle > angles)
605 angle = 1;
606 break;
608 default:
609 return M_PROPERTY_NOT_IMPLEMENTED;
611 angle = demuxer_set_angle(mpctx->demuxer, angle);
612 if (angle >= 0) {
613 struct sh_video *sh_video = mpctx->demuxer->video->sh;
614 if (sh_video)
615 resync_video_stream(sh_video);
617 struct sh_audio *sh_audio = mpctx->demuxer->audio->sh;
618 if (sh_audio)
619 resync_audio_stream(sh_audio);
622 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
623 "Angle: %d/%d", angle, angles);
624 return M_PROPERTY_OK;
627 /// Demuxer meta data
628 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
629 MPContext *mpctx)
631 m_property_action_t *ka;
632 char *meta;
633 static const m_option_t key_type =
635 "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL
637 if (!mpctx->demuxer)
638 return M_PROPERTY_UNAVAILABLE;
640 switch (action) {
641 case M_PROPERTY_GET:
642 if (!arg)
643 return M_PROPERTY_ERROR;
644 *(char ***)arg = mpctx->demuxer->info;
645 return M_PROPERTY_OK;
646 case M_PROPERTY_KEY_ACTION:
647 if (!arg)
648 return M_PROPERTY_ERROR;
649 ka = arg;
650 if (!(meta = demux_info_get(mpctx->demuxer, ka->key)))
651 return M_PROPERTY_UNKNOWN;
652 switch (ka->action) {
653 case M_PROPERTY_GET:
654 if (!ka->arg)
655 return M_PROPERTY_ERROR;
656 *(char **)ka->arg = meta;
657 return M_PROPERTY_OK;
658 case M_PROPERTY_GET_TYPE:
659 if (!ka->arg)
660 return M_PROPERTY_ERROR;
661 *(const m_option_t **)ka->arg = &key_type;
662 return M_PROPERTY_OK;
665 return M_PROPERTY_NOT_IMPLEMENTED;
668 static int mp_property_pause(m_option_t *prop, int action, void *arg,
669 void *ctx)
671 MPContext *mpctx = ctx;
673 switch (action) {
674 case M_PROPERTY_SET:
675 if (!arg)
676 return M_PROPERTY_ERROR;
677 if (mpctx->paused == (bool) * (int *)arg)
678 return M_PROPERTY_OK;
679 case M_PROPERTY_STEP_UP:
680 case M_PROPERTY_STEP_DOWN:
681 if (mpctx->paused) {
682 unpause_player(mpctx);
683 } else {
684 pause_player(mpctx);
686 return M_PROPERTY_OK;
687 default:
688 return m_property_flag(prop, action, arg, &mpctx->paused);
693 /// Volume (RW)
694 static int mp_property_volume(m_option_t *prop, int action, void *arg,
695 MPContext *mpctx)
698 if (!mpctx->sh_audio)
699 return M_PROPERTY_UNAVAILABLE;
701 switch (action) {
702 case M_PROPERTY_GET:
703 if (!arg)
704 return M_PROPERTY_ERROR;
705 mixer_getbothvolume(&mpctx->mixer, arg);
706 return M_PROPERTY_OK;
707 case M_PROPERTY_PRINT: {
708 float vol;
709 if (!arg)
710 return M_PROPERTY_ERROR;
711 mixer_getbothvolume(&mpctx->mixer, &vol);
712 return m_property_float_range(prop, action, arg, &vol);
714 case M_PROPERTY_STEP_UP:
715 case M_PROPERTY_STEP_DOWN:
716 case M_PROPERTY_SET:
717 break;
718 default:
719 return M_PROPERTY_NOT_IMPLEMENTED;
722 if (mpctx->edl_muted)
723 return M_PROPERTY_DISABLED;
724 mpctx->user_muted = 0;
726 switch (action) {
727 case M_PROPERTY_SET:
728 if (!arg)
729 return M_PROPERTY_ERROR;
730 M_PROPERTY_CLAMP(prop, *(float *) arg);
731 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
732 return M_PROPERTY_OK;
733 case M_PROPERTY_STEP_UP:
734 if (arg && *(float *) arg <= 0)
735 mixer_decvolume(&mpctx->mixer);
736 else
737 mixer_incvolume(&mpctx->mixer);
738 return M_PROPERTY_OK;
739 case M_PROPERTY_STEP_DOWN:
740 if (arg && *(float *) arg <= 0)
741 mixer_incvolume(&mpctx->mixer);
742 else
743 mixer_decvolume(&mpctx->mixer);
744 return M_PROPERTY_OK;
746 return M_PROPERTY_NOT_IMPLEMENTED;
749 /// Mute (RW)
750 static int mp_property_mute(m_option_t *prop, int action, void *arg,
751 MPContext *mpctx)
754 if (!mpctx->sh_audio)
755 return M_PROPERTY_UNAVAILABLE;
757 switch (action) {
758 case M_PROPERTY_SET:
759 if (mpctx->edl_muted)
760 return M_PROPERTY_DISABLED;
761 if (!arg)
762 return M_PROPERTY_ERROR;
763 if ((!!*(int *) arg) != mpctx->mixer.muted)
764 mixer_mute(&mpctx->mixer);
765 mpctx->user_muted = mpctx->mixer.muted;
766 return M_PROPERTY_OK;
767 case M_PROPERTY_STEP_UP:
768 case M_PROPERTY_STEP_DOWN:
769 if (mpctx->edl_muted)
770 return M_PROPERTY_DISABLED;
771 mixer_mute(&mpctx->mixer);
772 mpctx->user_muted = mpctx->mixer.muted;
773 return M_PROPERTY_OK;
774 case M_PROPERTY_PRINT:
775 if (!arg)
776 return M_PROPERTY_ERROR;
777 if (mpctx->edl_muted) {
778 *(char **) arg = talloc_strdup(NULL, mp_gtext("enabled (EDL)"));
779 return M_PROPERTY_OK;
781 default:
782 return m_property_flag(prop, action, arg, &mpctx->mixer.muted);
787 /// Audio delay (RW)
788 static int mp_property_audio_delay(m_option_t *prop, int action,
789 void *arg, MPContext *mpctx)
791 if (!(mpctx->sh_audio && mpctx->sh_video))
792 return M_PROPERTY_UNAVAILABLE;
793 switch (action) {
794 case M_PROPERTY_SET:
795 case M_PROPERTY_STEP_UP:
796 case M_PROPERTY_STEP_DOWN: {
797 int ret;
798 float delay = audio_delay;
799 ret = m_property_delay(prop, action, arg, &audio_delay);
800 if (ret != M_PROPERTY_OK)
801 return ret;
802 if (mpctx->sh_audio)
803 mpctx->delay -= audio_delay - delay;
805 return M_PROPERTY_OK;
806 default:
807 return m_property_delay(prop, action, arg, &audio_delay);
811 /// Audio codec tag (RO)
812 static int mp_property_audio_format(m_option_t *prop, int action,
813 void *arg, MPContext *mpctx)
815 if (!mpctx->sh_audio)
816 return M_PROPERTY_UNAVAILABLE;
817 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
820 /// Audio codec name (RO)
821 static int mp_property_audio_codec(m_option_t *prop, int action,
822 void *arg, MPContext *mpctx)
824 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
825 return M_PROPERTY_UNAVAILABLE;
826 return m_property_string_ro(prop, action, arg,
827 mpctx->sh_audio->codec->name);
830 /// Audio bitrate (RO)
831 static int mp_property_audio_bitrate(m_option_t *prop, int action,
832 void *arg, MPContext *mpctx)
834 if (!mpctx->sh_audio)
835 return M_PROPERTY_UNAVAILABLE;
836 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
839 /// Samplerate (RO)
840 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
841 MPContext *mpctx)
843 if (!mpctx->sh_audio)
844 return M_PROPERTY_UNAVAILABLE;
845 switch (action) {
846 case M_PROPERTY_PRINT:
847 if (!arg)
848 return M_PROPERTY_ERROR;
849 *(char **)arg = talloc_asprintf(NULL, "%d kHz",
850 mpctx->sh_audio->samplerate / 1000);
851 return M_PROPERTY_OK;
853 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
856 /// Number of channels (RO)
857 static int mp_property_channels(m_option_t *prop, int action, void *arg,
858 MPContext *mpctx)
860 if (!mpctx->sh_audio)
861 return M_PROPERTY_UNAVAILABLE;
862 switch (action) {
863 case M_PROPERTY_PRINT:
864 if (!arg)
865 return M_PROPERTY_ERROR;
866 switch (mpctx->sh_audio->channels) {
867 case 1:
868 *(char **) arg = talloc_strdup(NULL, "mono");
869 break;
870 case 2:
871 *(char **) arg = talloc_strdup(NULL, "stereo");
872 break;
873 default:
874 *(char **) arg = talloc_asprintf(NULL, "%d channels",
875 mpctx->sh_audio->channels);
877 return M_PROPERTY_OK;
879 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
882 /// Balance (RW)
883 static int mp_property_balance(m_option_t *prop, int action, void *arg,
884 MPContext *mpctx)
886 float bal;
888 if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2)
889 return M_PROPERTY_UNAVAILABLE;
891 switch (action) {
892 case M_PROPERTY_GET:
893 if (!arg)
894 return M_PROPERTY_ERROR;
895 mixer_getbalance(&mpctx->mixer, arg);
896 return M_PROPERTY_OK;
897 case M_PROPERTY_PRINT: {
898 char **str = arg;
899 if (!arg)
900 return M_PROPERTY_ERROR;
901 mixer_getbalance(&mpctx->mixer, &bal);
902 if (bal == 0.f)
903 *str = talloc_strdup(NULL, "center");
904 else if (bal == -1.f)
905 *str = talloc_strdup(NULL, "left only");
906 else if (bal == 1.f)
907 *str = talloc_strdup(NULL, "right only");
908 else {
909 unsigned right = (bal + 1.f) / 2.f * 100.f;
910 *str = talloc_asprintf(NULL, "left %d%%, right %d%%",
911 100 - right, right);
913 return M_PROPERTY_OK;
915 case M_PROPERTY_STEP_UP:
916 case M_PROPERTY_STEP_DOWN:
917 mixer_getbalance(&mpctx->mixer, &bal);
918 bal += (arg ? *(float *)arg : .1f) *
919 (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
920 M_PROPERTY_CLAMP(prop, bal);
921 mixer_setbalance(&mpctx->mixer, bal);
922 return M_PROPERTY_OK;
923 case M_PROPERTY_SET:
924 if (!arg)
925 return M_PROPERTY_ERROR;
926 M_PROPERTY_CLAMP(prop, *(float *)arg);
927 mixer_setbalance(&mpctx->mixer, *(float *)arg);
928 return M_PROPERTY_OK;
930 return M_PROPERTY_NOT_IMPLEMENTED;
933 /// Selected audio id (RW)
934 static int mp_property_audio(m_option_t *prop, int action, void *arg,
935 MPContext *mpctx)
937 int current_id, tmp;
938 if (!mpctx->demuxer || !mpctx->d_audio)
939 return M_PROPERTY_UNAVAILABLE;
940 struct sh_audio *sh = mpctx->sh_audio;
941 current_id = sh ? sh->aid : -2;
943 switch (action) {
944 case M_PROPERTY_GET:
945 if (!arg)
946 return M_PROPERTY_ERROR;
947 *(int *) arg = current_id;
948 return M_PROPERTY_OK;
949 case M_PROPERTY_PRINT:
950 if (!arg)
951 return M_PROPERTY_ERROR;
953 if (current_id < 0)
954 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
955 else if (sh && (sh->lang || sh->title)) {
956 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
957 if (sh->title)
958 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
959 current_id, lang, sh->title);
960 else
961 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
962 lang);
963 } else {
964 char lang[40];
965 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
966 if (0) ;
967 #ifdef CONFIG_DVDREAD
968 else if (mpctx->stream->type == STREAMTYPE_DVD) {
969 int code = dvd_lang_from_aid(mpctx->stream, current_id);
970 if (code) {
971 lang[0] = code >> 8;
972 lang[1] = code;
973 lang[2] = 0;
976 #endif
978 #ifdef CONFIG_DVDNAV
979 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
980 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
981 #endif
982 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, lang);
984 return M_PROPERTY_OK;
986 case M_PROPERTY_STEP_UP:
987 case M_PROPERTY_SET:
988 if (action == M_PROPERTY_SET && arg)
989 tmp = *((int *) arg);
990 else
991 tmp = -1;
992 int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
993 if (new_id != current_id)
994 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
995 if (new_id != current_id && new_id >= 0) {
996 sh_audio_t *sh2;
997 sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
998 sh2->ds = mpctx->d_audio;
999 mpctx->sh_audio = sh2;
1000 reinit_audio_chain(mpctx);
1002 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
1003 return M_PROPERTY_OK;
1004 default:
1005 return M_PROPERTY_NOT_IMPLEMENTED;
1010 /// Selected video id (RW)
1011 static int mp_property_video(m_option_t *prop, int action, void *arg,
1012 MPContext *mpctx)
1014 struct MPOpts *opts = &mpctx->opts;
1015 int current_id, tmp;
1016 if (!mpctx->demuxer || !mpctx->d_video)
1017 return M_PROPERTY_UNAVAILABLE;
1018 current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
1020 switch (action) {
1021 case M_PROPERTY_GET:
1022 if (!arg)
1023 return M_PROPERTY_ERROR;
1024 *(int *) arg = current_id;
1025 return M_PROPERTY_OK;
1026 case M_PROPERTY_PRINT:
1027 if (!arg)
1028 return M_PROPERTY_ERROR;
1030 if (current_id < 0)
1031 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1032 else {
1033 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
1034 mp_gtext("unknown"));
1036 return M_PROPERTY_OK;
1038 case M_PROPERTY_STEP_UP:
1039 case M_PROPERTY_SET:
1040 if (action == M_PROPERTY_SET && arg)
1041 tmp = *((int *) arg);
1042 else
1043 tmp = -1;
1044 int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
1045 if (new_id != current_id)
1046 uninit_player(mpctx, INITIALIZED_VCODEC |
1047 (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
1048 if (new_id != current_id && new_id >= 0) {
1049 sh_video_t *sh2;
1050 sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
1051 sh2->ds = mpctx->d_video;
1052 mpctx->sh_video = sh2;
1053 reinit_video_chain(mpctx);
1055 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
1056 return M_PROPERTY_OK;
1058 default:
1059 return M_PROPERTY_NOT_IMPLEMENTED;
1063 static int mp_property_program(m_option_t *prop, int action, void *arg,
1064 MPContext *mpctx)
1066 demux_program_t prog;
1068 switch (action) {
1069 case M_PROPERTY_STEP_UP:
1070 case M_PROPERTY_SET:
1071 if (action == M_PROPERTY_SET && arg)
1072 prog.progid = *((int *) arg);
1073 else
1074 prog.progid = -1;
1075 if (demux_control(mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1076 &prog) == DEMUXER_CTRL_NOTIMPL)
1077 return M_PROPERTY_ERROR;
1079 if (prog.aid < 0 && prog.vid < 0) {
1080 mp_msg(MSGT_CPLAYER, MSGL_ERR,
1081 "Selected program contains no audio or video streams!\n");
1082 return M_PROPERTY_ERROR;
1084 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1085 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1086 return M_PROPERTY_OK;
1088 default:
1089 return M_PROPERTY_NOT_IMPLEMENTED;
1094 /// Fullscreen state (RW)
1095 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1096 MPContext *mpctx)
1099 if (!mpctx->video_out)
1100 return M_PROPERTY_UNAVAILABLE;
1102 switch (action) {
1103 case M_PROPERTY_SET:
1104 if (!arg)
1105 return M_PROPERTY_ERROR;
1106 M_PROPERTY_CLAMP(prop, *(int *) arg);
1107 if (vo_fs == !!*(int *) arg)
1108 return M_PROPERTY_OK;
1109 case M_PROPERTY_STEP_UP:
1110 case M_PROPERTY_STEP_DOWN:
1111 if (mpctx->video_out->config_ok)
1112 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1113 mpctx->opts.fullscreen = vo_fs;
1114 return M_PROPERTY_OK;
1115 default:
1116 return m_property_flag(prop, action, arg, &vo_fs);
1120 static int mp_property_deinterlace(m_option_t *prop, int action,
1121 void *arg, MPContext *mpctx)
1123 int deinterlace;
1124 vf_instance_t *vf;
1125 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1126 return M_PROPERTY_UNAVAILABLE;
1127 vf = mpctx->sh_video->vfilter;
1128 switch (action) {
1129 case M_PROPERTY_GET:
1130 if (!arg)
1131 return M_PROPERTY_ERROR;
1132 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1133 return M_PROPERTY_OK;
1134 case M_PROPERTY_SET:
1135 if (!arg)
1136 return M_PROPERTY_ERROR;
1137 M_PROPERTY_CLAMP(prop, *(int *) arg);
1138 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1139 return M_PROPERTY_OK;
1140 case M_PROPERTY_STEP_UP:
1141 case M_PROPERTY_STEP_DOWN:
1142 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1143 deinterlace = !deinterlace;
1144 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1145 return M_PROPERTY_OK;
1147 int value = 0;
1148 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1149 return m_property_flag_ro(prop, action, arg, value);
1152 static int colormatrix_property_helper(m_option_t *prop, int action,
1153 void *arg, MPContext *mpctx)
1155 int r = mp_property_generic_option(prop, action, arg, mpctx);
1156 // testing for an actual change is too much effort
1157 switch (action) {
1158 case M_PROPERTY_SET:
1159 case M_PROPERTY_STEP_UP:
1160 case M_PROPERTY_STEP_DOWN:
1161 if (mpctx->sh_video)
1162 set_video_colorspace(mpctx->sh_video);
1163 break;
1165 return r;
1168 static int mp_property_colormatrix(m_option_t *prop, int action, void *arg,
1169 MPContext *mpctx)
1171 struct MPOpts *opts = &mpctx->opts;
1172 switch (action) {
1173 case M_PROPERTY_PRINT:
1174 if (!arg)
1175 return M_PROPERTY_ERROR;
1176 struct mp_csp_details actual = { .format = -1 };
1177 char *req_csp = mp_csp_names[opts->requested_colorspace];
1178 char *real_csp = NULL;
1179 if (mpctx->sh_video) {
1180 struct vf_instance *vf = mpctx->sh_video->vfilter;
1181 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1182 real_csp = mp_csp_names[actual.format];
1183 } else {
1184 real_csp = "Unknown";
1187 char *res;
1188 if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) {
1189 // Caveat: doesn't handle the case when the autodetected colorspace
1190 // is different from the actual colorspace as used by the
1191 // VO - the OSD will display the VO colorspace without
1192 // indication that it doesn't match the requested colorspace.
1193 res = talloc_asprintf(NULL, "Auto (%s)", real_csp);
1194 } else if (opts->requested_colorspace == actual.format || !real_csp) {
1195 res = talloc_strdup(NULL, req_csp);
1196 } else
1197 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1198 req_csp, real_csp);
1199 *(char **)arg = res;
1200 return M_PROPERTY_OK;
1201 default:;
1202 return colormatrix_property_helper(prop, action, arg, mpctx);
1206 static int levels_property_helper(int offset, m_option_t *prop, int action,
1207 void *arg, MPContext *mpctx)
1209 char *optname = prop->priv;
1210 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
1211 bstr(optname));
1212 int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts);
1214 switch (action) {
1215 case M_PROPERTY_PRINT:
1216 if (!arg)
1217 return M_PROPERTY_ERROR;
1218 struct mp_csp_details actual = {0};
1219 int actual_level = -1;
1220 char *req_level = m_option_print(opt, valptr);
1221 char *real_level = NULL;
1222 if (mpctx->sh_video) {
1223 struct vf_instance *vf = mpctx->sh_video->vfilter;
1224 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1225 actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset);
1226 real_level = m_option_print(opt, &actual_level);
1227 } else {
1228 real_level = talloc_strdup(NULL, "Unknown");
1231 char *res;
1232 if (*valptr == MP_CSP_LEVELS_AUTO && real_level) {
1233 res = talloc_asprintf(NULL, "Auto (%s)", real_level);
1234 } else if (*valptr == actual_level || !real_level) {
1235 res = talloc_strdup(NULL, real_level);
1236 } else
1237 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1238 req_level, real_level);
1239 talloc_free(req_level);
1240 talloc_free(real_level);
1241 *(char **)arg = res;
1242 return M_PROPERTY_OK;
1243 default:;
1244 return colormatrix_property_helper(prop, action, arg, mpctx);
1248 static int mp_property_colormatrix_input_range(m_option_t *prop, int action,
1249 void *arg, MPContext *mpctx)
1251 return levels_property_helper(offsetof(struct mp_csp_details, levels_in),
1252 prop, action, arg, mpctx);
1255 static int mp_property_colormatrix_output_range(m_option_t *prop, int action,
1256 void *arg, MPContext *mpctx)
1258 return levels_property_helper(offsetof(struct mp_csp_details, levels_out),
1259 prop, action, arg, mpctx);
1262 static int mp_property_capture(m_option_t *prop, int action,
1263 void *arg, MPContext *mpctx)
1265 struct MPOpts *opts = &mpctx->opts;
1267 if (!mpctx->stream)
1268 return M_PROPERTY_UNAVAILABLE;
1270 if (!opts->capture_dump) {
1271 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1272 "Capturing not enabled (forgot -capture parameter?)\n");
1273 return M_PROPERTY_ERROR;
1276 int capturing = !!mpctx->stream->capture_file;
1278 int ret = m_property_flag(prop, action, arg, &capturing);
1279 if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1280 if (capturing) {
1281 mpctx->stream->capture_file = fopen(opts->stream_dump_name, "wb");
1282 if (!mpctx->stream->capture_file) {
1283 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1284 "Error opening capture file: %s\n", strerror(errno));
1285 ret = M_PROPERTY_ERROR;
1287 } else {
1288 fclose(mpctx->stream->capture_file);
1289 mpctx->stream->capture_file = NULL;
1293 return ret;
1296 /// Panscan (RW)
1297 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1298 MPContext *mpctx)
1301 if (!mpctx->video_out
1302 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1303 return M_PROPERTY_UNAVAILABLE;
1305 switch (action) {
1306 case M_PROPERTY_SET:
1307 if (!arg)
1308 return M_PROPERTY_ERROR;
1309 M_PROPERTY_CLAMP(prop, *(float *) arg);
1310 vo_panscan = *(float *) arg;
1311 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1312 return M_PROPERTY_OK;
1313 case M_PROPERTY_STEP_UP:
1314 case M_PROPERTY_STEP_DOWN:
1315 vo_panscan += (arg ? *(float *) arg : 0.1) *
1316 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1317 if (vo_panscan > 1)
1318 vo_panscan = 1;
1319 else if (vo_panscan < 0)
1320 vo_panscan = 0;
1321 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1322 return M_PROPERTY_OK;
1323 default:
1324 return m_property_float_range(prop, action, arg, &vo_panscan);
1328 /// Helper to set vo flags.
1329 /** \ingroup PropertyImplHelper
1331 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1332 int vo_ctrl, int *vo_var, MPContext *mpctx)
1335 if (!mpctx->video_out)
1336 return M_PROPERTY_UNAVAILABLE;
1338 switch (action) {
1339 case M_PROPERTY_SET:
1340 if (!arg)
1341 return M_PROPERTY_ERROR;
1342 M_PROPERTY_CLAMP(prop, *(int *) arg);
1343 if (*vo_var == !!*(int *) arg)
1344 return M_PROPERTY_OK;
1345 case M_PROPERTY_STEP_UP:
1346 case M_PROPERTY_STEP_DOWN:
1347 if (mpctx->video_out->config_ok)
1348 vo_control(mpctx->video_out, vo_ctrl, 0);
1349 return M_PROPERTY_OK;
1350 default:
1351 return m_property_flag(prop, action, arg, vo_var);
1355 /// Window always on top (RW)
1356 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1357 MPContext *mpctx)
1359 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1360 &mpctx->opts.vo_ontop, mpctx);
1363 /// Display in the root window (RW)
1364 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1365 MPContext *mpctx)
1367 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1368 &vo_rootwin, mpctx);
1371 /// Show window borders (RW)
1372 static int mp_property_border(m_option_t *prop, int action, void *arg,
1373 MPContext *mpctx)
1375 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1376 &vo_border, mpctx);
1379 /// Framedropping state (RW)
1380 static int mp_property_framedropping(m_option_t *prop, int action,
1381 void *arg, MPContext *mpctx)
1384 if (!mpctx->sh_video)
1385 return M_PROPERTY_UNAVAILABLE;
1387 switch (action) {
1388 case M_PROPERTY_PRINT:
1389 if (!arg)
1390 return M_PROPERTY_ERROR;
1391 *(char **) arg = talloc_strdup(NULL, frame_dropping == 1 ?
1392 mp_gtext("enabled") :
1393 (frame_dropping == 2 ? mp_gtext("hard") :
1394 mp_gtext("disabled")));
1395 return M_PROPERTY_OK;
1396 default:
1397 return m_property_choice(prop, action, arg, &frame_dropping);
1401 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1402 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1403 MPContext *mpctx)
1405 int *gamma = (int *)((char *)&mpctx->opts + prop->offset);
1406 int r, val;
1408 if (!mpctx->sh_video)
1409 return M_PROPERTY_UNAVAILABLE;
1411 if (gamma[0] == 1000) {
1412 gamma[0] = 0;
1413 get_video_colors(mpctx->sh_video, prop->name, gamma);
1416 switch (action) {
1417 case M_PROPERTY_SET:
1418 if (!arg)
1419 return M_PROPERTY_ERROR;
1420 M_PROPERTY_CLAMP(prop, *(int *) arg);
1421 *gamma = *(int *) arg;
1422 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1423 if (r <= 0)
1424 break;
1425 return r;
1426 case M_PROPERTY_GET:
1427 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1428 if (!arg)
1429 return M_PROPERTY_ERROR;
1430 *(int *)arg = val;
1431 return M_PROPERTY_OK;
1433 break;
1434 case M_PROPERTY_STEP_UP:
1435 case M_PROPERTY_STEP_DOWN:
1436 *gamma += (arg ? *(int *) arg : 1) *
1437 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1438 M_PROPERTY_CLAMP(prop, *gamma);
1439 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1440 if (r <= 0)
1441 break;
1442 return r;
1443 default:
1444 return M_PROPERTY_NOT_IMPLEMENTED;
1447 #ifdef CONFIG_TV
1448 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1449 int l = strlen(prop->name);
1450 char tv_prop[3 + l + 1];
1451 sprintf(tv_prop, "tv_%s", prop->name);
1452 return mp_property_do(tv_prop, action, arg, mpctx);
1454 #endif
1456 return M_PROPERTY_UNAVAILABLE;
1459 /// VSync (RW)
1460 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1461 MPContext *mpctx)
1463 return m_property_flag(prop, action, arg, &vo_vsync);
1466 /// Video codec tag (RO)
1467 static int mp_property_video_format(m_option_t *prop, int action,
1468 void *arg, MPContext *mpctx)
1470 char *meta;
1471 if (!mpctx->sh_video)
1472 return M_PROPERTY_UNAVAILABLE;
1473 switch (action) {
1474 case M_PROPERTY_PRINT:
1475 if (!arg)
1476 return M_PROPERTY_ERROR;
1477 switch (mpctx->sh_video->format) {
1478 case 0x10000001:
1479 meta = talloc_strdup(NULL, "mpeg1");
1480 break;
1481 case 0x10000002:
1482 meta = talloc_strdup(NULL, "mpeg2");
1483 break;
1484 case 0x10000004:
1485 meta = talloc_strdup(NULL, "mpeg4");
1486 break;
1487 case 0x10000005:
1488 meta = talloc_strdup(NULL, "h264");
1489 break;
1490 default:
1491 if (mpctx->sh_video->format >= 0x20202020) {
1492 meta = talloc_asprintf(NULL, "%.4s",
1493 (char *) &mpctx->sh_video->format);
1494 } else
1495 meta = talloc_asprintf(NULL, "0x%08X", mpctx->sh_video->format);
1497 *(char **)arg = meta;
1498 return M_PROPERTY_OK;
1500 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1503 /// Video codec name (RO)
1504 static int mp_property_video_codec(m_option_t *prop, int action,
1505 void *arg, MPContext *mpctx)
1507 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1508 return M_PROPERTY_UNAVAILABLE;
1509 return m_property_string_ro(prop, action, arg,
1510 mpctx->sh_video->codec->name);
1514 /// Video bitrate (RO)
1515 static int mp_property_video_bitrate(m_option_t *prop, int action,
1516 void *arg, MPContext *mpctx)
1518 if (!mpctx->sh_video)
1519 return M_PROPERTY_UNAVAILABLE;
1520 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1523 /// Video display width (RO)
1524 static int mp_property_width(m_option_t *prop, int action, void *arg,
1525 MPContext *mpctx)
1527 if (!mpctx->sh_video)
1528 return M_PROPERTY_UNAVAILABLE;
1529 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1532 /// Video display height (RO)
1533 static int mp_property_height(m_option_t *prop, int action, void *arg,
1534 MPContext *mpctx)
1536 if (!mpctx->sh_video)
1537 return M_PROPERTY_UNAVAILABLE;
1538 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1541 /// Video fps (RO)
1542 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1543 MPContext *mpctx)
1545 if (!mpctx->sh_video)
1546 return M_PROPERTY_UNAVAILABLE;
1547 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1550 /// Video aspect (RO)
1551 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1552 MPContext *mpctx)
1554 if (!mpctx->sh_video)
1555 return M_PROPERTY_UNAVAILABLE;
1556 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1560 /// Text subtitle position (RW)
1561 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1562 MPContext *mpctx)
1564 switch (action) {
1565 case M_PROPERTY_SET:
1566 if (!arg)
1567 return M_PROPERTY_ERROR;
1568 case M_PROPERTY_STEP_UP:
1569 case M_PROPERTY_STEP_DOWN:
1570 vo_osd_changed(OSDTYPE_SUBTITLE);
1571 default:
1572 return m_property_int_range(prop, action, arg, &sub_pos);
1576 /// Selected subtitles (RW)
1577 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1578 MPContext *mpctx)
1580 struct MPOpts *opts = &mpctx->opts;
1581 demux_stream_t *const d_sub = mpctx->d_sub;
1582 int source = -1, reset_spu = 0;
1583 int source_pos = -1;
1585 update_global_sub_size(mpctx);
1586 const int global_sub_size = mpctx->global_sub_size;
1588 if (global_sub_size <= 0)
1589 return M_PROPERTY_UNAVAILABLE;
1591 switch (action) {
1592 case M_PROPERTY_GET:
1593 if (!arg)
1594 return M_PROPERTY_ERROR;
1595 *(int *) arg = mpctx->global_sub_pos;
1596 return M_PROPERTY_OK;
1597 case M_PROPERTY_PRINT:
1598 if (!arg)
1599 return M_PROPERTY_ERROR;
1600 char *sub_name = NULL;
1601 if (mpctx->subdata)
1602 sub_name = mpctx->subdata->filename;
1603 #ifdef CONFIG_ASS
1604 if (mpctx->osd->ass_track)
1605 sub_name = mpctx->osd->ass_track->name;
1606 #endif
1607 if (!sub_name && mpctx->subdata)
1608 sub_name = mpctx->subdata->filename;
1609 if (sub_name) {
1610 const char *tmp = mp_basename(sub_name);
1612 *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
1613 mpctx->set_of_sub_pos + 1,
1614 strlen(tmp) < 20 ? "" : "...",
1615 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1616 return M_PROPERTY_OK;
1618 #ifdef CONFIG_DVDNAV
1619 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1620 if (vo_spudec && opts->sub_id >= 0) {
1621 unsigned char lang[3];
1622 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id,
1623 lang)) {
1624 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1625 opts->sub_id, lang);
1626 return M_PROPERTY_OK;
1630 #endif
1632 if ((d_sub->demuxer->type == DEMUXER_TYPE_MATROSKA
1633 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF
1634 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1635 || d_sub->demuxer->type == DEMUXER_TYPE_OGG)
1636 && d_sub->sh && opts->sub_id >= 0) {
1637 struct sh_sub *sh = d_sub->sh;
1638 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
1639 if (sh->title)
1640 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
1641 opts->sub_id, lang, sh->title);
1642 else
1643 *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
1644 opts->sub_id, lang);
1645 return M_PROPERTY_OK;
1648 if (vo_vobsub && vobsub_id >= 0) {
1649 const char *language = mp_gtext("unknown");
1650 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1651 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1652 vobsub_id, language ? language : mp_gtext("unknown"));
1653 return M_PROPERTY_OK;
1655 #ifdef CONFIG_DVDREAD
1656 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1657 && opts->sub_id >= 0) {
1658 char lang[3];
1659 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1660 lang[0] = code >> 8;
1661 lang[1] = code;
1662 lang[2] = 0;
1663 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1664 opts->sub_id, lang);
1665 return M_PROPERTY_OK;
1667 #endif
1668 if (opts->sub_id >= 0) {
1669 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
1670 mp_gtext("unknown"));
1671 return M_PROPERTY_OK;
1673 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1674 return M_PROPERTY_OK;
1676 case M_PROPERTY_SET:
1677 if (!arg)
1678 return M_PROPERTY_ERROR;
1679 if (*(int *) arg < -1)
1680 *(int *) arg = -1;
1681 else if (*(int *) arg >= global_sub_size)
1682 *(int *) arg = global_sub_size - 1;
1683 mpctx->global_sub_pos = *(int *) arg;
1684 break;
1685 case M_PROPERTY_STEP_UP:
1686 mpctx->global_sub_pos += 2;
1687 mpctx->global_sub_pos =
1688 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1689 break;
1690 case M_PROPERTY_STEP_DOWN:
1691 mpctx->global_sub_pos += global_sub_size + 1;
1692 mpctx->global_sub_pos =
1693 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1694 break;
1695 default:
1696 return M_PROPERTY_NOT_IMPLEMENTED;
1699 if (mpctx->global_sub_pos >= 0) {
1700 source = sub_source(mpctx);
1701 source_pos = sub_source_pos(mpctx);
1704 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1705 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1706 global_sub_size,
1707 mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1708 mpctx->sub_counts[SUB_SOURCE_SUBS],
1709 mpctx->sub_counts[SUB_SOURCE_DEMUX],
1710 mpctx->global_sub_pos, source);
1712 mpctx->set_of_sub_pos = -1;
1713 mpctx->subdata = NULL;
1715 vobsub_id = -1;
1716 opts->sub_id = -1;
1717 if (d_sub) {
1718 if (d_sub->id > -2)
1719 reset_spu = 1;
1720 d_sub->id = -2;
1722 mpctx->osd->ass_track = NULL;
1723 uninit_player(mpctx, INITIALIZED_SUB);
1725 if (source == SUB_SOURCE_VOBSUB)
1726 vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1727 else if (source == SUB_SOURCE_SUBS) {
1728 mpctx->set_of_sub_pos = source_pos;
1729 #ifdef CONFIG_ASS
1730 if (opts->ass_enabled
1731 && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
1732 mpctx->osd->ass_track =
1733 mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
1734 mpctx->osd->ass_track_changed = true;
1735 mpctx->osd->vsfilter_aspect =
1736 mpctx->track_was_native_ass[mpctx->set_of_sub_pos];
1737 } else
1738 #endif
1740 mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1741 vo_osd_changed(OSDTYPE_SUBTITLE);
1743 } else if (source == SUB_SOURCE_DEMUX) {
1744 opts->sub_id = source_pos;
1745 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1746 int i = 0;
1747 // default: assume 1:1 mapping of sid and stream id
1748 d_sub->id = opts->sub_id;
1749 d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
1750 ds_free_packs(d_sub);
1751 for (i = 0; i < MAX_S_STREAMS; i++) {
1752 sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
1753 if (sh && sh->sid == opts->sub_id) {
1754 d_sub->id = i;
1755 d_sub->sh = sh;
1756 break;
1759 if (d_sub->sh && d_sub->id >= 0) {
1760 sh_sub_t *sh = d_sub->sh;
1761 if (sh->type == 'v')
1762 init_vo_spudec(mpctx);
1763 else {
1764 sub_init(sh, mpctx->osd);
1765 mpctx->initialized_flags |= INITIALIZED_SUB;
1767 } else {
1768 d_sub->id = -2;
1769 d_sub->sh = NULL;
1773 #ifdef CONFIG_DVDREAD
1774 if (vo_spudec
1775 && (mpctx->stream->type == STREAMTYPE_DVD
1776 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1777 && opts->sub_id < 0 && reset_spu) {
1778 d_sub->id = -2;
1779 d_sub->sh = NULL;
1781 #endif
1783 update_subtitles(mpctx, 0, 0, true);
1785 return M_PROPERTY_OK;
1788 /// Selected sub source (RW)
1789 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1790 MPContext *mpctx)
1792 int source;
1793 update_global_sub_size(mpctx);
1794 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1795 return M_PROPERTY_UNAVAILABLE;
1797 switch (action) {
1798 case M_PROPERTY_GET:
1799 if (!arg)
1800 return M_PROPERTY_ERROR;
1801 *(int *) arg = sub_source(mpctx);
1802 return M_PROPERTY_OK;
1803 case M_PROPERTY_PRINT:
1804 if (!arg)
1805 return M_PROPERTY_ERROR;
1806 char *sourcename;
1807 switch (sub_source(mpctx)) {
1808 case SUB_SOURCE_SUBS:
1809 sourcename = mp_gtext("file");
1810 break;
1811 case SUB_SOURCE_VOBSUB:
1812 sourcename = mp_gtext("vobsub");
1813 break;
1814 case SUB_SOURCE_DEMUX:
1815 sourcename = mp_gtext("embedded");
1816 break;
1817 default:
1818 sourcename = mp_gtext("disabled");
1820 *(char **)arg = talloc_strdup(NULL, sourcename);
1821 return M_PROPERTY_OK;
1822 case M_PROPERTY_SET:
1823 if (!arg)
1824 return M_PROPERTY_ERROR;
1825 M_PROPERTY_CLAMP(prop, *(int *)arg);
1826 if (*(int *) arg < 0)
1827 mpctx->global_sub_pos = -1;
1828 else if (*(int *) arg != sub_source(mpctx)) {
1829 int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1830 if (new_pos == -1)
1831 return M_PROPERTY_UNAVAILABLE;
1832 mpctx->global_sub_pos = new_pos;
1834 break;
1835 case M_PROPERTY_STEP_UP:
1836 case M_PROPERTY_STEP_DOWN: {
1837 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1838 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1839 int step = (step_all > 0) ? 1 : -1;
1840 int cur_source = sub_source(mpctx);
1841 source = cur_source;
1842 while (step_all) {
1843 source += step;
1844 if (source >= SUB_SOURCES)
1845 source = -1;
1846 else if (source < -1)
1847 source = SUB_SOURCES - 1;
1848 if (source == cur_source || source == -1 ||
1849 mpctx->sub_counts[source])
1850 step_all -= step;
1852 if (source == cur_source)
1853 return M_PROPERTY_OK;
1854 if (source == -1)
1855 mpctx->global_sub_pos = -1;
1856 else
1857 mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1858 break;
1860 default:
1861 return M_PROPERTY_NOT_IMPLEMENTED;
1863 --mpctx->global_sub_pos;
1864 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1867 /// Selected subtitles from specific source (RW)
1868 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1869 MPContext *mpctx)
1871 int source, is_cur_source, offset, new_pos;
1872 update_global_sub_size(mpctx);
1873 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1874 return M_PROPERTY_UNAVAILABLE;
1876 if (!strcmp(prop->name, "sub_file"))
1877 source = SUB_SOURCE_SUBS;
1878 else if (!strcmp(prop->name, "sub_vob"))
1879 source = SUB_SOURCE_VOBSUB;
1880 else if (!strcmp(prop->name, "sub_demux"))
1881 source = SUB_SOURCE_DEMUX;
1882 else
1883 return M_PROPERTY_ERROR;
1885 offset = sub_pos_by_source(mpctx, source);
1886 if (offset < 0)
1887 return M_PROPERTY_UNAVAILABLE;
1889 is_cur_source = sub_source(mpctx) == source;
1890 new_pos = mpctx->global_sub_pos;
1891 switch (action) {
1892 case M_PROPERTY_GET:
1893 if (!arg)
1894 return M_PROPERTY_ERROR;
1895 if (is_cur_source) {
1896 *(int *) arg = sub_source_pos(mpctx);
1897 if (source == SUB_SOURCE_VOBSUB)
1898 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1899 } else
1900 *(int *) arg = -1;
1901 return M_PROPERTY_OK;
1902 case M_PROPERTY_PRINT:
1903 if (!arg)
1904 return M_PROPERTY_ERROR;
1905 if (is_cur_source)
1906 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1907 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1908 return M_PROPERTY_OK;
1909 case M_PROPERTY_SET:
1910 if (!arg)
1911 return M_PROPERTY_ERROR;
1912 if (*(int *) arg >= 0) {
1913 int index = *(int *)arg;
1914 if (source == SUB_SOURCE_VOBSUB)
1915 index = vobsub_get_index_by_id(vo_vobsub, index);
1916 new_pos = offset + index;
1917 if (index < 0 || index > mpctx->sub_counts[source]) {
1918 new_pos = -1;
1919 *(int *) arg = -1;
1921 } else
1922 new_pos = -1;
1923 break;
1924 case M_PROPERTY_STEP_UP:
1925 case M_PROPERTY_STEP_DOWN: {
1926 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1927 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1928 int step = (step_all > 0) ? 1 : -1;
1929 int max_sub_pos_for_source = -1;
1930 if (!is_cur_source)
1931 new_pos = -1;
1932 while (step_all) {
1933 if (new_pos == -1) {
1934 if (step > 0)
1935 new_pos = offset;
1936 else if (max_sub_pos_for_source == -1) {
1937 // Find max pos for specific source
1938 new_pos = mpctx->global_sub_size - 1;
1939 while (new_pos >= 0 && sub_source(mpctx) != source)
1940 new_pos--;
1941 } else
1942 new_pos = max_sub_pos_for_source;
1943 } else {
1944 new_pos += step;
1945 if (new_pos < offset ||
1946 new_pos >= mpctx->global_sub_size ||
1947 sub_source(mpctx) != source)
1948 new_pos = -1;
1950 step_all -= step;
1952 break;
1954 default:
1955 return M_PROPERTY_NOT_IMPLEMENTED;
1957 return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1960 /// Subtitle delay (RW)
1961 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1962 MPContext *mpctx)
1964 if (!mpctx->sh_video)
1965 return M_PROPERTY_UNAVAILABLE;
1966 return m_property_delay(prop, action, arg, &sub_delay);
1969 /// Alignment of text subtitles (RW)
1970 static int mp_property_sub_alignment(m_option_t *prop, int action,
1971 void *arg, MPContext *mpctx)
1973 char *name[] = {
1974 _("top"), _("center"), _("bottom")
1977 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1978 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1979 return M_PROPERTY_UNAVAILABLE;
1981 switch (action) {
1982 case M_PROPERTY_PRINT:
1983 if (!arg)
1984 return M_PROPERTY_ERROR;
1985 M_PROPERTY_CLAMP(prop, sub_alignment);
1986 *(char **) arg = talloc_strdup(NULL, mp_gtext(name[sub_alignment]));
1987 return M_PROPERTY_OK;
1988 case M_PROPERTY_SET:
1989 if (!arg)
1990 return M_PROPERTY_ERROR;
1991 case M_PROPERTY_STEP_UP:
1992 case M_PROPERTY_STEP_DOWN:
1993 vo_osd_changed(OSDTYPE_SUBTITLE);
1994 default:
1995 return m_property_choice(prop, action, arg, &sub_alignment);
1999 /// Subtitle visibility (RW)
2000 static int mp_property_sub_visibility(m_option_t *prop, int action,
2001 void *arg, MPContext *mpctx)
2003 if (!mpctx->sh_video)
2004 return M_PROPERTY_UNAVAILABLE;
2006 switch (action) {
2007 case M_PROPERTY_SET:
2008 if (!arg)
2009 return M_PROPERTY_ERROR;
2010 case M_PROPERTY_STEP_UP:
2011 case M_PROPERTY_STEP_DOWN:
2012 vo_osd_changed(OSDTYPE_SUBTITLE);
2013 if (vo_spudec)
2014 vo_osd_changed(OSDTYPE_SPU);
2015 default:
2016 return m_property_flag(prop, action, arg, &sub_visibility);
2020 #ifdef CONFIG_ASS
2021 /// Use margins for libass subtitles (RW)
2022 static int mp_property_ass_use_margins(m_option_t *prop, int action,
2023 void *arg, MPContext *mpctx)
2025 struct MPOpts *opts = &mpctx->opts;
2026 if (!mpctx->sh_video)
2027 return M_PROPERTY_UNAVAILABLE;
2029 switch (action) {
2030 case M_PROPERTY_SET:
2031 if (!arg)
2032 return M_PROPERTY_ERROR;
2033 case M_PROPERTY_STEP_UP:
2034 case M_PROPERTY_STEP_DOWN:
2035 mpctx->osd->ass_force_reload = true;
2036 default:
2037 return m_property_flag(prop, action, arg, &opts->ass_use_margins);
2041 static int mp_property_ass_vsfilter_aspect_compat(m_option_t *prop, int action,
2042 void *arg, MPContext *mpctx)
2044 if (!mpctx->sh_video)
2045 return M_PROPERTY_UNAVAILABLE;
2047 switch (action) {
2048 case M_PROPERTY_SET:
2049 if (!arg)
2050 return M_PROPERTY_ERROR;
2051 case M_PROPERTY_STEP_UP:
2052 case M_PROPERTY_STEP_DOWN:
2053 //has to re-render subs with new aspect ratio
2054 mpctx->osd->ass_force_reload = 1;
2055 default:
2056 return m_property_flag(prop, action, arg,
2057 &mpctx->opts.ass_vsfilter_aspect_compat);
2061 #endif
2063 /// Show only forced subtitles (RW)
2064 static int mp_property_sub_forced_only(m_option_t *prop, int action,
2065 void *arg, MPContext *mpctx)
2067 if (!vo_spudec)
2068 return M_PROPERTY_UNAVAILABLE;
2070 switch (action) {
2071 case M_PROPERTY_SET:
2072 if (!arg)
2073 return M_PROPERTY_ERROR;
2074 case M_PROPERTY_STEP_UP:
2075 case M_PROPERTY_STEP_DOWN:
2076 m_property_flag(prop, action, arg, &forced_subs_only);
2077 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
2078 return M_PROPERTY_OK;
2079 default:
2080 return m_property_flag(prop, action, arg, &forced_subs_only);
2085 #ifdef CONFIG_FREETYPE
2086 /// Subtitle scale (RW)
2087 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
2088 MPContext *mpctx)
2090 struct MPOpts *opts = &mpctx->opts;
2092 switch (action) {
2093 case M_PROPERTY_SET:
2094 if (!arg)
2095 return M_PROPERTY_ERROR;
2096 M_PROPERTY_CLAMP(prop, *(float *) arg);
2097 #ifdef CONFIG_ASS
2098 if (opts->ass_enabled) {
2099 opts->ass_font_scale = *(float *) arg;
2100 mpctx->osd->ass_force_reload = true;
2102 #endif
2103 text_font_scale_factor = *(float *) arg;
2104 force_load_font = 1;
2105 vo_osd_changed(OSDTYPE_SUBTITLE);
2106 return M_PROPERTY_OK;
2107 case M_PROPERTY_STEP_UP:
2108 case M_PROPERTY_STEP_DOWN:
2109 #ifdef CONFIG_ASS
2110 if (opts->ass_enabled) {
2111 opts->ass_font_scale += (arg ? *(float *) arg : 0.1) *
2112 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2113 M_PROPERTY_CLAMP(prop, opts->ass_font_scale);
2114 mpctx->osd->ass_force_reload = true;
2116 #endif
2117 text_font_scale_factor += (arg ? *(float *) arg : 0.1) *
2118 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2119 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
2120 force_load_font = 1;
2121 vo_osd_changed(OSDTYPE_SUBTITLE);
2122 return M_PROPERTY_OK;
2123 default:
2124 #ifdef CONFIG_ASS
2125 if (opts->ass_enabled)
2126 return m_property_float_ro(prop, action, arg, opts->ass_font_scale);
2127 else
2128 #endif
2129 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
2132 #endif
2135 #ifdef CONFIG_TV
2137 /// TV color settings (RW)
2138 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
2139 MPContext *mpctx)
2141 int r, val;
2142 tvi_handle_t *tvh = mpctx->demuxer->priv;
2143 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
2144 return M_PROPERTY_UNAVAILABLE;
2146 switch (action) {
2147 case M_PROPERTY_SET:
2148 if (!arg)
2149 return M_PROPERTY_ERROR;
2150 M_PROPERTY_CLAMP(prop, *(int *) arg);
2151 return tv_set_color_options(tvh, prop->offset, *(int *) arg);
2152 case M_PROPERTY_GET:
2153 return tv_get_color_options(tvh, prop->offset, arg);
2154 case M_PROPERTY_STEP_UP:
2155 case M_PROPERTY_STEP_DOWN:
2156 if ((r = tv_get_color_options(tvh, prop->offset, &val)) >= 0) {
2157 if (!r)
2158 return M_PROPERTY_ERROR;
2159 val += (arg ? *(int *) arg : 1) *
2160 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2161 M_PROPERTY_CLAMP(prop, val);
2162 return tv_set_color_options(tvh, prop->offset, val);
2164 return M_PROPERTY_ERROR;
2166 return M_PROPERTY_NOT_IMPLEMENTED;
2169 #endif
2171 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
2172 MPContext *mpctx)
2174 int val, result;
2175 int base_ioctl = prop->offset;
2177 for teletext's GET,SET,STEP ioctls this is not 0
2178 SET is GET+1
2179 STEP is GET+2
2181 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2182 return M_PROPERTY_UNAVAILABLE;
2183 if (!base_ioctl)
2184 return M_PROPERTY_ERROR;
2186 switch (action) {
2187 case M_PROPERTY_GET:
2188 if (!arg)
2189 return M_PROPERTY_ERROR;
2190 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2191 break;
2192 case M_PROPERTY_SET:
2193 if (!arg)
2194 return M_PROPERTY_ERROR;
2195 M_PROPERTY_CLAMP(prop, *(int *) arg);
2196 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2197 arg);
2198 break;
2199 case M_PROPERTY_STEP_UP:
2200 case M_PROPERTY_STEP_DOWN:
2201 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2202 val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2203 -1 : 1);
2204 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2205 &val);
2206 break;
2207 default:
2208 return M_PROPERTY_NOT_IMPLEMENTED;
2211 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2214 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2215 MPContext *mpctx)
2217 int result;
2218 int val;
2220 //with tvh==NULL will fail too
2221 result = mp_property_teletext_common(prop, action, arg, mpctx);
2222 if (result != M_PROPERTY_OK)
2223 return result;
2225 if (teletext_control(mpctx->demuxer->teletext,
2226 prop->offset, &val) == VBI_CONTROL_TRUE && val)
2227 mp_input_set_section(mpctx->input, "teletext");
2228 else
2229 mp_input_set_section(mpctx->input, "tv");
2230 return M_PROPERTY_OK;
2233 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2234 MPContext *mpctx)
2236 int result;
2237 int val;
2238 if (!mpctx->demuxer->teletext)
2239 return M_PROPERTY_UNAVAILABLE;
2240 switch (action) {
2241 case M_PROPERTY_STEP_UP:
2242 case M_PROPERTY_STEP_DOWN:
2243 //This should be handled separately
2244 val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2245 -1 : 1);
2246 result = teletext_control(mpctx->demuxer->teletext,
2247 TV_VBI_CONTROL_STEP_PAGE, &val);
2248 break;
2249 default:
2250 result = mp_property_teletext_common(prop, action, arg, mpctx);
2252 return result;
2256 /// All properties available in MPlayer.
2257 /** \ingroup Properties
2259 static const m_option_t mp_properties[] = {
2260 // General
2261 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2262 M_OPT_RANGE, 0, 3, NULL },
2263 { "loop", mp_property_loop, CONF_TYPE_INT,
2264 M_OPT_MIN, -1, 0, NULL },
2265 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2266 M_OPT_RANGE, 0.01, 100.0, NULL },
2267 { "filename", mp_property_filename, CONF_TYPE_STRING,
2268 0, 0, 0, NULL },
2269 { "path", mp_property_path, CONF_TYPE_STRING,
2270 0, 0, 0, NULL },
2271 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2272 0, 0, 0, NULL },
2273 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2274 M_OPT_MIN, 0, 0, NULL },
2275 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2276 M_OPT_MIN, 0, 0, NULL },
2277 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2278 M_OPT_MIN, 0, 0, NULL },
2279 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2280 M_OPT_MIN, 0, 0, NULL },
2281 { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2282 M_OPT_MIN, 0, 0, NULL },
2283 { "length", mp_property_length, CONF_TYPE_TIME,
2284 M_OPT_MIN, 0, 0, NULL },
2285 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2286 M_OPT_RANGE, 0, 100, NULL },
2287 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2288 M_OPT_MIN, 0, 0, NULL },
2289 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2290 M_OPT_MIN, 0, 0, NULL },
2291 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2292 0, 0, 0, NULL },
2293 { "angle", mp_property_angle, CONF_TYPE_INT,
2294 CONF_RANGE, -2, 10, NULL },
2295 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2296 0, 0, 0, NULL },
2297 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2298 M_OPT_RANGE, 0, 1, NULL },
2299 { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2300 M_OPT_RANGE, 0, 1, NULL },
2301 { "pts_association_mode", mp_property_generic_option, &m_option_type_choice,
2302 0, 0, 0, "pts-association-mode" },
2303 { "hr_seek", mp_property_generic_option, &m_option_type_choice,
2304 0, 0, 0, "hr-seek" },
2306 // Audio
2307 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2308 M_OPT_RANGE, 0, 100, NULL },
2309 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2310 M_OPT_RANGE, 0, 1, NULL },
2311 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2312 M_OPT_RANGE, -100, 100, NULL },
2313 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2314 0, 0, 0, NULL },
2315 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2316 0, 0, 0, NULL },
2317 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2318 0, 0, 0, NULL },
2319 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2320 0, 0, 0, NULL },
2321 { "channels", mp_property_channels, CONF_TYPE_INT,
2322 0, 0, 0, NULL },
2323 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2324 CONF_RANGE, -2, 65535, NULL },
2325 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2326 M_OPT_RANGE, -1, 1, NULL },
2328 // Video
2329 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2330 M_OPT_RANGE, 0, 1, NULL },
2331 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2332 M_OPT_RANGE, 0, 1, NULL },
2333 { "colormatrix", mp_property_colormatrix, &m_option_type_choice,
2334 0, 0, 0, "colormatrix" },
2335 { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice,
2336 0, 0, 0, "colormatrix-input-range" },
2337 { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice,
2338 0, 0, 0, "colormatrix-output-range" },
2339 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2340 M_OPT_RANGE, 0, 1, NULL },
2341 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2342 M_OPT_RANGE, 0, 1, NULL },
2343 { "border", mp_property_border, CONF_TYPE_FLAG,
2344 M_OPT_RANGE, 0, 1, NULL },
2345 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2346 M_OPT_RANGE, 0, 2, NULL },
2347 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2348 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_gamma)},
2349 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2350 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_brightness) },
2351 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2352 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_contrast) },
2353 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2354 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_saturation) },
2355 { "hue", mp_property_gamma, CONF_TYPE_INT,
2356 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_hue) },
2357 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2358 M_OPT_RANGE, 0, 1, NULL },
2359 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2360 M_OPT_RANGE, 0, 1, NULL },
2361 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2362 0, 0, 0, NULL },
2363 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2364 0, 0, 0, NULL },
2365 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2366 0, 0, 0, NULL },
2367 { "width", mp_property_width, CONF_TYPE_INT,
2368 0, 0, 0, NULL },
2369 { "height", mp_property_height, CONF_TYPE_INT,
2370 0, 0, 0, NULL },
2371 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2372 0, 0, 0, NULL },
2373 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2374 0, 0, 0, NULL },
2375 { "switch_video", mp_property_video, CONF_TYPE_INT,
2376 CONF_RANGE, -2, 65535, NULL },
2377 { "switch_program", mp_property_program, CONF_TYPE_INT,
2378 CONF_RANGE, -1, 65535, NULL },
2380 // Subs
2381 { "sub", mp_property_sub, CONF_TYPE_INT,
2382 M_OPT_MIN, -1, 0, NULL },
2383 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2384 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2385 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2386 M_OPT_MIN, -1, 0, NULL },
2387 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2388 M_OPT_MIN, -1, 0, NULL },
2389 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2390 M_OPT_MIN, -1, 0, NULL },
2391 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2392 0, 0, 0, NULL },
2393 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2394 M_OPT_RANGE, 0, 100, NULL },
2395 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2396 M_OPT_RANGE, 0, 2, NULL },
2397 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2398 M_OPT_RANGE, 0, 1, NULL },
2399 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2400 M_OPT_RANGE, 0, 1, NULL },
2401 #ifdef CONFIG_FREETYPE
2402 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2403 M_OPT_RANGE, 0, 100, NULL },
2404 #endif
2405 #ifdef CONFIG_ASS
2406 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2407 M_OPT_RANGE, 0, 1, NULL },
2408 { "ass_vsfilter_aspect_compat", mp_property_ass_vsfilter_aspect_compat,
2409 CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL },
2410 #endif
2412 #ifdef CONFIG_TV
2413 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2414 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS },
2415 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2416 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST },
2417 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2418 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION },
2419 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2420 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
2421 #endif
2422 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2423 M_OPT_RANGE, 100, 899, .offset = TV_VBI_CONTROL_GET_PAGE },
2424 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2425 M_OPT_RANGE, 0, 64, .offset = TV_VBI_CONTROL_GET_SUBPAGE },
2426 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2427 M_OPT_RANGE, 0, 1, .offset = TV_VBI_CONTROL_GET_MODE },
2428 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2429 M_OPT_RANGE, 0, 3, .offset = TV_VBI_CONTROL_GET_FORMAT },
2430 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2431 M_OPT_RANGE, 0, 2, .offset = TV_VBI_CONTROL_GET_HALF_PAGE },
2432 { NULL, NULL, NULL, 0, 0, 0, NULL }
2436 int mp_property_do(const char *name, int action, void *val, void *ctx)
2438 return m_property_do(mp_properties, name, action, val, ctx);
2441 char *mp_property_print(const char *name, void *ctx)
2443 char *ret = NULL;
2444 if (mp_property_do(name, M_PROPERTY_PRINT, &ret, ctx) <= 0)
2445 return NULL;
2446 return ret;
2449 char *property_expand_string(MPContext *mpctx, char *str)
2451 return m_properties_expand_string(mp_properties, str, mpctx);
2454 void property_print_help(void)
2456 m_properties_print_help_list(mp_properties);
2460 /* List of default ways to show a property on OSD.
2462 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2463 * a bar showing the current position between min/max values of the
2464 * property. In this case osd_msg is only used for terminal output
2465 * if there is no video; it'll be a label shown together with percentage.
2467 * Otherwise setting osd_msg will show the string on OSD, formatted with
2468 * the text value of the property as argument.
2470 static struct property_osd_display {
2471 /// property name
2472 const char *name;
2473 /// progressbar type
2474 int osd_progbar; // -1 is special value for seek indicators
2475 /// osd msg id if it must be shared
2476 int osd_id;
2477 /// osd msg template
2478 const char *osd_msg;
2479 } property_osd_display[] = {
2480 // general
2481 { "loop", 0, -1, _("Loop: %s") },
2482 { "chapter", -1, -1, NULL },
2483 { "capturing", 0, -1, _("Capturing: %s") },
2484 { "pts_association_mode", 0, -1, "PTS association mode: %s" },
2485 { "hr_seek", 0, -1, "hr-seek: %s" },
2486 { "speed", 0, -1, _("Speed: x %6s") },
2487 // audio
2488 { "volume", OSD_VOLUME, -1, _("Volume") },
2489 { "mute", 0, -1, _("Mute: %s") },
2490 { "audio_delay", 0, -1, _("A-V delay: %s") },
2491 { "switch_audio", 0, -1, _("Audio: %s") },
2492 { "balance", OSD_BALANCE, -1, _("Balance") },
2493 // video
2494 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2495 { "ontop", 0, -1, _("Stay on top: %s") },
2496 { "rootwin", 0, -1, _("Rootwin: %s") },
2497 { "border", 0, -1, _("Border: %s") },
2498 { "framedropping", 0, -1, _("Framedropping: %s") },
2499 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2500 { "colormatrix", 0, -1, _("YUV colormatrix: %s") },
2501 { "colormatrix_input_range", 0, -1, _("YUV input range: %s") },
2502 { "colormatrix_output_range", 0, -1, _("RGB output range: %s") },
2503 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2504 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2505 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2506 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2507 { "hue", OSD_HUE, -1, _("Hue") },
2508 { "vsync", 0, -1, _("VSync: %s") },
2509 // subs
2510 { "sub", 0, -1, _("Subtitles: %s") },
2511 { "sub_source", 0, -1, _("Sub source: %s") },
2512 { "sub_vob", 0, -1, _("Subtitles: %s") },
2513 { "sub_demux", 0, -1, _("Subtitles: %s") },
2514 { "sub_file", 0, -1, _("Subtitles: %s") },
2515 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2516 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2517 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2518 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2519 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2520 #ifdef CONFIG_FREETYPE
2521 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2522 #endif
2523 { "ass_vsfilter_aspect_compat", 0, -1,
2524 _("Subtitle VSFilter aspect compat: %s")},
2525 #ifdef CONFIG_TV
2526 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2527 { "tv_hue", OSD_HUE, -1, _("Hue") },
2528 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2529 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2530 #endif
2534 static int show_property_osd(MPContext *mpctx, const char *pname)
2536 struct MPOpts *opts = &mpctx->opts;
2537 int r;
2538 m_option_t *prop;
2539 struct property_osd_display *p;
2541 // look for the command
2542 for (p = property_osd_display; p->name; p++)
2543 if (!strcmp(p->name, pname))
2544 break;
2546 if (!p->name)
2547 return -1;
2549 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2550 return -1;
2552 if (p->osd_progbar == -1)
2553 mpctx->add_osd_seek_info = true;
2554 else if (p->osd_progbar) {
2555 if (prop->type == CONF_TYPE_INT) {
2556 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2557 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2558 prop->min, prop->max, r);
2559 } else if (prop->type == CONF_TYPE_FLOAT) {
2560 float f;
2561 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2562 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2563 prop->min, prop->max, f);
2564 } else {
2565 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2566 "Property use an unsupported type.\n");
2567 return -1;
2569 return 0;
2572 if (p->osd_msg) {
2573 char *val = mp_property_print(pname, mpctx);
2574 if (val) {
2575 int index = p - property_osd_display;
2576 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2577 1, opts->osd_duration, p->osd_msg, val);
2578 talloc_free(val);
2581 return 0;
2586 * Command to property bridge
2588 * It is used to handle most commands that just set a property
2589 * and optionally display something on the OSD.
2590 * Two kinds of commands are handled: adjust or toggle.
2592 * Adjust commands take 1 or 2 parameters: <value> <abs>
2593 * If <abs> is non-zero the property is set to the given value
2594 * otherwise it is adjusted.
2596 * Toggle commands take 0 or 1 parameters. With no parameter
2597 * or a value less than the property minimum it just steps the
2598 * property to its next or previous value respectively.
2599 * Otherwise it sets it to the given value.
2602 /// List of the commands that can be handled by setting a property.
2603 static struct {
2604 /// property name
2605 const char *name;
2606 /// cmd id
2607 int cmd;
2608 /// set/adjust or toggle command
2609 int toggle;
2610 } set_prop_cmd[] = {
2611 // general
2612 { "loop", MP_CMD_LOOP, 0},
2613 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2614 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2615 { "pause", MP_CMD_PAUSE, 0},
2616 { "capturing", MP_CMD_CAPTURING, 1},
2617 // audio
2618 { "volume", MP_CMD_VOLUME, 0},
2619 { "mute", MP_CMD_MUTE, 1},
2620 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2621 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2622 { "balance", MP_CMD_BALANCE, 0},
2623 // video
2624 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2625 { "panscan", MP_CMD_PANSCAN, 0},
2626 { "ontop", MP_CMD_VO_ONTOP, 1},
2627 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2628 { "border", MP_CMD_VO_BORDER, 1},
2629 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2630 { "gamma", MP_CMD_GAMMA, 0},
2631 { "brightness", MP_CMD_BRIGHTNESS, 0},
2632 { "contrast", MP_CMD_CONTRAST, 0},
2633 { "saturation", MP_CMD_SATURATION, 0},
2634 { "hue", MP_CMD_HUE, 0},
2635 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2636 // subs
2637 { "sub", MP_CMD_SUB_SELECT, 1},
2638 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2639 { "sub_vob", MP_CMD_SUB_VOB, 1},
2640 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2641 { "sub_file", MP_CMD_SUB_FILE, 1},
2642 { "sub_pos", MP_CMD_SUB_POS, 0},
2643 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2644 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2645 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2646 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2647 #ifdef CONFIG_FREETYPE
2648 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2649 #endif
2650 #ifdef CONFIG_ASS
2651 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2652 #endif
2653 #ifdef CONFIG_TV
2654 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2655 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2656 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2657 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2658 #endif
2662 /// Handle commands that set a property.
2663 static bool set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2665 int i, r;
2666 m_option_t *prop;
2667 const char *pname;
2669 // look for the command
2670 for (i = 0; set_prop_cmd[i].name; i++)
2671 if (set_prop_cmd[i].cmd == cmd->id)
2672 break;
2673 if (!(pname = set_prop_cmd[i].name))
2674 return 0;
2676 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2677 return 0;
2679 // toggle command
2680 if (set_prop_cmd[i].toggle) {
2681 // set to value
2682 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2683 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2684 else if (cmd->nargs > 0)
2685 r = mp_property_do(pname, M_PROPERTY_STEP_DOWN, NULL, mpctx);
2686 else
2687 r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2688 } else if (cmd->args[1].v.i) //set
2689 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2690 else // adjust
2691 r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2693 if (r <= 0)
2694 return 1;
2696 show_property_osd(mpctx, pname);
2698 return 1;
2701 #ifdef CONFIG_DVDNAV
2702 static const struct {
2703 const char *name;
2704 const enum mp_command_type cmd;
2705 } mp_dvdnav_bindings[] = {
2706 { "up", MP_CMD_DVDNAV_UP },
2707 { "down", MP_CMD_DVDNAV_DOWN },
2708 { "left", MP_CMD_DVDNAV_LEFT },
2709 { "right", MP_CMD_DVDNAV_RIGHT },
2710 { "menu", MP_CMD_DVDNAV_MENU },
2711 { "select", MP_CMD_DVDNAV_SELECT },
2712 { "prev", MP_CMD_DVDNAV_PREVMENU },
2713 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2716 * keep old dvdnav sub-command options for a while in order not to
2717 * break slave-mode API too suddenly.
2719 { "1", MP_CMD_DVDNAV_UP },
2720 { "2", MP_CMD_DVDNAV_DOWN },
2721 { "3", MP_CMD_DVDNAV_LEFT },
2722 { "4", MP_CMD_DVDNAV_RIGHT },
2723 { "5", MP_CMD_DVDNAV_MENU },
2724 { "6", MP_CMD_DVDNAV_SELECT },
2725 { "7", MP_CMD_DVDNAV_PREVMENU },
2726 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2727 { NULL, 0 }
2729 #endif
2731 static const char *property_error_string(int error_value)
2733 switch (error_value) {
2734 case M_PROPERTY_ERROR:
2735 return "ERROR";
2736 case M_PROPERTY_UNAVAILABLE:
2737 return "PROPERTY_UNAVAILABLE";
2738 case M_PROPERTY_NOT_IMPLEMENTED:
2739 return "NOT_IMPLEMENTED";
2740 case M_PROPERTY_UNKNOWN:
2741 return "PROPERTY_UNKNOWN";
2742 case M_PROPERTY_DISABLED:
2743 return "DISABLED";
2745 return "UNKNOWN";
2748 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2750 int idx;
2751 int end = start + count;
2752 int after = mpctx->set_of_sub_size - end;
2753 sub_data **subs = mpctx->set_of_subtitles;
2754 #ifdef CONFIG_ASS
2755 struct ass_track **ass_tracks = mpctx->set_of_ass_tracks;
2756 #endif
2757 if (count < 0 || count > mpctx->set_of_sub_size ||
2758 start < 0 || start > mpctx->set_of_sub_size - count) {
2759 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2760 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2761 return;
2763 for (idx = start; idx < end; idx++) {
2764 sub_data *subd = subs[idx];
2765 char *filename = "";
2766 if (subd)
2767 filename = subd->filename;
2768 #ifdef CONFIG_ASS
2769 if (!subd)
2770 filename = ass_tracks[idx]->name;
2771 #endif
2772 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2773 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2774 filename_recode(filename));
2775 sub_free(subd);
2776 subs[idx] = NULL;
2777 #ifdef CONFIG_ASS
2778 if (ass_tracks[idx])
2779 ass_free_track(ass_tracks[idx]);
2780 ass_tracks[idx] = NULL;
2781 #endif
2784 mpctx->global_sub_size -= count;
2785 mpctx->set_of_sub_size -= count;
2786 if (mpctx->set_of_sub_size <= 0)
2787 mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2789 memmove(subs + start, subs + end, after * sizeof(*subs));
2790 memset(subs + start + after, 0, count * sizeof(*subs));
2791 #ifdef CONFIG_ASS
2792 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2793 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2794 #endif
2796 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2797 mpctx->global_sub_pos = -2;
2798 mpctx->subdata = NULL;
2799 mpctx->osd->ass_track = NULL;
2800 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2801 } else if (mpctx->set_of_sub_pos >= end) {
2802 mpctx->set_of_sub_pos -= count;
2803 mpctx->global_sub_pos -= count;
2807 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2809 struct MPOpts *opts = &mpctx->opts;
2810 sh_audio_t *const sh_audio = mpctx->sh_audio;
2811 sh_video_t *const sh_video = mpctx->sh_video;
2812 int osd_duration = opts->osd_duration;
2813 int case_fallthrough_hack = 0;
2814 if (set_property_command(mpctx, cmd))
2815 goto old_pause_hack; // was handled already
2816 switch (cmd->id) {
2817 case MP_CMD_SEEK: {
2818 mpctx->add_osd_seek_info = true;
2819 float v = cmd->args[0].v.f;
2820 int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2821 int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0;
2822 if (abs == 2) { // Absolute seek to a timestamp in seconds
2823 queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact);
2824 mpctx->osd_function = v > get_current_time(mpctx) ?
2825 OSD_FFW : OSD_REW;
2826 } else if (abs) { /* Absolute seek by percentage */
2827 queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact);
2828 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2829 } else {
2830 queue_seek(mpctx, MPSEEK_RELATIVE, v, exact);
2831 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2833 break;
2836 case MP_CMD_SET_PROPERTY_OSD:
2837 case_fallthrough_hack = 1;
2839 case MP_CMD_SET_PROPERTY: {
2840 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2841 cmd->args[1].v.s, mpctx);
2842 if (r == M_PROPERTY_UNKNOWN)
2843 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2844 "Unknown property: '%s'\n", cmd->args[0].v.s);
2845 else if (r <= 0)
2846 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2847 "Failed to set property '%s' to '%s'.\n",
2848 cmd->args[0].v.s, cmd->args[1].v.s);
2849 else if (case_fallthrough_hack)
2850 show_property_osd(mpctx, cmd->args[0].v.s);
2851 if (r <= 0)
2852 mp_msg(MSGT_GLOBAL, MSGL_INFO,
2853 "ANS_ERROR=%s\n", property_error_string(r));
2854 break;
2857 case MP_CMD_STEP_PROPERTY_OSD:
2858 case_fallthrough_hack = 1;
2860 case MP_CMD_STEP_PROPERTY: {
2861 void *arg = NULL;
2862 int r, i;
2863 double d;
2864 off_t o;
2865 if (cmd->args[1].v.f) {
2866 m_option_t *prop;
2867 if ((r = mp_property_do(cmd->args[0].v.s,
2868 M_PROPERTY_GET_TYPE,
2869 &prop, mpctx)) <= 0)
2870 goto step_prop_err;
2871 if (prop->type == CONF_TYPE_INT ||
2872 prop->type == CONF_TYPE_FLAG)
2873 i = cmd->args[1].v.f, arg = &i;
2874 else if (prop->type == CONF_TYPE_FLOAT)
2875 arg = &cmd->args[1].v.f;
2876 else if (prop->type == CONF_TYPE_DOUBLE ||
2877 prop->type == CONF_TYPE_TIME)
2878 d = cmd->args[1].v.f, arg = &d;
2879 else if (prop->type == CONF_TYPE_POSITION)
2880 o = cmd->args[1].v.f, arg = &o;
2881 else
2882 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2883 "Ignoring step size stepping property '%s'.\n",
2884 cmd->args[0].v.s);
2886 r = mp_property_do(cmd->args[0].v.s,
2887 cmd->args[2].v.i < 0 ?
2888 M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2889 arg, mpctx);
2890 step_prop_err:
2891 if (r == M_PROPERTY_UNKNOWN)
2892 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2893 "Unknown property: '%s'\n", cmd->args[0].v.s);
2894 else if (r <= 0)
2895 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2896 "Failed to increment property '%s' by %f.\n",
2897 cmd->args[0].v.s, cmd->args[1].v.f);
2898 else if (case_fallthrough_hack)
2899 show_property_osd(mpctx, cmd->args[0].v.s);
2900 if (r <= 0)
2901 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2902 property_error_string(r));
2903 break;
2906 case MP_CMD_GET_PROPERTY: {
2907 char *tmp;
2908 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2909 &tmp, mpctx);
2910 if (r <= 0) {
2911 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2912 "Failed to get value of property '%s'.\n",
2913 cmd->args[0].v.s);
2914 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2915 property_error_string(r));
2916 break;
2918 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2919 cmd->args[0].v.s, tmp);
2920 talloc_free(tmp);
2921 break;
2924 case MP_CMD_EDL_MARK:
2925 if (edl_fd) {
2926 float v = get_current_time(mpctx);
2927 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2928 mpctx->begin_skip = v;
2929 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2930 "EDL skip start, press 'i' again to end block.\n");
2931 } else {
2932 if (mpctx->begin_skip > v)
2933 mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
2934 "EDL skip canceled, last start > stop\n");
2935 else {
2936 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2937 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2938 "EDL skip end, line written.\n");
2940 mpctx->begin_skip = MP_NOPTS_VALUE;
2943 break;
2945 case MP_CMD_SWITCH_RATIO:
2946 if (!sh_video)
2947 break;
2948 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2949 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2950 else
2951 opts->movie_aspect = cmd->args[0].v.f;
2952 video_reset_aspect(sh_video);
2953 break;
2955 case MP_CMD_SPEED_INCR: {
2956 float v = cmd->args[0].v.f;
2957 mp_property_do("speed", M_PROPERTY_STEP_UP, &v, mpctx);
2958 show_property_osd(mpctx, "speed");
2959 break;
2962 case MP_CMD_SPEED_MULT:
2963 case_fallthrough_hack = true;
2965 case MP_CMD_SPEED_SET: {
2966 float v = cmd->args[0].v.f;
2967 if (case_fallthrough_hack)
2968 v *= mpctx->opts.playback_speed;
2969 mp_property_do("speed", M_PROPERTY_SET, &v, mpctx);
2970 show_property_osd(mpctx, "speed");
2971 break;
2974 case MP_CMD_FRAME_STEP:
2975 add_step_frame(mpctx);
2976 break;
2978 case MP_CMD_QUIT:
2979 exit_player_with_rc(mpctx, EXIT_QUIT,
2980 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2982 case MP_CMD_PLAY_TREE_STEP: {
2983 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2984 int force = cmd->args[1].v.i;
2987 if (!force && mpctx->playtree_iter) {
2988 play_tree_iter_t *i =
2989 play_tree_iter_new_copy(mpctx->playtree_iter);
2990 if (play_tree_iter_step(i, n, 0) ==
2991 PLAY_TREE_ITER_ENTRY)
2992 mpctx->stop_play =
2993 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2994 play_tree_iter_free(i);
2995 } else
2996 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2997 if (mpctx->stop_play)
2998 mpctx->play_tree_step = n;
3000 break;
3003 case MP_CMD_PLAY_TREE_UP_STEP: {
3004 int n = cmd->args[0].v.i > 0 ? 1 : -1;
3005 int force = cmd->args[1].v.i;
3007 if (!force && mpctx->playtree_iter) {
3008 play_tree_iter_t *i =
3009 play_tree_iter_new_copy(mpctx->playtree_iter);
3010 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
3011 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3012 play_tree_iter_free(i);
3013 } else
3014 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3015 break;
3018 case MP_CMD_PLAY_ALT_SRC_STEP:
3019 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
3020 int v = cmd->args[0].v.i;
3021 if (v > 0
3022 && mpctx->playtree_iter->file <
3023 mpctx->playtree_iter->num_files)
3024 mpctx->stop_play = PT_NEXT_SRC;
3025 else if (v < 0 && mpctx->playtree_iter->file > 1)
3026 mpctx->stop_play = PT_PREV_SRC;
3028 break;
3030 case MP_CMD_SUB_STEP:
3031 if (sh_video) {
3032 int movement = cmd->args[0].v.i;
3033 step_sub(mpctx->subdata, mpctx->video_pts, movement);
3034 #ifdef CONFIG_ASS
3035 if (mpctx->osd->ass_track)
3036 sub_delay +=
3037 ass_step_sub(mpctx->osd->ass_track,
3038 (mpctx->video_pts +
3039 sub_delay) * 1000 + .5, movement) / 1000.;
3040 #endif
3041 set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration,
3042 "Sub delay: %d ms", ROUND(sub_delay * 1000));
3044 break;
3046 case MP_CMD_SUB_LOG:
3047 log_sub(mpctx);
3048 break;
3050 case MP_CMD_OSD: {
3051 int v = cmd->args[0].v.i;
3052 int max = (opts->term_osd
3053 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
3054 if (opts->osd_level > max)
3055 opts->osd_level = max;
3056 if (v < 0)
3057 opts->osd_level = (opts->osd_level + 1) % (max + 1);
3058 else
3059 opts->osd_level = v > max ? max : v;
3060 /* Show OSD state when disabled, but not when an explicit
3061 argument is given to the OSD command, i.e. in slave mode. */
3062 if (v == -1 && opts->osd_level <= 1)
3063 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
3064 "OSD: %s",
3065 opts->osd_level ? mp_gtext("enabled") :
3066 mp_gtext("disabled"));
3067 else
3068 rm_osd_msg(OSD_MSG_OSD_STATUS);
3069 break;
3072 case MP_CMD_OSD_SHOW_TEXT:
3073 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3074 (cmd->args[1].v.i <
3075 0 ? osd_duration : cmd->args[1].v.i),
3076 "%s", cmd->args[0].v.s);
3077 break;
3079 case MP_CMD_OSD_SHOW_PROPERTY_TEXT: {
3080 char *txt = m_properties_expand_string(mp_properties,
3081 cmd->args[0].v.s,
3082 mpctx);
3083 // if no argument supplied use default osd_duration, else <arg> ms.
3084 if (txt) {
3085 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3086 (cmd->args[1].v.i <
3087 0 ? osd_duration : cmd->args[1].v.i),
3088 "%s", txt);
3089 free(txt);
3091 break;
3094 case MP_CMD_LOADFILE: {
3095 play_tree_t *e = play_tree_new();
3096 play_tree_add_file(e, cmd->args[0].v.s);
3098 if (cmd->args[1].v.i) // append
3099 play_tree_append_entry(mpctx->playtree->child, e);
3100 else {
3101 // Go back to the starting point.
3102 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3103 != PLAY_TREE_ITER_END)
3104 /* NOP */;
3105 play_tree_free_list(mpctx->playtree->child, 1);
3106 play_tree_set_child(mpctx->playtree, e);
3107 pt_iter_goto_head(mpctx->playtree_iter);
3108 mpctx->stop_play = PT_NEXT_SRC;
3110 break;
3113 case MP_CMD_LOADLIST: {
3114 play_tree_t *e = parse_playlist_file(mpctx->mconfig,
3115 bstr(cmd->args[0].v.s));
3116 if (!e)
3117 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
3118 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
3119 else {
3120 if (cmd->args[1].v.i) // append
3121 play_tree_append_entry(mpctx->playtree->child, e);
3122 else {
3123 // Go back to the starting point.
3124 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3125 != PLAY_TREE_ITER_END)
3126 /* NOP */;
3127 play_tree_free_list(mpctx->playtree->child, 1);
3128 play_tree_set_child(mpctx->playtree, e);
3129 pt_iter_goto_head(mpctx->playtree_iter);
3130 mpctx->stop_play = PT_NEXT_SRC;
3133 break;
3136 case MP_CMD_STOP:
3137 // Go back to the starting point.
3138 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
3139 PLAY_TREE_ITER_END)
3140 /* NOP */;
3141 mpctx->stop_play = PT_STOP;
3142 break;
3144 case MP_CMD_OSD_SHOW_PROGRESSION: {
3145 int len = get_time_length(mpctx);
3146 int pts = get_current_time(mpctx);
3147 set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
3148 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3149 "%c %02d:%02d:%02d / %02d:%02d:%02d",
3150 mpctx->osd_function, pts / 3600, (pts / 60) % 60, pts % 60,
3151 len / 3600, (len / 60) % 60, len % 60);
3152 break;
3155 #ifdef CONFIG_RADIO
3156 case MP_CMD_RADIO_STEP_CHANNEL:
3157 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3158 int v = cmd->args[0].v.i;
3159 if (v > 0)
3160 radio_step_channel(mpctx->demuxer->stream,
3161 RADIO_CHANNEL_HIGHER);
3162 else
3163 radio_step_channel(mpctx->demuxer->stream,
3164 RADIO_CHANNEL_LOWER);
3165 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3166 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3167 "Channel: %s",
3168 radio_get_channel_name(mpctx->demuxer->stream));
3171 break;
3173 case MP_CMD_RADIO_SET_CHANNEL:
3174 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3175 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
3176 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3177 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3178 "Channel: %s",
3179 radio_get_channel_name(mpctx->demuxer->stream));
3182 break;
3184 case MP_CMD_RADIO_SET_FREQ:
3185 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3186 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3187 break;
3189 case MP_CMD_RADIO_STEP_FREQ:
3190 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3191 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3192 break;
3193 #endif
3195 #ifdef CONFIG_TV
3196 case MP_CMD_TV_START_SCAN:
3197 if (mpctx->file_format == DEMUXER_TYPE_TV)
3198 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv), 1);
3199 break;
3200 case MP_CMD_TV_SET_FREQ:
3201 if (mpctx->file_format == DEMUXER_TYPE_TV)
3202 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3203 cmd->args[0].v.f * 16.0);
3204 #ifdef CONFIG_PVR
3205 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3206 pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f));
3207 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3208 pvr_get_current_channelname(mpctx->stream),
3209 pvr_get_current_stationname(mpctx->stream));
3211 #endif /* CONFIG_PVR */
3212 break;
3214 case MP_CMD_TV_STEP_FREQ:
3215 if (mpctx->file_format == DEMUXER_TYPE_TV)
3216 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3217 cmd->args[0].v.f * 16.0);
3218 #ifdef CONFIG_PVR
3219 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3220 pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f));
3221 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3222 pvr_get_current_channelname(mpctx->stream),
3223 pvr_get_current_frequency(mpctx->stream));
3225 #endif /* CONFIG_PVR */
3226 break;
3228 case MP_CMD_TV_SET_NORM:
3229 if (mpctx->file_format == DEMUXER_TYPE_TV)
3230 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3231 cmd->args[0].v.s);
3232 break;
3234 case MP_CMD_TV_STEP_CHANNEL:
3235 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3236 int v = cmd->args[0].v.i;
3237 if (v > 0) {
3238 tv_step_channel((tvi_handle_t *) (mpctx->
3239 demuxer->priv),
3240 TV_CHANNEL_HIGHER);
3241 } else {
3242 tv_step_channel((tvi_handle_t *) (mpctx->
3243 demuxer->priv),
3244 TV_CHANNEL_LOWER);
3246 if (tv_channel_list) {
3247 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3248 "Channel: %s", tv_channel_current->name);
3249 //vo_osd_changed(OSDTYPE_SUBTITLE);
3252 #ifdef CONFIG_PVR
3253 else if (mpctx->stream &&
3254 mpctx->stream->type == STREAMTYPE_PVR) {
3255 pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i);
3256 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3257 pvr_get_current_channelname(mpctx->stream),
3258 pvr_get_current_stationname(mpctx->stream));
3260 #endif /* CONFIG_PVR */
3261 #ifdef CONFIG_DVBIN
3262 if (mpctx->stream->type == STREAMTYPE_DVB) {
3263 int dir;
3264 int v = cmd->args[0].v.i;
3266 mpctx->last_dvb_step = v;
3267 if (v > 0)
3268 dir = DVB_CHANNEL_HIGHER;
3269 else
3270 dir = DVB_CHANNEL_LOWER;
3273 if (dvb_step_channel(mpctx->stream, dir)) {
3274 mpctx->stop_play = PT_NEXT_ENTRY;
3275 mpctx->dvbin_reopen = 1;
3278 #endif /* CONFIG_DVBIN */
3279 break;
3281 case MP_CMD_TV_SET_CHANNEL:
3282 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3283 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3284 cmd->args[0].v.s);
3285 if (tv_channel_list) {
3286 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3287 "Channel: %s", tv_channel_current->name);
3288 //vo_osd_changed(OSDTYPE_SUBTITLE);
3291 #ifdef CONFIG_PVR
3292 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3293 pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
3294 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3295 pvr_get_current_channelname(mpctx->stream),
3296 pvr_get_current_stationname(mpctx->stream));
3298 #endif /* CONFIG_PVR */
3299 break;
3301 #ifdef CONFIG_DVBIN
3302 case MP_CMD_DVB_SET_CHANNEL:
3303 if (mpctx->stream->type == STREAMTYPE_DVB) {
3304 mpctx->last_dvb_step = 1;
3306 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3307 cmd->args[0].v.i)) {
3308 mpctx->stop_play = PT_NEXT_ENTRY;
3309 mpctx->dvbin_reopen = 1;
3312 break;
3313 #endif /* CONFIG_DVBIN */
3315 case MP_CMD_TV_LAST_CHANNEL:
3316 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3317 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3318 if (tv_channel_list) {
3319 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3320 "Channel: %s", tv_channel_current->name);
3321 //vo_osd_changed(OSDTYPE_SUBTITLE);
3324 #ifdef CONFIG_PVR
3325 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3326 pvr_set_lastchannel(mpctx->stream);
3327 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3328 pvr_get_current_channelname(mpctx->stream),
3329 pvr_get_current_stationname(mpctx->stream));
3331 #endif /* CONFIG_PVR */
3332 break;
3334 case MP_CMD_TV_STEP_NORM:
3335 if (mpctx->file_format == DEMUXER_TYPE_TV)
3336 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3337 break;
3339 case MP_CMD_TV_STEP_CHANNEL_LIST:
3340 if (mpctx->file_format == DEMUXER_TYPE_TV)
3341 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3342 break;
3343 #endif /* CONFIG_TV */
3344 case MP_CMD_TV_TELETEXT_ADD_DEC:
3345 if (mpctx->demuxer->teletext)
3346 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_ADD_DEC,
3347 &(cmd->args[0].v.s));
3348 break;
3349 case MP_CMD_TV_TELETEXT_GO_LINK:
3350 if (mpctx->demuxer->teletext)
3351 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_GO_LINK,
3352 &(cmd->args[0].v.i));
3353 break;
3355 case MP_CMD_SUB_LOAD:
3356 if (sh_video) {
3357 int n = mpctx->set_of_sub_size;
3358 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3359 if (n != mpctx->set_of_sub_size) {
3360 mpctx->sub_counts[SUB_SOURCE_SUBS]++;
3361 ++mpctx->global_sub_size;
3364 break;
3366 case MP_CMD_SUB_REMOVE:
3367 if (sh_video) {
3368 int v = cmd->args[0].v.i;
3369 if (v < 0)
3370 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3371 else if (v < mpctx->set_of_sub_size)
3372 remove_subtitle_range(mpctx, v, 1);
3374 break;
3376 case MP_CMD_GET_SUB_VISIBILITY:
3377 if (sh_video) {
3378 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3379 "ANS_SUB_VISIBILITY=%d\n", sub_visibility);
3381 break;
3383 case MP_CMD_SCREENSHOT:
3384 screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i);
3385 break;
3387 case MP_CMD_VF_CHANGE_RECTANGLE:
3388 if (!sh_video)
3389 break;
3390 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3391 break;
3393 case MP_CMD_GET_TIME_LENGTH:
3394 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3395 get_time_length(mpctx));
3396 break;
3398 case MP_CMD_GET_FILENAME: {
3399 char *inf = get_metadata(mpctx, META_NAME);
3400 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n", inf);
3401 talloc_free(inf);
3402 break;
3405 case MP_CMD_GET_VIDEO_CODEC: {
3406 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3407 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3408 talloc_free(inf);
3409 break;
3412 case MP_CMD_GET_VIDEO_BITRATE: {
3413 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3414 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3415 talloc_free(inf);
3416 break;
3419 case MP_CMD_GET_VIDEO_RESOLUTION: {
3420 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3421 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3422 talloc_free(inf);
3423 break;
3426 case MP_CMD_GET_AUDIO_CODEC: {
3427 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3428 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3429 talloc_free(inf);
3430 break;
3433 case MP_CMD_GET_AUDIO_BITRATE: {
3434 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3435 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3436 talloc_free(inf);
3437 break;
3440 case MP_CMD_GET_AUDIO_SAMPLES: {
3441 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3442 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3443 talloc_free(inf);
3444 break;
3447 case MP_CMD_GET_META_TITLE: {
3448 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3449 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3450 talloc_free(inf);
3451 break;
3454 case MP_CMD_GET_META_ARTIST: {
3455 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3456 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3457 talloc_free(inf);
3458 break;
3461 case MP_CMD_GET_META_ALBUM: {
3462 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3463 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3464 talloc_free(inf);
3465 break;
3468 case MP_CMD_GET_META_YEAR: {
3469 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3470 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3471 talloc_free(inf);
3472 break;
3475 case MP_CMD_GET_META_COMMENT: {
3476 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3477 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3478 talloc_free(inf);
3479 break;
3482 case MP_CMD_GET_META_TRACK: {
3483 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3484 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3485 talloc_free(inf);
3486 break;
3489 case MP_CMD_GET_META_GENRE: {
3490 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3491 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3492 talloc_free(inf);
3493 break;
3496 case MP_CMD_GET_VO_FULLSCREEN:
3497 if (mpctx->video_out && mpctx->video_out->config_ok)
3498 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3499 break;
3501 case MP_CMD_GET_PERCENT_POS:
3502 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3503 get_percent_pos(mpctx));
3504 break;
3506 case MP_CMD_GET_TIME_POS: {
3507 float pos = get_current_time(mpctx);
3508 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3509 break;
3512 case MP_CMD_RUN:
3513 #ifndef __MINGW32__
3514 if (!fork()) {
3515 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3516 exit(0);
3518 #endif
3519 break;
3521 case MP_CMD_KEYDOWN_EVENTS:
3522 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3523 break;
3525 case MP_CMD_SET_MOUSE_POS: {
3526 int pointer_x, pointer_y;
3527 double dx, dy;
3528 pointer_x = cmd->args[0].v.i;
3529 pointer_y = cmd->args[1].v.i;
3530 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3531 #ifdef CONFIG_DVDNAV
3532 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3533 && dx > 0.0 && dy > 0.0) {
3534 int button = -1;
3535 pointer_x = (int) (dx * (double) sh_video->disp_w);
3536 pointer_y = (int) (dy * (double) sh_video->disp_h);
3537 mp_dvdnav_update_mouse_pos(mpctx->stream,
3538 pointer_x, pointer_y, &button);
3539 if (opts->osd_level > 1 && button > 0)
3540 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3541 "Selected button number %d", button);
3543 #endif
3544 break;
3547 #ifdef CONFIG_DVDNAV
3548 case MP_CMD_DVDNAV: {
3549 int button = -1;
3550 int i;
3551 enum mp_command_type command = 0;
3552 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3553 break;
3555 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3556 if (cmd->args[0].v.s &&
3557 !strcasecmp(cmd->args[0].v.s,
3558 mp_dvdnav_bindings[i].name))
3559 command = mp_dvdnav_bindings[i].cmd;
3561 mp_dvdnav_handle_input(mpctx->stream, command, &button);
3562 if (opts->osd_level > 1 && button > 0)
3563 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3564 "Selected button number %d", button);
3565 break;
3568 case MP_CMD_SWITCH_TITLE:
3569 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3570 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3571 break;
3573 #endif
3575 case MP_CMD_AF_SWITCH:
3576 if (sh_audio) {
3577 af_uninit(mpctx->mixer.afilter);
3578 af_init(mpctx->mixer.afilter);
3580 case MP_CMD_AF_ADD:
3581 case MP_CMD_AF_DEL: {
3582 if (!sh_audio)
3583 break;
3584 char *af_args = strdup(cmd->args[0].v.s);
3585 char *af_commands = af_args;
3586 char *af_command;
3587 af_instance_t *af;
3588 while ((af_command = strsep(&af_commands, ",")) != NULL) {
3589 if (cmd->id == MP_CMD_AF_DEL) {
3590 af = af_get(mpctx->mixer.afilter, af_command);
3591 if (af != NULL)
3592 af_remove(mpctx->mixer.afilter, af);
3593 } else
3594 af_add(mpctx->mixer.afilter, af_command);
3596 reinit_audio_chain(mpctx);
3597 free(af_args);
3598 break;
3600 case MP_CMD_AF_CLR:
3601 if (!sh_audio)
3602 break;
3603 af_uninit(mpctx->mixer.afilter);
3604 af_init(mpctx->mixer.afilter);
3605 reinit_audio_chain(mpctx);
3606 break;
3607 case MP_CMD_AF_CMDLINE:
3608 if (sh_audio) {
3609 af_instance_t *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
3610 if (!af) {
3611 mp_msg(MSGT_CPLAYER, MSGL_WARN,
3612 "Filter '%s' not found in chain.\n", cmd->args[0].v.s);
3613 break;
3615 af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
3616 af_reinit(sh_audio->afilter, af);
3618 break;
3620 default:
3621 mp_msg(MSGT_CPLAYER, MSGL_V,
3622 "Received unknown cmd %s\n", cmd->name);
3625 old_pause_hack:
3626 switch (cmd->pausing) {
3627 case 1: // "pausing"
3628 pause_player(mpctx);
3629 break;
3630 case 3: // "pausing_toggle"
3631 if (mpctx->paused)
3632 unpause_player(mpctx);
3633 else
3634 pause_player(mpctx);
3635 break;