Libav API updates (remove most deprecated-in-0.7 uses)
[mplayer.git] / command.c
blobbe1d688f8f4dc089be46e7eb4ecd6da8ac156b10
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 mpctx->osd_function = OSD_PLAY;
684 } else {
685 pause_player(mpctx);
686 mpctx->osd_function = OSD_PAUSE;
688 return M_PROPERTY_OK;
689 default:
690 return m_property_flag(prop, action, arg, &mpctx->paused);
695 /// Volume (RW)
696 static int mp_property_volume(m_option_t *prop, int action, void *arg,
697 MPContext *mpctx)
700 if (!mpctx->sh_audio)
701 return M_PROPERTY_UNAVAILABLE;
703 switch (action) {
704 case M_PROPERTY_GET:
705 if (!arg)
706 return M_PROPERTY_ERROR;
707 mixer_getbothvolume(&mpctx->mixer, arg);
708 return M_PROPERTY_OK;
709 case M_PROPERTY_PRINT: {
710 float vol;
711 if (!arg)
712 return M_PROPERTY_ERROR;
713 mixer_getbothvolume(&mpctx->mixer, &vol);
714 return m_property_float_range(prop, action, arg, &vol);
716 case M_PROPERTY_STEP_UP:
717 case M_PROPERTY_STEP_DOWN:
718 case M_PROPERTY_SET:
719 break;
720 default:
721 return M_PROPERTY_NOT_IMPLEMENTED;
724 if (mpctx->edl_muted)
725 return M_PROPERTY_DISABLED;
726 mpctx->user_muted = 0;
728 switch (action) {
729 case M_PROPERTY_SET:
730 if (!arg)
731 return M_PROPERTY_ERROR;
732 M_PROPERTY_CLAMP(prop, *(float *) arg);
733 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
734 return M_PROPERTY_OK;
735 case M_PROPERTY_STEP_UP:
736 if (arg && *(float *) arg <= 0)
737 mixer_decvolume(&mpctx->mixer);
738 else
739 mixer_incvolume(&mpctx->mixer);
740 return M_PROPERTY_OK;
741 case M_PROPERTY_STEP_DOWN:
742 if (arg && *(float *) arg <= 0)
743 mixer_incvolume(&mpctx->mixer);
744 else
745 mixer_decvolume(&mpctx->mixer);
746 return M_PROPERTY_OK;
748 return M_PROPERTY_NOT_IMPLEMENTED;
751 /// Mute (RW)
752 static int mp_property_mute(m_option_t *prop, int action, void *arg,
753 MPContext *mpctx)
756 if (!mpctx->sh_audio)
757 return M_PROPERTY_UNAVAILABLE;
759 switch (action) {
760 case M_PROPERTY_SET:
761 if (mpctx->edl_muted)
762 return M_PROPERTY_DISABLED;
763 if (!arg)
764 return M_PROPERTY_ERROR;
765 if ((!!*(int *) arg) != mpctx->mixer.muted)
766 mixer_mute(&mpctx->mixer);
767 mpctx->user_muted = mpctx->mixer.muted;
768 return M_PROPERTY_OK;
769 case M_PROPERTY_STEP_UP:
770 case M_PROPERTY_STEP_DOWN:
771 if (mpctx->edl_muted)
772 return M_PROPERTY_DISABLED;
773 mixer_mute(&mpctx->mixer);
774 mpctx->user_muted = mpctx->mixer.muted;
775 return M_PROPERTY_OK;
776 case M_PROPERTY_PRINT:
777 if (!arg)
778 return M_PROPERTY_ERROR;
779 if (mpctx->edl_muted) {
780 *(char **) arg = talloc_strdup(NULL, mp_gtext("enabled (EDL)"));
781 return M_PROPERTY_OK;
783 default:
784 return m_property_flag(prop, action, arg, &mpctx->mixer.muted);
789 /// Audio delay (RW)
790 static int mp_property_audio_delay(m_option_t *prop, int action,
791 void *arg, MPContext *mpctx)
793 if (!(mpctx->sh_audio && mpctx->sh_video))
794 return M_PROPERTY_UNAVAILABLE;
795 switch (action) {
796 case M_PROPERTY_SET:
797 case M_PROPERTY_STEP_UP:
798 case M_PROPERTY_STEP_DOWN: {
799 int ret;
800 float delay = audio_delay;
801 ret = m_property_delay(prop, action, arg, &audio_delay);
802 if (ret != M_PROPERTY_OK)
803 return ret;
804 if (mpctx->sh_audio)
805 mpctx->delay -= audio_delay - delay;
807 return M_PROPERTY_OK;
808 default:
809 return m_property_delay(prop, action, arg, &audio_delay);
813 /// Audio codec tag (RO)
814 static int mp_property_audio_format(m_option_t *prop, int action,
815 void *arg, MPContext *mpctx)
817 if (!mpctx->sh_audio)
818 return M_PROPERTY_UNAVAILABLE;
819 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
822 /// Audio codec name (RO)
823 static int mp_property_audio_codec(m_option_t *prop, int action,
824 void *arg, MPContext *mpctx)
826 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
827 return M_PROPERTY_UNAVAILABLE;
828 return m_property_string_ro(prop, action, arg,
829 mpctx->sh_audio->codec->name);
832 /// Audio bitrate (RO)
833 static int mp_property_audio_bitrate(m_option_t *prop, int action,
834 void *arg, MPContext *mpctx)
836 if (!mpctx->sh_audio)
837 return M_PROPERTY_UNAVAILABLE;
838 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
841 /// Samplerate (RO)
842 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
843 MPContext *mpctx)
845 if (!mpctx->sh_audio)
846 return M_PROPERTY_UNAVAILABLE;
847 switch (action) {
848 case M_PROPERTY_PRINT:
849 if (!arg)
850 return M_PROPERTY_ERROR;
851 *(char **)arg = talloc_asprintf(NULL, "%d kHz",
852 mpctx->sh_audio->samplerate / 1000);
853 return M_PROPERTY_OK;
855 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
858 /// Number of channels (RO)
859 static int mp_property_channels(m_option_t *prop, int action, void *arg,
860 MPContext *mpctx)
862 if (!mpctx->sh_audio)
863 return M_PROPERTY_UNAVAILABLE;
864 switch (action) {
865 case M_PROPERTY_PRINT:
866 if (!arg)
867 return M_PROPERTY_ERROR;
868 switch (mpctx->sh_audio->channels) {
869 case 1:
870 *(char **) arg = talloc_strdup(NULL, "mono");
871 break;
872 case 2:
873 *(char **) arg = talloc_strdup(NULL, "stereo");
874 break;
875 default:
876 *(char **) arg = talloc_asprintf(NULL, "%d channels",
877 mpctx->sh_audio->channels);
879 return M_PROPERTY_OK;
881 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
884 /// Balance (RW)
885 static int mp_property_balance(m_option_t *prop, int action, void *arg,
886 MPContext *mpctx)
888 float bal;
890 if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2)
891 return M_PROPERTY_UNAVAILABLE;
893 switch (action) {
894 case M_PROPERTY_GET:
895 if (!arg)
896 return M_PROPERTY_ERROR;
897 mixer_getbalance(&mpctx->mixer, arg);
898 return M_PROPERTY_OK;
899 case M_PROPERTY_PRINT: {
900 char **str = arg;
901 if (!arg)
902 return M_PROPERTY_ERROR;
903 mixer_getbalance(&mpctx->mixer, &bal);
904 if (bal == 0.f)
905 *str = talloc_strdup(NULL, "center");
906 else if (bal == -1.f)
907 *str = talloc_strdup(NULL, "left only");
908 else if (bal == 1.f)
909 *str = talloc_strdup(NULL, "right only");
910 else {
911 unsigned right = (bal + 1.f) / 2.f * 100.f;
912 *str = talloc_asprintf(NULL, "left %d%%, right %d%%",
913 100 - right, right);
915 return M_PROPERTY_OK;
917 case M_PROPERTY_STEP_UP:
918 case M_PROPERTY_STEP_DOWN:
919 mixer_getbalance(&mpctx->mixer, &bal);
920 bal += (arg ? *(float *)arg : .1f) *
921 (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
922 M_PROPERTY_CLAMP(prop, bal);
923 mixer_setbalance(&mpctx->mixer, bal);
924 return M_PROPERTY_OK;
925 case M_PROPERTY_SET:
926 if (!arg)
927 return M_PROPERTY_ERROR;
928 M_PROPERTY_CLAMP(prop, *(float *)arg);
929 mixer_setbalance(&mpctx->mixer, *(float *)arg);
930 return M_PROPERTY_OK;
932 return M_PROPERTY_NOT_IMPLEMENTED;
935 /// Selected audio id (RW)
936 static int mp_property_audio(m_option_t *prop, int action, void *arg,
937 MPContext *mpctx)
939 int current_id, tmp;
940 if (!mpctx->demuxer || !mpctx->d_audio)
941 return M_PROPERTY_UNAVAILABLE;
942 struct sh_audio *sh = mpctx->sh_audio;
943 current_id = sh ? sh->aid : -2;
945 switch (action) {
946 case M_PROPERTY_GET:
947 if (!arg)
948 return M_PROPERTY_ERROR;
949 *(int *) arg = current_id;
950 return M_PROPERTY_OK;
951 case M_PROPERTY_PRINT:
952 if (!arg)
953 return M_PROPERTY_ERROR;
955 if (current_id < 0)
956 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
957 else if (sh && (sh->lang || sh->title)) {
958 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
959 if (sh->title)
960 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
961 current_id, lang, sh->title);
962 else
963 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
964 lang);
965 } else {
966 char lang[40];
967 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
968 if (0) ;
969 #ifdef CONFIG_DVDREAD
970 else if (mpctx->stream->type == STREAMTYPE_DVD) {
971 int code = dvd_lang_from_aid(mpctx->stream, current_id);
972 if (code) {
973 lang[0] = code >> 8;
974 lang[1] = code;
975 lang[2] = 0;
978 #endif
980 #ifdef CONFIG_DVDNAV
981 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
982 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
983 #endif
984 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, lang);
986 return M_PROPERTY_OK;
988 case M_PROPERTY_STEP_UP:
989 case M_PROPERTY_SET:
990 if (action == M_PROPERTY_SET && arg)
991 tmp = *((int *) arg);
992 else
993 tmp = -1;
994 int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
995 if (new_id != current_id)
996 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
997 if (new_id != current_id && new_id >= 0) {
998 sh_audio_t *sh2;
999 sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
1000 sh2->ds = mpctx->d_audio;
1001 mpctx->sh_audio = sh2;
1002 reinit_audio_chain(mpctx);
1004 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
1005 return M_PROPERTY_OK;
1006 default:
1007 return M_PROPERTY_NOT_IMPLEMENTED;
1012 /// Selected video id (RW)
1013 static int mp_property_video(m_option_t *prop, int action, void *arg,
1014 MPContext *mpctx)
1016 struct MPOpts *opts = &mpctx->opts;
1017 int current_id, tmp;
1018 if (!mpctx->demuxer || !mpctx->d_video)
1019 return M_PROPERTY_UNAVAILABLE;
1020 current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
1022 switch (action) {
1023 case M_PROPERTY_GET:
1024 if (!arg)
1025 return M_PROPERTY_ERROR;
1026 *(int *) arg = current_id;
1027 return M_PROPERTY_OK;
1028 case M_PROPERTY_PRINT:
1029 if (!arg)
1030 return M_PROPERTY_ERROR;
1032 if (current_id < 0)
1033 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1034 else {
1035 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
1036 mp_gtext("unknown"));
1038 return M_PROPERTY_OK;
1040 case M_PROPERTY_STEP_UP:
1041 case M_PROPERTY_SET:
1042 if (action == M_PROPERTY_SET && arg)
1043 tmp = *((int *) arg);
1044 else
1045 tmp = -1;
1046 int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
1047 if (new_id != current_id)
1048 uninit_player(mpctx, INITIALIZED_VCODEC |
1049 (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
1050 if (new_id != current_id && new_id >= 0) {
1051 sh_video_t *sh2;
1052 sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
1053 sh2->ds = mpctx->d_video;
1054 mpctx->sh_video = sh2;
1055 reinit_video_chain(mpctx);
1057 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
1058 return M_PROPERTY_OK;
1060 default:
1061 return M_PROPERTY_NOT_IMPLEMENTED;
1065 static int mp_property_program(m_option_t *prop, int action, void *arg,
1066 MPContext *mpctx)
1068 demux_program_t prog;
1070 switch (action) {
1071 case M_PROPERTY_STEP_UP:
1072 case M_PROPERTY_SET:
1073 if (action == M_PROPERTY_SET && arg)
1074 prog.progid = *((int *) arg);
1075 else
1076 prog.progid = -1;
1077 if (demux_control(mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1078 &prog) == DEMUXER_CTRL_NOTIMPL)
1079 return M_PROPERTY_ERROR;
1081 if (prog.aid < 0 && prog.vid < 0) {
1082 mp_msg(MSGT_CPLAYER, MSGL_ERR,
1083 "Selected program contains no audio or video streams!\n");
1084 return M_PROPERTY_ERROR;
1086 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1087 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1088 return M_PROPERTY_OK;
1090 default:
1091 return M_PROPERTY_NOT_IMPLEMENTED;
1096 /// Fullscreen state (RW)
1097 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1098 MPContext *mpctx)
1101 if (!mpctx->video_out)
1102 return M_PROPERTY_UNAVAILABLE;
1104 switch (action) {
1105 case M_PROPERTY_SET:
1106 if (!arg)
1107 return M_PROPERTY_ERROR;
1108 M_PROPERTY_CLAMP(prop, *(int *) arg);
1109 if (vo_fs == !!*(int *) arg)
1110 return M_PROPERTY_OK;
1111 case M_PROPERTY_STEP_UP:
1112 case M_PROPERTY_STEP_DOWN:
1113 if (mpctx->video_out->config_ok)
1114 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1115 mpctx->opts.fullscreen = vo_fs;
1116 return M_PROPERTY_OK;
1117 default:
1118 return m_property_flag(prop, action, arg, &vo_fs);
1122 static int mp_property_deinterlace(m_option_t *prop, int action,
1123 void *arg, MPContext *mpctx)
1125 int deinterlace;
1126 vf_instance_t *vf;
1127 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1128 return M_PROPERTY_UNAVAILABLE;
1129 vf = mpctx->sh_video->vfilter;
1130 switch (action) {
1131 case M_PROPERTY_GET:
1132 if (!arg)
1133 return M_PROPERTY_ERROR;
1134 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1135 return M_PROPERTY_OK;
1136 case M_PROPERTY_SET:
1137 if (!arg)
1138 return M_PROPERTY_ERROR;
1139 M_PROPERTY_CLAMP(prop, *(int *) arg);
1140 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1141 return M_PROPERTY_OK;
1142 case M_PROPERTY_STEP_UP:
1143 case M_PROPERTY_STEP_DOWN:
1144 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1145 deinterlace = !deinterlace;
1146 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1147 return M_PROPERTY_OK;
1149 int value = 0;
1150 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1151 return m_property_flag_ro(prop, action, arg, value);
1154 static int colormatrix_property_helper(m_option_t *prop, int action,
1155 void *arg, MPContext *mpctx)
1157 int r = mp_property_generic_option(prop, action, arg, mpctx);
1158 // testing for an actual change is too much effort
1159 switch (action) {
1160 case M_PROPERTY_SET:
1161 case M_PROPERTY_STEP_UP:
1162 case M_PROPERTY_STEP_DOWN:
1163 if (mpctx->sh_video)
1164 set_video_colorspace(mpctx->sh_video);
1165 break;
1167 return r;
1170 static int mp_property_colormatrix(m_option_t *prop, int action, void *arg,
1171 MPContext *mpctx)
1173 struct MPOpts *opts = &mpctx->opts;
1174 switch (action) {
1175 case M_PROPERTY_PRINT:
1176 if (!arg)
1177 return M_PROPERTY_ERROR;
1178 struct mp_csp_details actual = { .format = -1 };
1179 char *req_csp = mp_csp_names[opts->requested_colorspace];
1180 char *real_csp = NULL;
1181 if (mpctx->sh_video) {
1182 struct vf_instance *vf = mpctx->sh_video->vfilter;
1183 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1184 real_csp = mp_csp_names[actual.format];
1185 } else {
1186 real_csp = "Unknown";
1189 char *res;
1190 if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) {
1191 // Caveat: doesn't handle the case when the autodetected colorspace
1192 // is different from the actual colorspace as used by the
1193 // VO - the OSD will display the VO colorspace without
1194 // indication that it doesn't match the requested colorspace.
1195 res = talloc_asprintf(NULL, "Auto (%s)", real_csp);
1196 } else if (opts->requested_colorspace == actual.format || !real_csp) {
1197 res = talloc_strdup(NULL, req_csp);
1198 } else
1199 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1200 req_csp, real_csp);
1201 *(char **)arg = res;
1202 return M_PROPERTY_OK;
1203 default:;
1204 return colormatrix_property_helper(prop, action, arg, mpctx);
1208 static int levels_property_helper(int offset, m_option_t *prop, int action,
1209 void *arg, MPContext *mpctx)
1211 char *optname = prop->priv;
1212 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
1213 bstr(optname));
1214 int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts);
1216 switch (action) {
1217 case M_PROPERTY_PRINT:
1218 if (!arg)
1219 return M_PROPERTY_ERROR;
1220 struct mp_csp_details actual = {0};
1221 int actual_level = -1;
1222 char *req_level = m_option_print(opt, valptr);
1223 char *real_level = NULL;
1224 if (mpctx->sh_video) {
1225 struct vf_instance *vf = mpctx->sh_video->vfilter;
1226 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1227 actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset);
1228 real_level = m_option_print(opt, &actual_level);
1229 } else {
1230 real_level = talloc_strdup(NULL, "Unknown");
1233 char *res;
1234 if (*valptr == MP_CSP_LEVELS_AUTO && real_level) {
1235 res = talloc_asprintf(NULL, "Auto (%s)", real_level);
1236 } else if (*valptr == actual_level || !real_level) {
1237 res = talloc_strdup(NULL, real_level);
1238 } else
1239 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1240 req_level, real_level);
1241 talloc_free(req_level);
1242 talloc_free(real_level);
1243 *(char **)arg = res;
1244 return M_PROPERTY_OK;
1245 default:;
1246 return colormatrix_property_helper(prop, action, arg, mpctx);
1250 static int mp_property_colormatrix_input_range(m_option_t *prop, int action,
1251 void *arg, MPContext *mpctx)
1253 return levels_property_helper(offsetof(struct mp_csp_details, levels_in),
1254 prop, action, arg, mpctx);
1257 static int mp_property_colormatrix_output_range(m_option_t *prop, int action,
1258 void *arg, MPContext *mpctx)
1260 return levels_property_helper(offsetof(struct mp_csp_details, levels_out),
1261 prop, action, arg, mpctx);
1264 static int mp_property_capture(m_option_t *prop, int action,
1265 void *arg, MPContext *mpctx)
1267 struct MPOpts *opts = &mpctx->opts;
1269 if (!mpctx->stream)
1270 return M_PROPERTY_UNAVAILABLE;
1272 if (!opts->capture_dump) {
1273 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1274 "Capturing not enabled (forgot -capture parameter?)\n");
1275 return M_PROPERTY_ERROR;
1278 int capturing = !!mpctx->stream->capture_file;
1280 int ret = m_property_flag(prop, action, arg, &capturing);
1281 if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1282 if (capturing) {
1283 mpctx->stream->capture_file = fopen(opts->stream_dump_name, "wb");
1284 if (!mpctx->stream->capture_file) {
1285 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1286 "Error opening capture file: %s\n", strerror(errno));
1287 ret = M_PROPERTY_ERROR;
1289 } else {
1290 fclose(mpctx->stream->capture_file);
1291 mpctx->stream->capture_file = NULL;
1295 return ret;
1298 /// Panscan (RW)
1299 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1300 MPContext *mpctx)
1303 if (!mpctx->video_out
1304 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1305 return M_PROPERTY_UNAVAILABLE;
1307 switch (action) {
1308 case M_PROPERTY_SET:
1309 if (!arg)
1310 return M_PROPERTY_ERROR;
1311 M_PROPERTY_CLAMP(prop, *(float *) arg);
1312 vo_panscan = *(float *) arg;
1313 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1314 return M_PROPERTY_OK;
1315 case M_PROPERTY_STEP_UP:
1316 case M_PROPERTY_STEP_DOWN:
1317 vo_panscan += (arg ? *(float *) arg : 0.1) *
1318 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1319 if (vo_panscan > 1)
1320 vo_panscan = 1;
1321 else if (vo_panscan < 0)
1322 vo_panscan = 0;
1323 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1324 return M_PROPERTY_OK;
1325 default:
1326 return m_property_float_range(prop, action, arg, &vo_panscan);
1330 /// Helper to set vo flags.
1331 /** \ingroup PropertyImplHelper
1333 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1334 int vo_ctrl, int *vo_var, MPContext *mpctx)
1337 if (!mpctx->video_out)
1338 return M_PROPERTY_UNAVAILABLE;
1340 switch (action) {
1341 case M_PROPERTY_SET:
1342 if (!arg)
1343 return M_PROPERTY_ERROR;
1344 M_PROPERTY_CLAMP(prop, *(int *) arg);
1345 if (*vo_var == !!*(int *) arg)
1346 return M_PROPERTY_OK;
1347 case M_PROPERTY_STEP_UP:
1348 case M_PROPERTY_STEP_DOWN:
1349 if (mpctx->video_out->config_ok)
1350 vo_control(mpctx->video_out, vo_ctrl, 0);
1351 return M_PROPERTY_OK;
1352 default:
1353 return m_property_flag(prop, action, arg, vo_var);
1357 /// Window always on top (RW)
1358 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1359 MPContext *mpctx)
1361 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1362 &mpctx->opts.vo_ontop, mpctx);
1365 /// Display in the root window (RW)
1366 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1367 MPContext *mpctx)
1369 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1370 &vo_rootwin, mpctx);
1373 /// Show window borders (RW)
1374 static int mp_property_border(m_option_t *prop, int action, void *arg,
1375 MPContext *mpctx)
1377 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1378 &vo_border, mpctx);
1381 /// Framedropping state (RW)
1382 static int mp_property_framedropping(m_option_t *prop, int action,
1383 void *arg, MPContext *mpctx)
1386 if (!mpctx->sh_video)
1387 return M_PROPERTY_UNAVAILABLE;
1389 switch (action) {
1390 case M_PROPERTY_PRINT:
1391 if (!arg)
1392 return M_PROPERTY_ERROR;
1393 *(char **) arg = talloc_strdup(NULL, frame_dropping == 1 ?
1394 mp_gtext("enabled") :
1395 (frame_dropping == 2 ? mp_gtext("hard") :
1396 mp_gtext("disabled")));
1397 return M_PROPERTY_OK;
1398 default:
1399 return m_property_choice(prop, action, arg, &frame_dropping);
1403 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1404 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1405 MPContext *mpctx)
1407 int *gamma = (int *)((char *)&mpctx->opts + prop->offset);
1408 int r, val;
1410 if (!mpctx->sh_video)
1411 return M_PROPERTY_UNAVAILABLE;
1413 if (gamma[0] == 1000) {
1414 gamma[0] = 0;
1415 get_video_colors(mpctx->sh_video, prop->name, gamma);
1418 switch (action) {
1419 case M_PROPERTY_SET:
1420 if (!arg)
1421 return M_PROPERTY_ERROR;
1422 M_PROPERTY_CLAMP(prop, *(int *) arg);
1423 *gamma = *(int *) arg;
1424 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1425 if (r <= 0)
1426 break;
1427 return r;
1428 case M_PROPERTY_GET:
1429 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1430 if (!arg)
1431 return M_PROPERTY_ERROR;
1432 *(int *)arg = val;
1433 return M_PROPERTY_OK;
1435 break;
1436 case M_PROPERTY_STEP_UP:
1437 case M_PROPERTY_STEP_DOWN:
1438 *gamma += (arg ? *(int *) arg : 1) *
1439 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1440 M_PROPERTY_CLAMP(prop, *gamma);
1441 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1442 if (r <= 0)
1443 break;
1444 return r;
1445 default:
1446 return M_PROPERTY_NOT_IMPLEMENTED;
1449 #ifdef CONFIG_TV
1450 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1451 int l = strlen(prop->name);
1452 char tv_prop[3 + l + 1];
1453 sprintf(tv_prop, "tv_%s", prop->name);
1454 return mp_property_do(tv_prop, action, arg, mpctx);
1456 #endif
1458 return M_PROPERTY_UNAVAILABLE;
1461 /// VSync (RW)
1462 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1463 MPContext *mpctx)
1465 return m_property_flag(prop, action, arg, &vo_vsync);
1468 /// Video codec tag (RO)
1469 static int mp_property_video_format(m_option_t *prop, int action,
1470 void *arg, MPContext *mpctx)
1472 char *meta;
1473 if (!mpctx->sh_video)
1474 return M_PROPERTY_UNAVAILABLE;
1475 switch (action) {
1476 case M_PROPERTY_PRINT:
1477 if (!arg)
1478 return M_PROPERTY_ERROR;
1479 switch (mpctx->sh_video->format) {
1480 case 0x10000001:
1481 meta = talloc_strdup(NULL, "mpeg1");
1482 break;
1483 case 0x10000002:
1484 meta = talloc_strdup(NULL, "mpeg2");
1485 break;
1486 case 0x10000004:
1487 meta = talloc_strdup(NULL, "mpeg4");
1488 break;
1489 case 0x10000005:
1490 meta = talloc_strdup(NULL, "h264");
1491 break;
1492 default:
1493 if (mpctx->sh_video->format >= 0x20202020) {
1494 meta = talloc_asprintf(NULL, "%.4s",
1495 (char *) &mpctx->sh_video->format);
1496 } else
1497 meta = talloc_asprintf(NULL, "0x%08X", mpctx->sh_video->format);
1499 *(char **)arg = meta;
1500 return M_PROPERTY_OK;
1502 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1505 /// Video codec name (RO)
1506 static int mp_property_video_codec(m_option_t *prop, int action,
1507 void *arg, MPContext *mpctx)
1509 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1510 return M_PROPERTY_UNAVAILABLE;
1511 return m_property_string_ro(prop, action, arg,
1512 mpctx->sh_video->codec->name);
1516 /// Video bitrate (RO)
1517 static int mp_property_video_bitrate(m_option_t *prop, int action,
1518 void *arg, MPContext *mpctx)
1520 if (!mpctx->sh_video)
1521 return M_PROPERTY_UNAVAILABLE;
1522 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1525 /// Video display width (RO)
1526 static int mp_property_width(m_option_t *prop, int action, void *arg,
1527 MPContext *mpctx)
1529 if (!mpctx->sh_video)
1530 return M_PROPERTY_UNAVAILABLE;
1531 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1534 /// Video display height (RO)
1535 static int mp_property_height(m_option_t *prop, int action, void *arg,
1536 MPContext *mpctx)
1538 if (!mpctx->sh_video)
1539 return M_PROPERTY_UNAVAILABLE;
1540 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1543 /// Video fps (RO)
1544 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1545 MPContext *mpctx)
1547 if (!mpctx->sh_video)
1548 return M_PROPERTY_UNAVAILABLE;
1549 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1552 /// Video aspect (RO)
1553 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1554 MPContext *mpctx)
1556 if (!mpctx->sh_video)
1557 return M_PROPERTY_UNAVAILABLE;
1558 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1562 /// Text subtitle position (RW)
1563 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1564 MPContext *mpctx)
1566 switch (action) {
1567 case M_PROPERTY_SET:
1568 if (!arg)
1569 return M_PROPERTY_ERROR;
1570 case M_PROPERTY_STEP_UP:
1571 case M_PROPERTY_STEP_DOWN:
1572 vo_osd_changed(OSDTYPE_SUBTITLE);
1573 default:
1574 return m_property_int_range(prop, action, arg, &sub_pos);
1578 /// Selected subtitles (RW)
1579 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1580 MPContext *mpctx)
1582 struct MPOpts *opts = &mpctx->opts;
1583 demux_stream_t *const d_sub = mpctx->d_sub;
1584 int source = -1, reset_spu = 0;
1585 int source_pos = -1;
1587 update_global_sub_size(mpctx);
1588 const int global_sub_size = mpctx->global_sub_size;
1590 if (global_sub_size <= 0)
1591 return M_PROPERTY_UNAVAILABLE;
1593 switch (action) {
1594 case M_PROPERTY_GET:
1595 if (!arg)
1596 return M_PROPERTY_ERROR;
1597 *(int *) arg = mpctx->global_sub_pos;
1598 return M_PROPERTY_OK;
1599 case M_PROPERTY_PRINT:
1600 if (!arg)
1601 return M_PROPERTY_ERROR;
1602 char *sub_name = NULL;
1603 if (mpctx->subdata)
1604 sub_name = mpctx->subdata->filename;
1605 #ifdef CONFIG_ASS
1606 if (mpctx->osd->ass_track)
1607 sub_name = mpctx->osd->ass_track->name;
1608 #endif
1609 if (!sub_name && mpctx->subdata)
1610 sub_name = mpctx->subdata->filename;
1611 if (sub_name) {
1612 const char *tmp = mp_basename(sub_name);
1614 *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
1615 mpctx->set_of_sub_pos + 1,
1616 strlen(tmp) < 20 ? "" : "...",
1617 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1618 return M_PROPERTY_OK;
1620 #ifdef CONFIG_DVDNAV
1621 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1622 if (vo_spudec && opts->sub_id >= 0) {
1623 unsigned char lang[3];
1624 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id,
1625 lang)) {
1626 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1627 opts->sub_id, lang);
1628 return M_PROPERTY_OK;
1632 #endif
1634 if ((d_sub->demuxer->type == DEMUXER_TYPE_MATROSKA
1635 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF
1636 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1637 || d_sub->demuxer->type == DEMUXER_TYPE_OGG)
1638 && d_sub->sh && opts->sub_id >= 0) {
1639 struct sh_sub *sh = d_sub->sh;
1640 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
1641 if (sh->title)
1642 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
1643 opts->sub_id, lang, sh->title);
1644 else
1645 *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
1646 opts->sub_id, lang);
1647 return M_PROPERTY_OK;
1650 if (vo_vobsub && vobsub_id >= 0) {
1651 const char *language = mp_gtext("unknown");
1652 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1653 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1654 vobsub_id, language ? language : mp_gtext("unknown"));
1655 return M_PROPERTY_OK;
1657 #ifdef CONFIG_DVDREAD
1658 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1659 && opts->sub_id >= 0) {
1660 char lang[3];
1661 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1662 lang[0] = code >> 8;
1663 lang[1] = code;
1664 lang[2] = 0;
1665 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1666 opts->sub_id, lang);
1667 return M_PROPERTY_OK;
1669 #endif
1670 if (opts->sub_id >= 0) {
1671 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
1672 mp_gtext("unknown"));
1673 return M_PROPERTY_OK;
1675 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1676 return M_PROPERTY_OK;
1678 case M_PROPERTY_SET:
1679 if (!arg)
1680 return M_PROPERTY_ERROR;
1681 if (*(int *) arg < -1)
1682 *(int *) arg = -1;
1683 else if (*(int *) arg >= global_sub_size)
1684 *(int *) arg = global_sub_size - 1;
1685 mpctx->global_sub_pos = *(int *) arg;
1686 break;
1687 case M_PROPERTY_STEP_UP:
1688 mpctx->global_sub_pos += 2;
1689 mpctx->global_sub_pos =
1690 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1691 break;
1692 case M_PROPERTY_STEP_DOWN:
1693 mpctx->global_sub_pos += global_sub_size + 1;
1694 mpctx->global_sub_pos =
1695 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1696 break;
1697 default:
1698 return M_PROPERTY_NOT_IMPLEMENTED;
1701 if (mpctx->global_sub_pos >= 0) {
1702 source = sub_source(mpctx);
1703 source_pos = sub_source_pos(mpctx);
1706 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1707 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1708 global_sub_size,
1709 mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1710 mpctx->sub_counts[SUB_SOURCE_SUBS],
1711 mpctx->sub_counts[SUB_SOURCE_DEMUX],
1712 mpctx->global_sub_pos, source);
1714 mpctx->set_of_sub_pos = -1;
1715 mpctx->subdata = NULL;
1717 vobsub_id = -1;
1718 opts->sub_id = -1;
1719 if (d_sub) {
1720 if (d_sub->id > -2)
1721 reset_spu = 1;
1722 d_sub->id = -2;
1724 mpctx->osd->ass_track = NULL;
1725 uninit_player(mpctx, INITIALIZED_SUB);
1727 if (source == SUB_SOURCE_VOBSUB)
1728 vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1729 else if (source == SUB_SOURCE_SUBS) {
1730 mpctx->set_of_sub_pos = source_pos;
1731 #ifdef CONFIG_ASS
1732 if (opts->ass_enabled
1733 && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
1734 mpctx->osd->ass_track =
1735 mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
1736 mpctx->osd->ass_track_changed = true;
1737 mpctx->osd->vsfilter_aspect =
1738 mpctx->track_was_native_ass[mpctx->set_of_sub_pos];
1739 } else
1740 #endif
1742 mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1743 vo_osd_changed(OSDTYPE_SUBTITLE);
1745 } else if (source == SUB_SOURCE_DEMUX) {
1746 opts->sub_id = source_pos;
1747 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1748 int i = 0;
1749 // default: assume 1:1 mapping of sid and stream id
1750 d_sub->id = opts->sub_id;
1751 d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
1752 ds_free_packs(d_sub);
1753 for (i = 0; i < MAX_S_STREAMS; i++) {
1754 sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
1755 if (sh && sh->sid == opts->sub_id) {
1756 d_sub->id = i;
1757 d_sub->sh = sh;
1758 break;
1761 if (d_sub->sh && d_sub->id >= 0) {
1762 sh_sub_t *sh = d_sub->sh;
1763 if (sh->type == 'v')
1764 init_vo_spudec(mpctx);
1765 else {
1766 sub_init(sh, mpctx->osd);
1767 mpctx->initialized_flags |= INITIALIZED_SUB;
1769 } else {
1770 d_sub->id = -2;
1771 d_sub->sh = NULL;
1775 #ifdef CONFIG_DVDREAD
1776 if (vo_spudec
1777 && (mpctx->stream->type == STREAMTYPE_DVD
1778 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1779 && opts->sub_id < 0 && reset_spu) {
1780 d_sub->id = -2;
1781 d_sub->sh = NULL;
1783 #endif
1785 update_subtitles(mpctx, 0, 0, true);
1787 return M_PROPERTY_OK;
1790 /// Selected sub source (RW)
1791 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1792 MPContext *mpctx)
1794 int source;
1795 update_global_sub_size(mpctx);
1796 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1797 return M_PROPERTY_UNAVAILABLE;
1799 switch (action) {
1800 case M_PROPERTY_GET:
1801 if (!arg)
1802 return M_PROPERTY_ERROR;
1803 *(int *) arg = sub_source(mpctx);
1804 return M_PROPERTY_OK;
1805 case M_PROPERTY_PRINT:
1806 if (!arg)
1807 return M_PROPERTY_ERROR;
1808 char *sourcename;
1809 switch (sub_source(mpctx)) {
1810 case SUB_SOURCE_SUBS:
1811 sourcename = mp_gtext("file");
1812 break;
1813 case SUB_SOURCE_VOBSUB:
1814 sourcename = mp_gtext("vobsub");
1815 break;
1816 case SUB_SOURCE_DEMUX:
1817 sourcename = mp_gtext("embedded");
1818 break;
1819 default:
1820 sourcename = mp_gtext("disabled");
1822 *(char **)arg = talloc_strdup(NULL, sourcename);
1823 return M_PROPERTY_OK;
1824 case M_PROPERTY_SET:
1825 if (!arg)
1826 return M_PROPERTY_ERROR;
1827 M_PROPERTY_CLAMP(prop, *(int *)arg);
1828 if (*(int *) arg < 0)
1829 mpctx->global_sub_pos = -1;
1830 else if (*(int *) arg != sub_source(mpctx)) {
1831 int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1832 if (new_pos == -1)
1833 return M_PROPERTY_UNAVAILABLE;
1834 mpctx->global_sub_pos = new_pos;
1836 break;
1837 case M_PROPERTY_STEP_UP:
1838 case M_PROPERTY_STEP_DOWN: {
1839 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1840 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1841 int step = (step_all > 0) ? 1 : -1;
1842 int cur_source = sub_source(mpctx);
1843 source = cur_source;
1844 while (step_all) {
1845 source += step;
1846 if (source >= SUB_SOURCES)
1847 source = -1;
1848 else if (source < -1)
1849 source = SUB_SOURCES - 1;
1850 if (source == cur_source || source == -1 ||
1851 mpctx->sub_counts[source])
1852 step_all -= step;
1854 if (source == cur_source)
1855 return M_PROPERTY_OK;
1856 if (source == -1)
1857 mpctx->global_sub_pos = -1;
1858 else
1859 mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1860 break;
1862 default:
1863 return M_PROPERTY_NOT_IMPLEMENTED;
1865 --mpctx->global_sub_pos;
1866 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1869 /// Selected subtitles from specific source (RW)
1870 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1871 MPContext *mpctx)
1873 int source, is_cur_source, offset, new_pos;
1874 update_global_sub_size(mpctx);
1875 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1876 return M_PROPERTY_UNAVAILABLE;
1878 if (!strcmp(prop->name, "sub_file"))
1879 source = SUB_SOURCE_SUBS;
1880 else if (!strcmp(prop->name, "sub_vob"))
1881 source = SUB_SOURCE_VOBSUB;
1882 else if (!strcmp(prop->name, "sub_demux"))
1883 source = SUB_SOURCE_DEMUX;
1884 else
1885 return M_PROPERTY_ERROR;
1887 offset = sub_pos_by_source(mpctx, source);
1888 if (offset < 0)
1889 return M_PROPERTY_UNAVAILABLE;
1891 is_cur_source = sub_source(mpctx) == source;
1892 new_pos = mpctx->global_sub_pos;
1893 switch (action) {
1894 case M_PROPERTY_GET:
1895 if (!arg)
1896 return M_PROPERTY_ERROR;
1897 if (is_cur_source) {
1898 *(int *) arg = sub_source_pos(mpctx);
1899 if (source == SUB_SOURCE_VOBSUB)
1900 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1901 } else
1902 *(int *) arg = -1;
1903 return M_PROPERTY_OK;
1904 case M_PROPERTY_PRINT:
1905 if (!arg)
1906 return M_PROPERTY_ERROR;
1907 if (is_cur_source)
1908 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1909 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1910 return M_PROPERTY_OK;
1911 case M_PROPERTY_SET:
1912 if (!arg)
1913 return M_PROPERTY_ERROR;
1914 if (*(int *) arg >= 0) {
1915 int index = *(int *)arg;
1916 if (source == SUB_SOURCE_VOBSUB)
1917 index = vobsub_get_index_by_id(vo_vobsub, index);
1918 new_pos = offset + index;
1919 if (index < 0 || index > mpctx->sub_counts[source]) {
1920 new_pos = -1;
1921 *(int *) arg = -1;
1923 } else
1924 new_pos = -1;
1925 break;
1926 case M_PROPERTY_STEP_UP:
1927 case M_PROPERTY_STEP_DOWN: {
1928 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1929 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1930 int step = (step_all > 0) ? 1 : -1;
1931 int max_sub_pos_for_source = -1;
1932 if (!is_cur_source)
1933 new_pos = -1;
1934 while (step_all) {
1935 if (new_pos == -1) {
1936 if (step > 0)
1937 new_pos = offset;
1938 else if (max_sub_pos_for_source == -1) {
1939 // Find max pos for specific source
1940 new_pos = mpctx->global_sub_size - 1;
1941 while (new_pos >= 0 && sub_source(mpctx) != source)
1942 new_pos--;
1943 } else
1944 new_pos = max_sub_pos_for_source;
1945 } else {
1946 new_pos += step;
1947 if (new_pos < offset ||
1948 new_pos >= mpctx->global_sub_size ||
1949 sub_source(mpctx) != source)
1950 new_pos = -1;
1952 step_all -= step;
1954 break;
1956 default:
1957 return M_PROPERTY_NOT_IMPLEMENTED;
1959 return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1962 /// Subtitle delay (RW)
1963 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1964 MPContext *mpctx)
1966 if (!mpctx->sh_video)
1967 return M_PROPERTY_UNAVAILABLE;
1968 return m_property_delay(prop, action, arg, &sub_delay);
1971 /// Alignment of text subtitles (RW)
1972 static int mp_property_sub_alignment(m_option_t *prop, int action,
1973 void *arg, MPContext *mpctx)
1975 char *name[] = {
1976 _("top"), _("center"), _("bottom")
1979 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1980 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1981 return M_PROPERTY_UNAVAILABLE;
1983 switch (action) {
1984 case M_PROPERTY_PRINT:
1985 if (!arg)
1986 return M_PROPERTY_ERROR;
1987 M_PROPERTY_CLAMP(prop, sub_alignment);
1988 *(char **) arg = talloc_strdup(NULL, mp_gtext(name[sub_alignment]));
1989 return M_PROPERTY_OK;
1990 case M_PROPERTY_SET:
1991 if (!arg)
1992 return M_PROPERTY_ERROR;
1993 case M_PROPERTY_STEP_UP:
1994 case M_PROPERTY_STEP_DOWN:
1995 vo_osd_changed(OSDTYPE_SUBTITLE);
1996 default:
1997 return m_property_choice(prop, action, arg, &sub_alignment);
2001 /// Subtitle visibility (RW)
2002 static int mp_property_sub_visibility(m_option_t *prop, int action,
2003 void *arg, MPContext *mpctx)
2005 if (!mpctx->sh_video)
2006 return M_PROPERTY_UNAVAILABLE;
2008 switch (action) {
2009 case M_PROPERTY_SET:
2010 if (!arg)
2011 return M_PROPERTY_ERROR;
2012 case M_PROPERTY_STEP_UP:
2013 case M_PROPERTY_STEP_DOWN:
2014 vo_osd_changed(OSDTYPE_SUBTITLE);
2015 if (vo_spudec)
2016 vo_osd_changed(OSDTYPE_SPU);
2017 default:
2018 return m_property_flag(prop, action, arg, &sub_visibility);
2022 #ifdef CONFIG_ASS
2023 /// Use margins for libass subtitles (RW)
2024 static int mp_property_ass_use_margins(m_option_t *prop, int action,
2025 void *arg, MPContext *mpctx)
2027 struct MPOpts *opts = &mpctx->opts;
2028 if (!mpctx->sh_video)
2029 return M_PROPERTY_UNAVAILABLE;
2031 switch (action) {
2032 case M_PROPERTY_SET:
2033 if (!arg)
2034 return M_PROPERTY_ERROR;
2035 case M_PROPERTY_STEP_UP:
2036 case M_PROPERTY_STEP_DOWN:
2037 mpctx->osd->ass_force_reload = true;
2038 default:
2039 return m_property_flag(prop, action, arg, &opts->ass_use_margins);
2043 static int mp_property_ass_vsfilter_aspect_compat(m_option_t *prop, int action,
2044 void *arg, MPContext *mpctx)
2046 if (!mpctx->sh_video)
2047 return M_PROPERTY_UNAVAILABLE;
2049 switch (action) {
2050 case M_PROPERTY_SET:
2051 if (!arg)
2052 return M_PROPERTY_ERROR;
2053 case M_PROPERTY_STEP_UP:
2054 case M_PROPERTY_STEP_DOWN:
2055 //has to re-render subs with new aspect ratio
2056 mpctx->osd->ass_force_reload = 1;
2057 default:
2058 return m_property_flag(prop, action, arg,
2059 &mpctx->opts.ass_vsfilter_aspect_compat);
2063 #endif
2065 /// Show only forced subtitles (RW)
2066 static int mp_property_sub_forced_only(m_option_t *prop, int action,
2067 void *arg, MPContext *mpctx)
2069 if (!vo_spudec)
2070 return M_PROPERTY_UNAVAILABLE;
2072 switch (action) {
2073 case M_PROPERTY_SET:
2074 if (!arg)
2075 return M_PROPERTY_ERROR;
2076 case M_PROPERTY_STEP_UP:
2077 case M_PROPERTY_STEP_DOWN:
2078 m_property_flag(prop, action, arg, &forced_subs_only);
2079 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
2080 return M_PROPERTY_OK;
2081 default:
2082 return m_property_flag(prop, action, arg, &forced_subs_only);
2087 #ifdef CONFIG_FREETYPE
2088 /// Subtitle scale (RW)
2089 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
2090 MPContext *mpctx)
2092 struct MPOpts *opts = &mpctx->opts;
2094 switch (action) {
2095 case M_PROPERTY_SET:
2096 if (!arg)
2097 return M_PROPERTY_ERROR;
2098 M_PROPERTY_CLAMP(prop, *(float *) arg);
2099 #ifdef CONFIG_ASS
2100 if (opts->ass_enabled) {
2101 opts->ass_font_scale = *(float *) arg;
2102 mpctx->osd->ass_force_reload = true;
2104 #endif
2105 text_font_scale_factor = *(float *) arg;
2106 force_load_font = 1;
2107 vo_osd_changed(OSDTYPE_SUBTITLE);
2108 return M_PROPERTY_OK;
2109 case M_PROPERTY_STEP_UP:
2110 case M_PROPERTY_STEP_DOWN:
2111 #ifdef CONFIG_ASS
2112 if (opts->ass_enabled) {
2113 opts->ass_font_scale += (arg ? *(float *) arg : 0.1) *
2114 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2115 M_PROPERTY_CLAMP(prop, opts->ass_font_scale);
2116 mpctx->osd->ass_force_reload = true;
2118 #endif
2119 text_font_scale_factor += (arg ? *(float *) arg : 0.1) *
2120 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2121 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
2122 force_load_font = 1;
2123 vo_osd_changed(OSDTYPE_SUBTITLE);
2124 return M_PROPERTY_OK;
2125 default:
2126 #ifdef CONFIG_ASS
2127 if (opts->ass_enabled)
2128 return m_property_float_ro(prop, action, arg, opts->ass_font_scale);
2129 else
2130 #endif
2131 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
2134 #endif
2137 #ifdef CONFIG_TV
2139 /// TV color settings (RW)
2140 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
2141 MPContext *mpctx)
2143 int r, val;
2144 tvi_handle_t *tvh = mpctx->demuxer->priv;
2145 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
2146 return M_PROPERTY_UNAVAILABLE;
2148 switch (action) {
2149 case M_PROPERTY_SET:
2150 if (!arg)
2151 return M_PROPERTY_ERROR;
2152 M_PROPERTY_CLAMP(prop, *(int *) arg);
2153 return tv_set_color_options(tvh, prop->offset, *(int *) arg);
2154 case M_PROPERTY_GET:
2155 return tv_get_color_options(tvh, prop->offset, arg);
2156 case M_PROPERTY_STEP_UP:
2157 case M_PROPERTY_STEP_DOWN:
2158 if ((r = tv_get_color_options(tvh, prop->offset, &val)) >= 0) {
2159 if (!r)
2160 return M_PROPERTY_ERROR;
2161 val += (arg ? *(int *) arg : 1) *
2162 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2163 M_PROPERTY_CLAMP(prop, val);
2164 return tv_set_color_options(tvh, prop->offset, val);
2166 return M_PROPERTY_ERROR;
2168 return M_PROPERTY_NOT_IMPLEMENTED;
2171 #endif
2173 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
2174 MPContext *mpctx)
2176 int val, result;
2177 int base_ioctl = prop->offset;
2179 for teletext's GET,SET,STEP ioctls this is not 0
2180 SET is GET+1
2181 STEP is GET+2
2183 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2184 return M_PROPERTY_UNAVAILABLE;
2185 if (!base_ioctl)
2186 return M_PROPERTY_ERROR;
2188 switch (action) {
2189 case M_PROPERTY_GET:
2190 if (!arg)
2191 return M_PROPERTY_ERROR;
2192 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2193 break;
2194 case M_PROPERTY_SET:
2195 if (!arg)
2196 return M_PROPERTY_ERROR;
2197 M_PROPERTY_CLAMP(prop, *(int *) arg);
2198 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2199 arg);
2200 break;
2201 case M_PROPERTY_STEP_UP:
2202 case M_PROPERTY_STEP_DOWN:
2203 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2204 val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2205 -1 : 1);
2206 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2207 &val);
2208 break;
2209 default:
2210 return M_PROPERTY_NOT_IMPLEMENTED;
2213 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2216 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2217 MPContext *mpctx)
2219 int result;
2220 int val;
2222 //with tvh==NULL will fail too
2223 result = mp_property_teletext_common(prop, action, arg, mpctx);
2224 if (result != M_PROPERTY_OK)
2225 return result;
2227 if (teletext_control(mpctx->demuxer->teletext,
2228 prop->offset, &val) == VBI_CONTROL_TRUE && val)
2229 mp_input_set_section(mpctx->input, "teletext");
2230 else
2231 mp_input_set_section(mpctx->input, "tv");
2232 return M_PROPERTY_OK;
2235 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2236 MPContext *mpctx)
2238 int result;
2239 int val;
2240 if (!mpctx->demuxer->teletext)
2241 return M_PROPERTY_UNAVAILABLE;
2242 switch (action) {
2243 case M_PROPERTY_STEP_UP:
2244 case M_PROPERTY_STEP_DOWN:
2245 //This should be handled separately
2246 val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2247 -1 : 1);
2248 result = teletext_control(mpctx->demuxer->teletext,
2249 TV_VBI_CONTROL_STEP_PAGE, &val);
2250 break;
2251 default:
2252 result = mp_property_teletext_common(prop, action, arg, mpctx);
2254 return result;
2258 /// All properties available in MPlayer.
2259 /** \ingroup Properties
2261 static const m_option_t mp_properties[] = {
2262 // General
2263 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2264 M_OPT_RANGE, 0, 3, NULL },
2265 { "loop", mp_property_loop, CONF_TYPE_INT,
2266 M_OPT_MIN, -1, 0, NULL },
2267 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2268 M_OPT_RANGE, 0.01, 100.0, NULL },
2269 { "filename", mp_property_filename, CONF_TYPE_STRING,
2270 0, 0, 0, NULL },
2271 { "path", mp_property_path, CONF_TYPE_STRING,
2272 0, 0, 0, NULL },
2273 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2274 0, 0, 0, NULL },
2275 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2276 M_OPT_MIN, 0, 0, NULL },
2277 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2278 M_OPT_MIN, 0, 0, NULL },
2279 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2280 M_OPT_MIN, 0, 0, NULL },
2281 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2282 M_OPT_MIN, 0, 0, NULL },
2283 { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2284 M_OPT_MIN, 0, 0, NULL },
2285 { "length", mp_property_length, CONF_TYPE_TIME,
2286 M_OPT_MIN, 0, 0, NULL },
2287 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2288 M_OPT_RANGE, 0, 100, NULL },
2289 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2290 M_OPT_MIN, 0, 0, NULL },
2291 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2292 M_OPT_MIN, 0, 0, NULL },
2293 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2294 0, 0, 0, NULL },
2295 { "angle", mp_property_angle, CONF_TYPE_INT,
2296 CONF_RANGE, -2, 10, NULL },
2297 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2298 0, 0, 0, NULL },
2299 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2300 M_OPT_RANGE, 0, 1, NULL },
2301 { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2302 M_OPT_RANGE, 0, 1, NULL },
2303 { "pts_association_mode", mp_property_generic_option, &m_option_type_choice,
2304 0, 0, 0, "pts-association-mode" },
2305 { "hr_seek", mp_property_generic_option, &m_option_type_choice,
2306 0, 0, 0, "hr-seek" },
2308 // Audio
2309 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2310 M_OPT_RANGE, 0, 100, NULL },
2311 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2312 M_OPT_RANGE, 0, 1, NULL },
2313 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2314 M_OPT_RANGE, -100, 100, NULL },
2315 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2316 0, 0, 0, NULL },
2317 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2318 0, 0, 0, NULL },
2319 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2320 0, 0, 0, NULL },
2321 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2322 0, 0, 0, NULL },
2323 { "channels", mp_property_channels, CONF_TYPE_INT,
2324 0, 0, 0, NULL },
2325 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2326 CONF_RANGE, -2, 65535, NULL },
2327 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2328 M_OPT_RANGE, -1, 1, NULL },
2330 // Video
2331 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2332 M_OPT_RANGE, 0, 1, NULL },
2333 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2334 M_OPT_RANGE, 0, 1, NULL },
2335 { "colormatrix", mp_property_colormatrix, &m_option_type_choice,
2336 0, 0, 0, "colormatrix" },
2337 { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice,
2338 0, 0, 0, "colormatrix-input-range" },
2339 { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice,
2340 0, 0, 0, "colormatrix-output-range" },
2341 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2342 M_OPT_RANGE, 0, 1, NULL },
2343 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2344 M_OPT_RANGE, 0, 1, NULL },
2345 { "border", mp_property_border, CONF_TYPE_FLAG,
2346 M_OPT_RANGE, 0, 1, NULL },
2347 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2348 M_OPT_RANGE, 0, 2, NULL },
2349 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2350 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_gamma)},
2351 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2352 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_brightness) },
2353 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2354 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_contrast) },
2355 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2356 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_saturation) },
2357 { "hue", mp_property_gamma, CONF_TYPE_INT,
2358 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_hue) },
2359 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2360 M_OPT_RANGE, 0, 1, NULL },
2361 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2362 M_OPT_RANGE, 0, 1, NULL },
2363 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2364 0, 0, 0, NULL },
2365 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2366 0, 0, 0, NULL },
2367 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2368 0, 0, 0, NULL },
2369 { "width", mp_property_width, CONF_TYPE_INT,
2370 0, 0, 0, NULL },
2371 { "height", mp_property_height, CONF_TYPE_INT,
2372 0, 0, 0, NULL },
2373 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2374 0, 0, 0, NULL },
2375 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2376 0, 0, 0, NULL },
2377 { "switch_video", mp_property_video, CONF_TYPE_INT,
2378 CONF_RANGE, -2, 65535, NULL },
2379 { "switch_program", mp_property_program, CONF_TYPE_INT,
2380 CONF_RANGE, -1, 65535, NULL },
2382 // Subs
2383 { "sub", mp_property_sub, CONF_TYPE_INT,
2384 M_OPT_MIN, -1, 0, NULL },
2385 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2386 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2387 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2388 M_OPT_MIN, -1, 0, NULL },
2389 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2390 M_OPT_MIN, -1, 0, NULL },
2391 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2392 M_OPT_MIN, -1, 0, NULL },
2393 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2394 0, 0, 0, NULL },
2395 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2396 M_OPT_RANGE, 0, 100, NULL },
2397 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2398 M_OPT_RANGE, 0, 2, NULL },
2399 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2400 M_OPT_RANGE, 0, 1, NULL },
2401 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2402 M_OPT_RANGE, 0, 1, NULL },
2403 #ifdef CONFIG_FREETYPE
2404 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2405 M_OPT_RANGE, 0, 100, NULL },
2406 #endif
2407 #ifdef CONFIG_ASS
2408 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2409 M_OPT_RANGE, 0, 1, NULL },
2410 { "ass_vsfilter_aspect_compat", mp_property_ass_vsfilter_aspect_compat,
2411 CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL },
2412 #endif
2414 #ifdef CONFIG_TV
2415 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2416 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS },
2417 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2418 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST },
2419 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2420 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION },
2421 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2422 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
2423 #endif
2424 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2425 M_OPT_RANGE, 100, 899, .offset = TV_VBI_CONTROL_GET_PAGE },
2426 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2427 M_OPT_RANGE, 0, 64, .offset = TV_VBI_CONTROL_GET_SUBPAGE },
2428 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2429 M_OPT_RANGE, 0, 1, .offset = TV_VBI_CONTROL_GET_MODE },
2430 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2431 M_OPT_RANGE, 0, 3, .offset = TV_VBI_CONTROL_GET_FORMAT },
2432 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2433 M_OPT_RANGE, 0, 2, .offset = TV_VBI_CONTROL_GET_HALF_PAGE },
2434 { NULL, NULL, NULL, 0, 0, 0, NULL }
2438 int mp_property_do(const char *name, int action, void *val, void *ctx)
2440 return m_property_do(mp_properties, name, action, val, ctx);
2443 char *mp_property_print(const char *name, void *ctx)
2445 char *ret = NULL;
2446 if (mp_property_do(name, M_PROPERTY_PRINT, &ret, ctx) <= 0)
2447 return NULL;
2448 return ret;
2451 char *property_expand_string(MPContext *mpctx, char *str)
2453 return m_properties_expand_string(mp_properties, str, mpctx);
2456 void property_print_help(void)
2458 m_properties_print_help_list(mp_properties);
2462 /* List of default ways to show a property on OSD.
2464 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2465 * a bar showing the current position between min/max values of the
2466 * property. In this case osd_msg is only used for terminal output
2467 * if there is no video; it'll be a label shown together with percentage.
2469 * Otherwise setting osd_msg will show the string on OSD, formatted with
2470 * the text value of the property as argument.
2472 static struct property_osd_display {
2473 /// property name
2474 const char *name;
2475 /// progressbar type
2476 int osd_progbar; // -1 is special value for seek indicators
2477 /// osd msg id if it must be shared
2478 int osd_id;
2479 /// osd msg template
2480 const char *osd_msg;
2481 } property_osd_display[] = {
2482 // general
2483 { "loop", 0, -1, _("Loop: %s") },
2484 { "chapter", -1, -1, NULL },
2485 { "capturing", 0, -1, _("Capturing: %s") },
2486 { "pts_association_mode", 0, -1, "PTS association mode: %s" },
2487 { "hr_seek", 0, -1, "hr-seek: %s" },
2488 { "speed", 0, -1, _("Speed: x %6s") },
2489 // audio
2490 { "volume", OSD_VOLUME, -1, _("Volume") },
2491 { "mute", 0, -1, _("Mute: %s") },
2492 { "audio_delay", 0, -1, _("A-V delay: %s") },
2493 { "switch_audio", 0, -1, _("Audio: %s") },
2494 { "balance", OSD_BALANCE, -1, _("Balance") },
2495 // video
2496 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2497 { "ontop", 0, -1, _("Stay on top: %s") },
2498 { "rootwin", 0, -1, _("Rootwin: %s") },
2499 { "border", 0, -1, _("Border: %s") },
2500 { "framedropping", 0, -1, _("Framedropping: %s") },
2501 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2502 { "colormatrix", 0, -1, _("YUV colormatrix: %s") },
2503 { "colormatrix_input_range", 0, -1, _("YUV input range: %s") },
2504 { "colormatrix_output_range", 0, -1, _("RGB output range: %s") },
2505 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2506 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2507 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2508 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2509 { "hue", OSD_HUE, -1, _("Hue") },
2510 { "vsync", 0, -1, _("VSync: %s") },
2511 // subs
2512 { "sub", 0, -1, _("Subtitles: %s") },
2513 { "sub_source", 0, -1, _("Sub source: %s") },
2514 { "sub_vob", 0, -1, _("Subtitles: %s") },
2515 { "sub_demux", 0, -1, _("Subtitles: %s") },
2516 { "sub_file", 0, -1, _("Subtitles: %s") },
2517 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2518 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2519 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2520 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2521 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2522 #ifdef CONFIG_FREETYPE
2523 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2524 #endif
2525 { "ass_vsfilter_aspect_compat", 0, -1,
2526 _("Subtitle VSFilter aspect compat: %s")},
2527 #ifdef CONFIG_TV
2528 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2529 { "tv_hue", OSD_HUE, -1, _("Hue") },
2530 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2531 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2532 #endif
2536 static int show_property_osd(MPContext *mpctx, const char *pname)
2538 struct MPOpts *opts = &mpctx->opts;
2539 int r;
2540 m_option_t *prop;
2541 struct property_osd_display *p;
2543 // look for the command
2544 for (p = property_osd_display; p->name; p++)
2545 if (!strcmp(p->name, pname))
2546 break;
2548 if (!p->name)
2549 return -1;
2551 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2552 return -1;
2554 if (p->osd_progbar == -1)
2555 mpctx->add_osd_seek_info = true;
2556 else if (p->osd_progbar) {
2557 if (prop->type == CONF_TYPE_INT) {
2558 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2559 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2560 prop->min, prop->max, r);
2561 } else if (prop->type == CONF_TYPE_FLOAT) {
2562 float f;
2563 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2564 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2565 prop->min, prop->max, f);
2566 } else {
2567 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2568 "Property use an unsupported type.\n");
2569 return -1;
2571 return 0;
2574 if (p->osd_msg) {
2575 char *val = mp_property_print(pname, mpctx);
2576 if (val) {
2577 int index = p - property_osd_display;
2578 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2579 1, opts->osd_duration, p->osd_msg, val);
2580 talloc_free(val);
2583 return 0;
2588 * Command to property bridge
2590 * It is used to handle most commands that just set a property
2591 * and optionally display something on the OSD.
2592 * Two kinds of commands are handled: adjust or toggle.
2594 * Adjust commands take 1 or 2 parameters: <value> <abs>
2595 * If <abs> is non-zero the property is set to the given value
2596 * otherwise it is adjusted.
2598 * Toggle commands take 0 or 1 parameters. With no parameter
2599 * or a value less than the property minimum it just steps the
2600 * property to its next or previous value respectively.
2601 * Otherwise it sets it to the given value.
2604 /// List of the commands that can be handled by setting a property.
2605 static struct {
2606 /// property name
2607 const char *name;
2608 /// cmd id
2609 int cmd;
2610 /// set/adjust or toggle command
2611 int toggle;
2612 } set_prop_cmd[] = {
2613 // general
2614 { "loop", MP_CMD_LOOP, 0},
2615 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2616 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2617 { "pause", MP_CMD_PAUSE, 0},
2618 { "capturing", MP_CMD_CAPTURING, 1},
2619 // audio
2620 { "volume", MP_CMD_VOLUME, 0},
2621 { "mute", MP_CMD_MUTE, 1},
2622 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2623 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2624 { "balance", MP_CMD_BALANCE, 0},
2625 // video
2626 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2627 { "panscan", MP_CMD_PANSCAN, 0},
2628 { "ontop", MP_CMD_VO_ONTOP, 1},
2629 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2630 { "border", MP_CMD_VO_BORDER, 1},
2631 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2632 { "gamma", MP_CMD_GAMMA, 0},
2633 { "brightness", MP_CMD_BRIGHTNESS, 0},
2634 { "contrast", MP_CMD_CONTRAST, 0},
2635 { "saturation", MP_CMD_SATURATION, 0},
2636 { "hue", MP_CMD_HUE, 0},
2637 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2638 // subs
2639 { "sub", MP_CMD_SUB_SELECT, 1},
2640 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2641 { "sub_vob", MP_CMD_SUB_VOB, 1},
2642 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2643 { "sub_file", MP_CMD_SUB_FILE, 1},
2644 { "sub_pos", MP_CMD_SUB_POS, 0},
2645 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2646 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2647 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2648 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2649 #ifdef CONFIG_FREETYPE
2650 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2651 #endif
2652 #ifdef CONFIG_ASS
2653 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2654 #endif
2655 #ifdef CONFIG_TV
2656 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2657 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2658 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2659 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2660 #endif
2664 /// Handle commands that set a property.
2665 static bool set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2667 int i, r;
2668 m_option_t *prop;
2669 const char *pname;
2671 // look for the command
2672 for (i = 0; set_prop_cmd[i].name; i++)
2673 if (set_prop_cmd[i].cmd == cmd->id)
2674 break;
2675 if (!(pname = set_prop_cmd[i].name))
2676 return 0;
2678 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2679 return 0;
2681 // toggle command
2682 if (set_prop_cmd[i].toggle) {
2683 // set to value
2684 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2685 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2686 else if (cmd->nargs > 0)
2687 r = mp_property_do(pname, M_PROPERTY_STEP_DOWN, NULL, mpctx);
2688 else
2689 r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2690 } else if (cmd->args[1].v.i) //set
2691 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2692 else // adjust
2693 r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2695 if (r <= 0)
2696 return 1;
2698 show_property_osd(mpctx, pname);
2700 return 1;
2703 #ifdef CONFIG_DVDNAV
2704 static const struct {
2705 const char *name;
2706 const enum mp_command_type cmd;
2707 } mp_dvdnav_bindings[] = {
2708 { "up", MP_CMD_DVDNAV_UP },
2709 { "down", MP_CMD_DVDNAV_DOWN },
2710 { "left", MP_CMD_DVDNAV_LEFT },
2711 { "right", MP_CMD_DVDNAV_RIGHT },
2712 { "menu", MP_CMD_DVDNAV_MENU },
2713 { "select", MP_CMD_DVDNAV_SELECT },
2714 { "prev", MP_CMD_DVDNAV_PREVMENU },
2715 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2718 * keep old dvdnav sub-command options for a while in order not to
2719 * break slave-mode API too suddenly.
2721 { "1", MP_CMD_DVDNAV_UP },
2722 { "2", MP_CMD_DVDNAV_DOWN },
2723 { "3", MP_CMD_DVDNAV_LEFT },
2724 { "4", MP_CMD_DVDNAV_RIGHT },
2725 { "5", MP_CMD_DVDNAV_MENU },
2726 { "6", MP_CMD_DVDNAV_SELECT },
2727 { "7", MP_CMD_DVDNAV_PREVMENU },
2728 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2729 { NULL, 0 }
2731 #endif
2733 static const char *property_error_string(int error_value)
2735 switch (error_value) {
2736 case M_PROPERTY_ERROR:
2737 return "ERROR";
2738 case M_PROPERTY_UNAVAILABLE:
2739 return "PROPERTY_UNAVAILABLE";
2740 case M_PROPERTY_NOT_IMPLEMENTED:
2741 return "NOT_IMPLEMENTED";
2742 case M_PROPERTY_UNKNOWN:
2743 return "PROPERTY_UNKNOWN";
2744 case M_PROPERTY_DISABLED:
2745 return "DISABLED";
2747 return "UNKNOWN";
2750 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2752 int idx;
2753 int end = start + count;
2754 int after = mpctx->set_of_sub_size - end;
2755 sub_data **subs = mpctx->set_of_subtitles;
2756 #ifdef CONFIG_ASS
2757 struct ass_track **ass_tracks = mpctx->set_of_ass_tracks;
2758 #endif
2759 if (count < 0 || count > mpctx->set_of_sub_size ||
2760 start < 0 || start > mpctx->set_of_sub_size - count) {
2761 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2762 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2763 return;
2765 for (idx = start; idx < end; idx++) {
2766 sub_data *subd = subs[idx];
2767 char *filename = "";
2768 if (subd)
2769 filename = subd->filename;
2770 #ifdef CONFIG_ASS
2771 if (!subd)
2772 filename = ass_tracks[idx]->name;
2773 #endif
2774 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2775 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2776 filename_recode(filename));
2777 sub_free(subd);
2778 subs[idx] = NULL;
2779 #ifdef CONFIG_ASS
2780 if (ass_tracks[idx])
2781 ass_free_track(ass_tracks[idx]);
2782 ass_tracks[idx] = NULL;
2783 #endif
2786 mpctx->global_sub_size -= count;
2787 mpctx->set_of_sub_size -= count;
2788 if (mpctx->set_of_sub_size <= 0)
2789 mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2791 memmove(subs + start, subs + end, after * sizeof(*subs));
2792 memset(subs + start + after, 0, count * sizeof(*subs));
2793 #ifdef CONFIG_ASS
2794 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2795 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2796 #endif
2798 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2799 mpctx->global_sub_pos = -2;
2800 mpctx->subdata = NULL;
2801 mpctx->osd->ass_track = NULL;
2802 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2803 } else if (mpctx->set_of_sub_pos >= end) {
2804 mpctx->set_of_sub_pos -= count;
2805 mpctx->global_sub_pos -= count;
2809 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2811 struct MPOpts *opts = &mpctx->opts;
2812 sh_audio_t *const sh_audio = mpctx->sh_audio;
2813 sh_video_t *const sh_video = mpctx->sh_video;
2814 int osd_duration = opts->osd_duration;
2815 int case_fallthrough_hack = 0;
2816 if (set_property_command(mpctx, cmd))
2817 goto old_pause_hack; // was handled already
2818 switch (cmd->id) {
2819 case MP_CMD_SEEK: {
2820 mpctx->add_osd_seek_info = true;
2821 float v = cmd->args[0].v.f;
2822 int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2823 int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0;
2824 if (abs == 2) { // Absolute seek to a timestamp in seconds
2825 queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact);
2826 mpctx->osd_function = v > get_current_time(mpctx) ?
2827 OSD_FFW : OSD_REW;
2828 } else if (abs) { /* Absolute seek by percentage */
2829 queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact);
2830 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2831 } else {
2832 queue_seek(mpctx, MPSEEK_RELATIVE, v, exact);
2833 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2835 break;
2838 case MP_CMD_SET_PROPERTY_OSD:
2839 case_fallthrough_hack = 1;
2841 case MP_CMD_SET_PROPERTY: {
2842 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2843 cmd->args[1].v.s, mpctx);
2844 if (r == M_PROPERTY_UNKNOWN)
2845 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2846 "Unknown property: '%s'\n", cmd->args[0].v.s);
2847 else if (r <= 0)
2848 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2849 "Failed to set property '%s' to '%s'.\n",
2850 cmd->args[0].v.s, cmd->args[1].v.s);
2851 else if (case_fallthrough_hack)
2852 show_property_osd(mpctx, cmd->args[0].v.s);
2853 if (r <= 0)
2854 mp_msg(MSGT_GLOBAL, MSGL_INFO,
2855 "ANS_ERROR=%s\n", property_error_string(r));
2856 break;
2859 case MP_CMD_STEP_PROPERTY_OSD:
2860 case_fallthrough_hack = 1;
2862 case MP_CMD_STEP_PROPERTY: {
2863 void *arg = NULL;
2864 int r, i;
2865 double d;
2866 off_t o;
2867 if (cmd->args[1].v.f) {
2868 m_option_t *prop;
2869 if ((r = mp_property_do(cmd->args[0].v.s,
2870 M_PROPERTY_GET_TYPE,
2871 &prop, mpctx)) <= 0)
2872 goto step_prop_err;
2873 if (prop->type == CONF_TYPE_INT ||
2874 prop->type == CONF_TYPE_FLAG)
2875 i = cmd->args[1].v.f, arg = &i;
2876 else if (prop->type == CONF_TYPE_FLOAT)
2877 arg = &cmd->args[1].v.f;
2878 else if (prop->type == CONF_TYPE_DOUBLE ||
2879 prop->type == CONF_TYPE_TIME)
2880 d = cmd->args[1].v.f, arg = &d;
2881 else if (prop->type == CONF_TYPE_POSITION)
2882 o = cmd->args[1].v.f, arg = &o;
2883 else
2884 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2885 "Ignoring step size stepping property '%s'.\n",
2886 cmd->args[0].v.s);
2888 r = mp_property_do(cmd->args[0].v.s,
2889 cmd->args[2].v.i < 0 ?
2890 M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2891 arg, mpctx);
2892 step_prop_err:
2893 if (r == M_PROPERTY_UNKNOWN)
2894 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2895 "Unknown property: '%s'\n", cmd->args[0].v.s);
2896 else if (r <= 0)
2897 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2898 "Failed to increment property '%s' by %f.\n",
2899 cmd->args[0].v.s, cmd->args[1].v.f);
2900 else if (case_fallthrough_hack)
2901 show_property_osd(mpctx, cmd->args[0].v.s);
2902 if (r <= 0)
2903 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2904 property_error_string(r));
2905 break;
2908 case MP_CMD_GET_PROPERTY: {
2909 char *tmp;
2910 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2911 &tmp, mpctx);
2912 if (r <= 0) {
2913 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2914 "Failed to get value of property '%s'.\n",
2915 cmd->args[0].v.s);
2916 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2917 property_error_string(r));
2918 break;
2920 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2921 cmd->args[0].v.s, tmp);
2922 talloc_free(tmp);
2923 break;
2926 case MP_CMD_EDL_MARK:
2927 if (edl_fd) {
2928 float v = get_current_time(mpctx);
2929 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2930 mpctx->begin_skip = v;
2931 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2932 "EDL skip start, press 'i' again to end block.\n");
2933 } else {
2934 if (mpctx->begin_skip > v)
2935 mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
2936 "EDL skip canceled, last start > stop\n");
2937 else {
2938 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2939 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2940 "EDL skip end, line written.\n");
2942 mpctx->begin_skip = MP_NOPTS_VALUE;
2945 break;
2947 case MP_CMD_SWITCH_RATIO:
2948 if (!sh_video)
2949 break;
2950 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2951 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2952 else
2953 opts->movie_aspect = cmd->args[0].v.f;
2954 video_reset_aspect(sh_video);
2955 break;
2957 case MP_CMD_SPEED_INCR: {
2958 float v = cmd->args[0].v.f;
2959 mp_property_do("speed", M_PROPERTY_STEP_UP, &v, mpctx);
2960 show_property_osd(mpctx, "speed");
2961 break;
2964 case MP_CMD_SPEED_MULT:
2965 case_fallthrough_hack = true;
2967 case MP_CMD_SPEED_SET: {
2968 float v = cmd->args[0].v.f;
2969 if (case_fallthrough_hack)
2970 v *= mpctx->opts.playback_speed;
2971 mp_property_do("speed", M_PROPERTY_SET, &v, mpctx);
2972 show_property_osd(mpctx, "speed");
2973 break;
2976 case MP_CMD_FRAME_STEP:
2977 add_step_frame(mpctx);
2978 break;
2980 case MP_CMD_QUIT:
2981 exit_player_with_rc(mpctx, EXIT_QUIT,
2982 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2984 case MP_CMD_PLAY_TREE_STEP: {
2985 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2986 int force = cmd->args[1].v.i;
2989 if (!force && mpctx->playtree_iter) {
2990 play_tree_iter_t *i =
2991 play_tree_iter_new_copy(mpctx->playtree_iter);
2992 if (play_tree_iter_step(i, n, 0) ==
2993 PLAY_TREE_ITER_ENTRY)
2994 mpctx->stop_play =
2995 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2996 play_tree_iter_free(i);
2997 } else
2998 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2999 if (mpctx->stop_play)
3000 mpctx->play_tree_step = n;
3002 break;
3005 case MP_CMD_PLAY_TREE_UP_STEP: {
3006 int n = cmd->args[0].v.i > 0 ? 1 : -1;
3007 int force = cmd->args[1].v.i;
3009 if (!force && mpctx->playtree_iter) {
3010 play_tree_iter_t *i =
3011 play_tree_iter_new_copy(mpctx->playtree_iter);
3012 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
3013 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3014 play_tree_iter_free(i);
3015 } else
3016 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
3017 break;
3020 case MP_CMD_PLAY_ALT_SRC_STEP:
3021 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
3022 int v = cmd->args[0].v.i;
3023 if (v > 0
3024 && mpctx->playtree_iter->file <
3025 mpctx->playtree_iter->num_files)
3026 mpctx->stop_play = PT_NEXT_SRC;
3027 else if (v < 0 && mpctx->playtree_iter->file > 1)
3028 mpctx->stop_play = PT_PREV_SRC;
3030 break;
3032 case MP_CMD_SUB_STEP:
3033 if (sh_video) {
3034 int movement = cmd->args[0].v.i;
3035 step_sub(mpctx->subdata, mpctx->video_pts, movement);
3036 #ifdef CONFIG_ASS
3037 if (mpctx->osd->ass_track)
3038 sub_delay +=
3039 ass_step_sub(mpctx->osd->ass_track,
3040 (mpctx->video_pts +
3041 sub_delay) * 1000 + .5, movement) / 1000.;
3042 #endif
3043 set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration,
3044 "Sub delay: %d ms", ROUND(sub_delay * 1000));
3046 break;
3048 case MP_CMD_SUB_LOG:
3049 log_sub(mpctx);
3050 break;
3052 case MP_CMD_OSD: {
3053 int v = cmd->args[0].v.i;
3054 int max = (opts->term_osd
3055 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
3056 if (opts->osd_level > max)
3057 opts->osd_level = max;
3058 if (v < 0)
3059 opts->osd_level = (opts->osd_level + 1) % (max + 1);
3060 else
3061 opts->osd_level = v > max ? max : v;
3062 /* Show OSD state when disabled, but not when an explicit
3063 argument is given to the OSD command, i.e. in slave mode. */
3064 if (v == -1 && opts->osd_level <= 1)
3065 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
3066 "OSD: %s",
3067 opts->osd_level ? mp_gtext("enabled") :
3068 mp_gtext("disabled"));
3069 else
3070 rm_osd_msg(OSD_MSG_OSD_STATUS);
3071 break;
3074 case MP_CMD_OSD_SHOW_TEXT:
3075 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3076 (cmd->args[1].v.i <
3077 0 ? osd_duration : cmd->args[1].v.i),
3078 "%s", cmd->args[0].v.s);
3079 break;
3081 case MP_CMD_OSD_SHOW_PROPERTY_TEXT: {
3082 char *txt = m_properties_expand_string(mp_properties,
3083 cmd->args[0].v.s,
3084 mpctx);
3085 // if no argument supplied use default osd_duration, else <arg> ms.
3086 if (txt) {
3087 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3088 (cmd->args[1].v.i <
3089 0 ? osd_duration : cmd->args[1].v.i),
3090 "%s", txt);
3091 free(txt);
3093 break;
3096 case MP_CMD_LOADFILE: {
3097 play_tree_t *e = play_tree_new();
3098 play_tree_add_file(e, cmd->args[0].v.s);
3100 if (cmd->args[1].v.i) // append
3101 play_tree_append_entry(mpctx->playtree->child, e);
3102 else {
3103 // Go back to the starting point.
3104 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3105 != PLAY_TREE_ITER_END)
3106 /* NOP */;
3107 play_tree_free_list(mpctx->playtree->child, 1);
3108 play_tree_set_child(mpctx->playtree, e);
3109 pt_iter_goto_head(mpctx->playtree_iter);
3110 mpctx->stop_play = PT_NEXT_SRC;
3112 break;
3115 case MP_CMD_LOADLIST: {
3116 play_tree_t *e = parse_playlist_file(mpctx->mconfig,
3117 bstr(cmd->args[0].v.s));
3118 if (!e)
3119 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
3120 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
3121 else {
3122 if (cmd->args[1].v.i) // append
3123 play_tree_append_entry(mpctx->playtree->child, e);
3124 else {
3125 // Go back to the starting point.
3126 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3127 != PLAY_TREE_ITER_END)
3128 /* NOP */;
3129 play_tree_free_list(mpctx->playtree->child, 1);
3130 play_tree_set_child(mpctx->playtree, e);
3131 pt_iter_goto_head(mpctx->playtree_iter);
3132 mpctx->stop_play = PT_NEXT_SRC;
3135 break;
3138 case MP_CMD_STOP:
3139 // Go back to the starting point.
3140 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
3141 PLAY_TREE_ITER_END)
3142 /* NOP */;
3143 mpctx->stop_play = PT_STOP;
3144 break;
3146 case MP_CMD_OSD_SHOW_PROGRESSION: {
3147 int len = get_time_length(mpctx);
3148 int pts = get_current_time(mpctx);
3149 set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
3150 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3151 "%c %02d:%02d:%02d / %02d:%02d:%02d",
3152 mpctx->osd_function, pts / 3600, (pts / 60) % 60, pts % 60,
3153 len / 3600, (len / 60) % 60, len % 60);
3154 break;
3157 #ifdef CONFIG_RADIO
3158 case MP_CMD_RADIO_STEP_CHANNEL:
3159 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3160 int v = cmd->args[0].v.i;
3161 if (v > 0)
3162 radio_step_channel(mpctx->demuxer->stream,
3163 RADIO_CHANNEL_HIGHER);
3164 else
3165 radio_step_channel(mpctx->demuxer->stream,
3166 RADIO_CHANNEL_LOWER);
3167 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3168 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3169 "Channel: %s",
3170 radio_get_channel_name(mpctx->demuxer->stream));
3173 break;
3175 case MP_CMD_RADIO_SET_CHANNEL:
3176 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3177 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
3178 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3179 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3180 "Channel: %s",
3181 radio_get_channel_name(mpctx->demuxer->stream));
3184 break;
3186 case MP_CMD_RADIO_SET_FREQ:
3187 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3188 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3189 break;
3191 case MP_CMD_RADIO_STEP_FREQ:
3192 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3193 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3194 break;
3195 #endif
3197 #ifdef CONFIG_TV
3198 case MP_CMD_TV_START_SCAN:
3199 if (mpctx->file_format == DEMUXER_TYPE_TV)
3200 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv), 1);
3201 break;
3202 case MP_CMD_TV_SET_FREQ:
3203 if (mpctx->file_format == DEMUXER_TYPE_TV)
3204 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3205 cmd->args[0].v.f * 16.0);
3206 #ifdef CONFIG_PVR
3207 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3208 pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f));
3209 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3210 pvr_get_current_channelname(mpctx->stream),
3211 pvr_get_current_stationname(mpctx->stream));
3213 #endif /* CONFIG_PVR */
3214 break;
3216 case MP_CMD_TV_STEP_FREQ:
3217 if (mpctx->file_format == DEMUXER_TYPE_TV)
3218 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3219 cmd->args[0].v.f * 16.0);
3220 #ifdef CONFIG_PVR
3221 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3222 pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f));
3223 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3224 pvr_get_current_channelname(mpctx->stream),
3225 pvr_get_current_frequency(mpctx->stream));
3227 #endif /* CONFIG_PVR */
3228 break;
3230 case MP_CMD_TV_SET_NORM:
3231 if (mpctx->file_format == DEMUXER_TYPE_TV)
3232 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3233 cmd->args[0].v.s);
3234 break;
3236 case MP_CMD_TV_STEP_CHANNEL:
3237 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3238 int v = cmd->args[0].v.i;
3239 if (v > 0) {
3240 tv_step_channel((tvi_handle_t *) (mpctx->
3241 demuxer->priv),
3242 TV_CHANNEL_HIGHER);
3243 } else {
3244 tv_step_channel((tvi_handle_t *) (mpctx->
3245 demuxer->priv),
3246 TV_CHANNEL_LOWER);
3248 if (tv_channel_list) {
3249 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3250 "Channel: %s", tv_channel_current->name);
3251 //vo_osd_changed(OSDTYPE_SUBTITLE);
3254 #ifdef CONFIG_PVR
3255 else if (mpctx->stream &&
3256 mpctx->stream->type == STREAMTYPE_PVR) {
3257 pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i);
3258 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3259 pvr_get_current_channelname(mpctx->stream),
3260 pvr_get_current_stationname(mpctx->stream));
3262 #endif /* CONFIG_PVR */
3263 #ifdef CONFIG_DVBIN
3264 if (mpctx->stream->type == STREAMTYPE_DVB) {
3265 int dir;
3266 int v = cmd->args[0].v.i;
3268 mpctx->last_dvb_step = v;
3269 if (v > 0)
3270 dir = DVB_CHANNEL_HIGHER;
3271 else
3272 dir = DVB_CHANNEL_LOWER;
3275 if (dvb_step_channel(mpctx->stream, dir)) {
3276 mpctx->stop_play = PT_NEXT_ENTRY;
3277 mpctx->dvbin_reopen = 1;
3280 #endif /* CONFIG_DVBIN */
3281 break;
3283 case MP_CMD_TV_SET_CHANNEL:
3284 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3285 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3286 cmd->args[0].v.s);
3287 if (tv_channel_list) {
3288 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3289 "Channel: %s", tv_channel_current->name);
3290 //vo_osd_changed(OSDTYPE_SUBTITLE);
3293 #ifdef CONFIG_PVR
3294 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3295 pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
3296 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3297 pvr_get_current_channelname(mpctx->stream),
3298 pvr_get_current_stationname(mpctx->stream));
3300 #endif /* CONFIG_PVR */
3301 break;
3303 #ifdef CONFIG_DVBIN
3304 case MP_CMD_DVB_SET_CHANNEL:
3305 if (mpctx->stream->type == STREAMTYPE_DVB) {
3306 mpctx->last_dvb_step = 1;
3308 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3309 cmd->args[0].v.i)) {
3310 mpctx->stop_play = PT_NEXT_ENTRY;
3311 mpctx->dvbin_reopen = 1;
3314 break;
3315 #endif /* CONFIG_DVBIN */
3317 case MP_CMD_TV_LAST_CHANNEL:
3318 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3319 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3320 if (tv_channel_list) {
3321 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3322 "Channel: %s", tv_channel_current->name);
3323 //vo_osd_changed(OSDTYPE_SUBTITLE);
3326 #ifdef CONFIG_PVR
3327 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3328 pvr_set_lastchannel(mpctx->stream);
3329 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3330 pvr_get_current_channelname(mpctx->stream),
3331 pvr_get_current_stationname(mpctx->stream));
3333 #endif /* CONFIG_PVR */
3334 break;
3336 case MP_CMD_TV_STEP_NORM:
3337 if (mpctx->file_format == DEMUXER_TYPE_TV)
3338 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3339 break;
3341 case MP_CMD_TV_STEP_CHANNEL_LIST:
3342 if (mpctx->file_format == DEMUXER_TYPE_TV)
3343 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3344 break;
3345 #endif /* CONFIG_TV */
3346 case MP_CMD_TV_TELETEXT_ADD_DEC:
3347 if (mpctx->demuxer->teletext)
3348 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_ADD_DEC,
3349 &(cmd->args[0].v.s));
3350 break;
3351 case MP_CMD_TV_TELETEXT_GO_LINK:
3352 if (mpctx->demuxer->teletext)
3353 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_GO_LINK,
3354 &(cmd->args[0].v.i));
3355 break;
3357 case MP_CMD_SUB_LOAD:
3358 if (sh_video) {
3359 int n = mpctx->set_of_sub_size;
3360 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3361 if (n != mpctx->set_of_sub_size) {
3362 mpctx->sub_counts[SUB_SOURCE_SUBS]++;
3363 ++mpctx->global_sub_size;
3366 break;
3368 case MP_CMD_SUB_REMOVE:
3369 if (sh_video) {
3370 int v = cmd->args[0].v.i;
3371 if (v < 0)
3372 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3373 else if (v < mpctx->set_of_sub_size)
3374 remove_subtitle_range(mpctx, v, 1);
3376 break;
3378 case MP_CMD_GET_SUB_VISIBILITY:
3379 if (sh_video) {
3380 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3381 "ANS_SUB_VISIBILITY=%d\n", sub_visibility);
3383 break;
3385 case MP_CMD_SCREENSHOT:
3386 screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i);
3387 break;
3389 case MP_CMD_VF_CHANGE_RECTANGLE:
3390 if (!sh_video)
3391 break;
3392 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3393 break;
3395 case MP_CMD_GET_TIME_LENGTH:
3396 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3397 get_time_length(mpctx));
3398 break;
3400 case MP_CMD_GET_FILENAME: {
3401 char *inf = get_metadata(mpctx, META_NAME);
3402 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n", inf);
3403 talloc_free(inf);
3404 break;
3407 case MP_CMD_GET_VIDEO_CODEC: {
3408 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3409 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3410 talloc_free(inf);
3411 break;
3414 case MP_CMD_GET_VIDEO_BITRATE: {
3415 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3416 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3417 talloc_free(inf);
3418 break;
3421 case MP_CMD_GET_VIDEO_RESOLUTION: {
3422 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3423 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3424 talloc_free(inf);
3425 break;
3428 case MP_CMD_GET_AUDIO_CODEC: {
3429 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3430 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3431 talloc_free(inf);
3432 break;
3435 case MP_CMD_GET_AUDIO_BITRATE: {
3436 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3437 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3438 talloc_free(inf);
3439 break;
3442 case MP_CMD_GET_AUDIO_SAMPLES: {
3443 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3444 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3445 talloc_free(inf);
3446 break;
3449 case MP_CMD_GET_META_TITLE: {
3450 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3451 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3452 talloc_free(inf);
3453 break;
3456 case MP_CMD_GET_META_ARTIST: {
3457 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3458 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3459 talloc_free(inf);
3460 break;
3463 case MP_CMD_GET_META_ALBUM: {
3464 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3465 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3466 talloc_free(inf);
3467 break;
3470 case MP_CMD_GET_META_YEAR: {
3471 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3472 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3473 talloc_free(inf);
3474 break;
3477 case MP_CMD_GET_META_COMMENT: {
3478 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3479 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3480 talloc_free(inf);
3481 break;
3484 case MP_CMD_GET_META_TRACK: {
3485 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3486 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3487 talloc_free(inf);
3488 break;
3491 case MP_CMD_GET_META_GENRE: {
3492 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3493 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3494 talloc_free(inf);
3495 break;
3498 case MP_CMD_GET_VO_FULLSCREEN:
3499 if (mpctx->video_out && mpctx->video_out->config_ok)
3500 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3501 break;
3503 case MP_CMD_GET_PERCENT_POS:
3504 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3505 get_percent_pos(mpctx));
3506 break;
3508 case MP_CMD_GET_TIME_POS: {
3509 float pos = get_current_time(mpctx);
3510 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3511 break;
3514 case MP_CMD_RUN:
3515 #ifndef __MINGW32__
3516 if (!fork()) {
3517 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3518 exit(0);
3520 #endif
3521 break;
3523 case MP_CMD_KEYDOWN_EVENTS:
3524 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3525 break;
3527 case MP_CMD_SET_MOUSE_POS: {
3528 int pointer_x, pointer_y;
3529 double dx, dy;
3530 pointer_x = cmd->args[0].v.i;
3531 pointer_y = cmd->args[1].v.i;
3532 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3533 #ifdef CONFIG_DVDNAV
3534 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3535 && dx > 0.0 && dy > 0.0) {
3536 int button = -1;
3537 pointer_x = (int) (dx * (double) sh_video->disp_w);
3538 pointer_y = (int) (dy * (double) sh_video->disp_h);
3539 mp_dvdnav_update_mouse_pos(mpctx->stream,
3540 pointer_x, pointer_y, &button);
3541 if (opts->osd_level > 1 && button > 0)
3542 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3543 "Selected button number %d", button);
3545 #endif
3546 break;
3549 #ifdef CONFIG_DVDNAV
3550 case MP_CMD_DVDNAV: {
3551 int button = -1;
3552 int i;
3553 enum mp_command_type command = 0;
3554 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3555 break;
3557 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3558 if (cmd->args[0].v.s &&
3559 !strcasecmp(cmd->args[0].v.s,
3560 mp_dvdnav_bindings[i].name))
3561 command = mp_dvdnav_bindings[i].cmd;
3563 mp_dvdnav_handle_input(mpctx->stream, command, &button);
3564 if (opts->osd_level > 1 && button > 0)
3565 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3566 "Selected button number %d", button);
3567 break;
3570 case MP_CMD_SWITCH_TITLE:
3571 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3572 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3573 break;
3575 #endif
3577 case MP_CMD_AF_SWITCH:
3578 if (sh_audio) {
3579 af_uninit(mpctx->mixer.afilter);
3580 af_init(mpctx->mixer.afilter);
3582 case MP_CMD_AF_ADD:
3583 case MP_CMD_AF_DEL: {
3584 if (!sh_audio)
3585 break;
3586 char *af_args = strdup(cmd->args[0].v.s);
3587 char *af_commands = af_args;
3588 char *af_command;
3589 af_instance_t *af;
3590 while ((af_command = strsep(&af_commands, ",")) != NULL) {
3591 if (cmd->id == MP_CMD_AF_DEL) {
3592 af = af_get(mpctx->mixer.afilter, af_command);
3593 if (af != NULL)
3594 af_remove(mpctx->mixer.afilter, af);
3595 } else
3596 af_add(mpctx->mixer.afilter, af_command);
3598 reinit_audio_chain(mpctx);
3599 free(af_args);
3600 break;
3602 case MP_CMD_AF_CLR:
3603 if (!sh_audio)
3604 break;
3605 af_uninit(mpctx->mixer.afilter);
3606 af_init(mpctx->mixer.afilter);
3607 reinit_audio_chain(mpctx);
3608 break;
3609 case MP_CMD_AF_CMDLINE:
3610 if (sh_audio) {
3611 af_instance_t *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
3612 if (!af) {
3613 mp_msg(MSGT_CPLAYER, MSGL_WARN,
3614 "Filter '%s' not found in chain.\n", cmd->args[0].v.s);
3615 break;
3617 af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
3618 af_reinit(sh_audio->afilter, af);
3620 break;
3622 default:
3623 mp_msg(MSGT_CPLAYER, MSGL_V,
3624 "Received unknown cmd %s\n", cmd->name);
3627 old_pause_hack:
3628 switch (cmd->pausing) {
3629 case 1: // "pausing"
3630 pause_player(mpctx);
3631 break;
3632 case 3: // "pausing_toggle"
3633 if (mpctx->paused)
3634 unpause_player(mpctx);
3635 else
3636 pause_player(mpctx);
3637 break;