subreader: fix unsafe sscanf calls with "%["
[mplayer.git] / command.c
blobae796693d8b78f6655c8006542305de949be75dd
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 "playtree.h"
46 #include "libao2/audio_out.h"
47 #include "mpcommon.h"
48 #include "mixer.h"
49 #include "libmpcodecs/dec_video.h"
50 #include "libmpcodecs/dec_audio.h"
51 #include "libmpcodecs/dec_teletext.h"
52 #include "osdep/strsep.h"
53 #include "sub/vobsub.h"
54 #include "sub/spudec.h"
55 #include "path.h"
56 #include "sub/ass_mp.h"
57 #include "stream/tv.h"
58 #include "stream/stream_radio.h"
59 #include "stream/pvr.h"
60 #ifdef CONFIG_DVBIN
61 #include "stream/dvbin.h"
62 #endif
63 #ifdef CONFIG_DVDREAD
64 #include "stream/stream_dvd.h"
65 #endif
66 #include "stream/stream_dvdnav.h"
67 #include "m_struct.h"
68 #include "screenshot.h"
70 #include "mp_core.h"
71 #include "mp_fifo.h"
72 #include "libavutil/avstring.h"
74 static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
75 double *dx, double *dy)
77 struct MPOpts *opts = &mpctx->opts;
78 struct vo *vo = mpctx->video_out;
79 //remove the borders, if any, and rescale to the range [0,1],[0,1]
80 if (vo_fs) { //we are in full-screen mode
81 if (opts->vo_screenwidth > vo->dwidth)
82 // there are borders along the x axis
83 ix -= (opts->vo_screenwidth - vo->dwidth) / 2;
84 if (opts->vo_screenheight > vo->dheight)
85 // there are borders along the y axis (usual way)
86 iy -= (opts->vo_screenheight - vo->dheight) / 2;
88 if (ix < 0 || ix > vo->dwidth) {
89 *dx = *dy = -1.0;
90 return;
91 } //we are on one of the borders
92 if (iy < 0 || iy > vo->dheight) {
93 *dx = *dy = -1.0;
94 return;
95 } //we are on one of the borders
98 *dx = (double) ix / (double) vo->dwidth;
99 *dy = (double) iy / (double) vo->dheight;
101 mp_msg(MSGT_CPLAYER, MSGL_V,
102 "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
103 *dx, *dy, opts->vo_screenwidth, opts->vo_screenheight, vo->dwidth,
104 vo->dheight, vo_fs);
107 static int sub_pos_by_source(MPContext *mpctx, int src)
109 int i, cnt = 0;
110 if (src >= SUB_SOURCES || mpctx->sub_counts[src] == 0)
111 return -1;
112 for (i = 0; i < src; i++)
113 cnt += mpctx->sub_counts[i];
114 return cnt;
117 static int sub_source_and_index_by_pos(MPContext *mpctx, int *pos)
119 int start = 0;
120 int i;
121 for (i = 0; i < SUB_SOURCES; i++) {
122 int cnt = mpctx->sub_counts[i];
123 if (*pos >= start && *pos < start + cnt) {
124 *pos -= start;
125 return i;
127 start += cnt;
129 *pos = -1;
130 return -1;
133 static int sub_source_by_pos(MPContext *mpctx, int pos)
135 return sub_source_and_index_by_pos(mpctx, &pos);
138 static int sub_source_pos(MPContext *mpctx)
140 int pos = mpctx->global_sub_pos;
141 sub_source_and_index_by_pos(mpctx, &pos);
142 return pos;
145 static int sub_source(MPContext *mpctx)
147 return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
150 static void update_global_sub_size(MPContext *mpctx)
152 struct MPOpts *opts = &mpctx->opts;
153 int i;
154 int cnt = 0;
156 // update number of demuxer sub streams
157 for (i = 0; i < MAX_S_STREAMS; i++)
158 if (mpctx->d_sub->demuxer->s_streams[i])
159 cnt++;
160 if (cnt > mpctx->sub_counts[SUB_SOURCE_DEMUX])
161 mpctx->sub_counts[SUB_SOURCE_DEMUX] = cnt;
163 // update global size
164 mpctx->global_sub_size = 0;
165 for (i = 0; i < SUB_SOURCES; i++)
166 mpctx->global_sub_size += mpctx->sub_counts[i];
168 // update global_sub_pos if we auto-detected a demuxer sub
169 if (mpctx->global_sub_pos == -1) {
170 int sub_id = -1;
171 if (mpctx->demuxer->sub)
172 sub_id = mpctx->demuxer->sub->id;
173 if (sub_id < 0)
174 sub_id = opts->sub_id;
175 if (sub_id >= 0 && sub_id < mpctx->sub_counts[SUB_SOURCE_DEMUX])
176 mpctx->global_sub_pos = sub_pos_by_source(mpctx, SUB_SOURCE_DEMUX) +
177 sub_id;
182 * \brief Log the currently displayed subtitle to a file
184 * Logs the current or last displayed subtitle together with filename
185 * and time information to ~/.mplayer/subtitle_log
187 * Intended purpose is to allow convenient marking of bogus subtitles
188 * which need to be fixed while watching the movie.
191 static void log_sub(struct MPContext *mpctx)
193 char *fname;
194 FILE *f;
195 int i;
196 struct subtitle *vo_sub_last = mpctx->vo_sub_last;
198 if (mpctx->subdata == NULL || vo_sub_last == NULL)
199 return;
200 fname = get_path("subtitle_log");
201 f = fopen(fname, "a");
202 if (!f)
203 return;
204 fprintf(f, "----------------------------------------------------------\n");
205 if (mpctx->subdata->sub_uses_time) {
206 fprintf(f,
207 "N: %s S: %02ld:%02ld:%02ld.%02ld E: %02ld:%02ld:%02ld.%02ld\n",
208 mpctx->filename, vo_sub_last->start / 360000,
209 (vo_sub_last->start / 6000) % 60,
210 (vo_sub_last->start / 100) % 60, vo_sub_last->start % 100,
211 vo_sub_last->end / 360000, (vo_sub_last->end / 6000) % 60,
212 (vo_sub_last->end / 100) % 60, vo_sub_last->end % 100);
213 } else {
214 fprintf(f, "N: %s S: %ld E: %ld\n", mpctx->filename,
215 vo_sub_last->start, vo_sub_last->end);
217 for (i = 0; i < vo_sub_last->lines; i++)
218 fprintf(f, "%s\n", vo_sub_last->text[i]);
219 fclose(f);
223 static int mp_property_generic_option(struct m_option *prop, int action,
224 void *arg, MPContext *mpctx)
226 char *optname = prop->priv;
227 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
228 bstr(optname));
229 void *valptr = m_option_get_ptr(opt, &mpctx->opts);
231 switch (action) {
232 case M_PROPERTY_GET_TYPE:
233 *(const struct m_option **)arg = opt;
234 return M_PROPERTY_OK;
235 case M_PROPERTY_GET:
236 m_option_copy(opt, arg, valptr);
237 return M_PROPERTY_OK;
238 case M_PROPERTY_SET:
239 m_option_copy(opt, valptr, arg);
240 return M_PROPERTY_OK;
241 case M_PROPERTY_STEP:
242 if (opt->type == &m_option_type_choice) {
243 int v = *(int *) valptr;
244 int best = v;
245 struct m_opt_choice_alternatives *alt;
246 for (alt = opt->priv; alt->name; alt++)
247 if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1)
248 best = alt->value;
249 *(int *) valptr = best;
250 return M_PROPERTY_OK;
252 break;
254 return M_PROPERTY_NOT_IMPLEMENTED;
257 /// OSD level (RW)
258 static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
259 MPContext *mpctx)
261 return m_property_choice(prop, action, arg, &mpctx->opts.osd_level);
264 /// Loop (RW)
265 static int mp_property_loop(m_option_t *prop, int action, void *arg,
266 MPContext *mpctx)
268 struct MPOpts *opts = &mpctx->opts;
269 switch (action) {
270 case M_PROPERTY_PRINT:
271 if (!arg)
272 return M_PROPERTY_ERROR;
273 if (opts->loop_times < 0)
274 *(char **)arg = talloc_strdup(NULL, "off");
275 else if (opts->loop_times == 0)
276 *(char **)arg = talloc_strdup(NULL, "inf");
277 else
278 break;
279 return M_PROPERTY_OK;
281 return m_property_int_range(prop, action, arg, &opts->loop_times);
284 /// Playback speed (RW)
285 static int mp_property_playback_speed(m_option_t *prop, int action,
286 void *arg, MPContext *mpctx)
288 struct MPOpts *opts = &mpctx->opts;
289 double orig_speed = opts->playback_speed;
290 switch (action) {
291 case M_PROPERTY_SET:
292 if (!arg)
293 return M_PROPERTY_ERROR;
294 opts->playback_speed = *(float *) arg;
295 goto set;
296 case M_PROPERTY_STEP:
297 opts->playback_speed += (arg ? *(float *) arg : 0.1);
298 set:
299 M_PROPERTY_CLAMP(prop, opts->playback_speed);
300 if (opts->playback_speed == orig_speed)
301 return M_PROPERTY_OK;
302 // Adjust time until next frame flip for nosound mode
303 mpctx->time_frame *= orig_speed / opts->playback_speed;
304 if (mpctx->sh_audio) {
305 double a = ao_get_delay(mpctx->ao);
306 mpctx->delay += (opts->playback_speed - orig_speed) * a;
308 reinit_audio_chain(mpctx);
309 return M_PROPERTY_OK;
311 return m_property_float_range(prop, action, arg, &opts->playback_speed);
314 /// filename with path (RO)
315 static int mp_property_path(m_option_t *prop, int action, void *arg,
316 MPContext *mpctx)
318 if (!mpctx->filename)
319 return M_PROPERTY_UNAVAILABLE;
320 return m_property_string_ro(prop, action, arg, mpctx->filename);
323 /// filename without path (RO)
324 static int mp_property_filename(m_option_t *prop, int action, void *arg,
325 MPContext *mpctx)
327 char *f;
328 if (!mpctx->filename)
329 return M_PROPERTY_UNAVAILABLE;
330 f = (char *)mp_basename(mpctx->filename);
331 if (!*f)
332 f = mpctx->filename;
333 return m_property_string_ro(prop, action, arg, f);
336 static int mp_property_media_title(m_option_t *prop, int action, void *arg,
337 MPContext *mpctx)
339 char *name = NULL;
340 if (mpctx->resolve_result)
341 name = mpctx->resolve_result->title;
342 if (name && name[0])
343 return m_property_string_ro(prop, action, arg, name);
344 return mp_property_filename(prop, action, arg, mpctx);
347 static int mp_property_stream_path(m_option_t *prop, int action, void *arg,
348 MPContext *mpctx)
350 struct stream *stream = mpctx->stream;
351 if (!stream || !stream->url)
352 return M_PROPERTY_UNAVAILABLE;
353 return m_property_string_ro(prop, action, arg, stream->url);
356 /// Demuxer name (RO)
357 static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
358 MPContext *mpctx)
360 if (!mpctx->demuxer)
361 return M_PROPERTY_UNAVAILABLE;
362 return m_property_string_ro(prop, action, arg,
363 (char *) mpctx->demuxer->desc->name);
366 /// Position in the stream (RW)
367 static int mp_property_stream_pos(m_option_t *prop, int action, void *arg,
368 MPContext *mpctx)
370 if (!mpctx->demuxer || !mpctx->demuxer->stream)
371 return M_PROPERTY_UNAVAILABLE;
372 if (!arg)
373 return M_PROPERTY_ERROR;
374 switch (action) {
375 case M_PROPERTY_GET:
376 *(off_t *) arg = stream_tell(mpctx->demuxer->stream);
377 return M_PROPERTY_OK;
378 case M_PROPERTY_SET:
379 M_PROPERTY_CLAMP(prop, *(off_t *) arg);
380 stream_seek(mpctx->demuxer->stream, *(off_t *) arg);
381 return M_PROPERTY_OK;
383 return M_PROPERTY_NOT_IMPLEMENTED;
386 /// Stream start offset (RO)
387 static int mp_property_stream_start(m_option_t *prop, int action,
388 void *arg, MPContext *mpctx)
390 if (!mpctx->demuxer || !mpctx->demuxer->stream)
391 return M_PROPERTY_UNAVAILABLE;
392 switch (action) {
393 case M_PROPERTY_GET:
394 *(off_t *) arg = mpctx->demuxer->stream->start_pos;
395 return M_PROPERTY_OK;
397 return M_PROPERTY_NOT_IMPLEMENTED;
400 /// Stream end offset (RO)
401 static int mp_property_stream_end(m_option_t *prop, int action, void *arg,
402 MPContext *mpctx)
404 if (!mpctx->demuxer || !mpctx->demuxer->stream)
405 return M_PROPERTY_UNAVAILABLE;
406 switch (action) {
407 case M_PROPERTY_GET:
408 *(off_t *) arg = mpctx->demuxer->stream->end_pos;
409 return M_PROPERTY_OK;
411 return M_PROPERTY_NOT_IMPLEMENTED;
414 /// Stream length (RO)
415 static int mp_property_stream_length(m_option_t *prop, int action,
416 void *arg, MPContext *mpctx)
418 if (!mpctx->demuxer || !mpctx->demuxer->stream)
419 return M_PROPERTY_UNAVAILABLE;
420 switch (action) {
421 case M_PROPERTY_GET:
422 *(off_t *) arg =
423 mpctx->demuxer->stream->end_pos - mpctx->demuxer->stream->start_pos;
424 return M_PROPERTY_OK;
426 return M_PROPERTY_NOT_IMPLEMENTED;
429 /// Current stream position in seconds (RO)
430 static int mp_property_stream_time_pos(m_option_t *prop, int action,
431 void *arg, MPContext *mpctx)
433 if (!mpctx->demuxer || mpctx->demuxer->stream_pts == MP_NOPTS_VALUE)
434 return M_PROPERTY_UNAVAILABLE;
436 return m_property_time_ro(prop, action, arg, mpctx->demuxer->stream_pts);
440 /// Media length in seconds (RO)
441 static int mp_property_length(m_option_t *prop, int action, void *arg,
442 MPContext *mpctx)
444 double len;
446 if (!mpctx->demuxer ||
447 !(int) (len = get_time_length(mpctx)))
448 return M_PROPERTY_UNAVAILABLE;
450 return m_property_time_ro(prop, action, arg, len);
453 /// Current position in percent (RW)
454 static int mp_property_percent_pos(m_option_t *prop, int action,
455 void *arg, MPContext *mpctx)
457 int pos;
459 if (!mpctx->demuxer)
460 return M_PROPERTY_UNAVAILABLE;
462 switch (action) {
463 case M_PROPERTY_SET:
464 if (!arg)
465 return M_PROPERTY_ERROR;
466 M_PROPERTY_CLAMP(prop, *(int *)arg);
467 pos = *(int *)arg;
468 break;
469 case M_PROPERTY_STEP:
470 pos = get_percent_pos(mpctx);
471 pos += (arg ? *(int *)arg : 10);
472 M_PROPERTY_CLAMP(prop, pos);
473 break;
474 default:
475 return m_property_int_ro(prop, action, arg, get_percent_pos(mpctx));
478 queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0);
479 return M_PROPERTY_OK;
482 /// Current position in seconds (RW)
483 static int mp_property_time_pos(m_option_t *prop, int action,
484 void *arg, MPContext *mpctx)
486 if (!(mpctx->sh_video || mpctx->sh_audio))
487 return M_PROPERTY_UNAVAILABLE;
489 switch (action) {
490 case M_PROPERTY_SET:
491 if (!arg)
492 return M_PROPERTY_ERROR;
493 M_PROPERTY_CLAMP(prop, *(double *)arg);
494 queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, 0);
495 return M_PROPERTY_OK;
496 case M_PROPERTY_STEP:
497 queue_seek(mpctx, MPSEEK_RELATIVE, (arg ? *(double *)arg : 10.0), 0);
498 return M_PROPERTY_OK;
500 return m_property_time_ro(prop, action, arg, get_current_time(mpctx));
503 /// Current chapter (RW)
504 static int mp_property_chapter(m_option_t *prop, int action, void *arg,
505 MPContext *mpctx)
507 struct MPOpts *opts = &mpctx->opts;
508 int chapter = -1;
509 int step_all;
510 char *chapter_name = NULL;
512 if (mpctx->demuxer)
513 chapter = get_current_chapter(mpctx);
514 if (chapter < -1)
515 return M_PROPERTY_UNAVAILABLE;
517 switch (action) {
518 case M_PROPERTY_GET:
519 if (!arg)
520 return M_PROPERTY_ERROR;
521 *(int *) arg = chapter;
522 return M_PROPERTY_OK;
523 case M_PROPERTY_PRINT: {
524 if (!arg)
525 return M_PROPERTY_ERROR;
526 chapter_name = chapter_display_name(mpctx, chapter);
527 if (!chapter_name)
528 return M_PROPERTY_UNAVAILABLE;
529 *(char **) arg = chapter_name;
530 return M_PROPERTY_OK;
532 case M_PROPERTY_SET:
533 if (!arg)
534 return M_PROPERTY_ERROR;
535 M_PROPERTY_CLAMP(prop, *(int *)arg);
536 step_all = *(int *)arg - chapter;
537 chapter += step_all;
538 break;
539 case M_PROPERTY_STEP: {
540 step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1);
541 chapter += step_all;
542 if (chapter < 0)
543 chapter = 0;
544 break;
546 default:
547 return M_PROPERTY_NOT_IMPLEMENTED;
550 double next_pts = 0;
551 queue_seek(mpctx, MPSEEK_NONE, 0, 0);
552 chapter = seek_chapter(mpctx, chapter, &next_pts);
553 if (chapter >= 0) {
554 if (next_pts > -1.0)
555 queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
556 chapter_name = chapter_display_name(mpctx, chapter);
557 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
558 "Chapter: %s", chapter_name);
559 } else if (step_all > 0)
560 queue_seek(mpctx, MPSEEK_RELATIVE, 1000000000, 0);
561 else
562 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
563 "Chapter: (%d) %s", 0, mp_gtext("unknown"));
564 talloc_free(chapter_name);
565 return M_PROPERTY_OK;
568 /// Number of chapters in file
569 static int mp_property_chapters(m_option_t *prop, int action, void *arg,
570 MPContext *mpctx)
572 if (!mpctx->demuxer)
573 return M_PROPERTY_UNAVAILABLE;
574 int count = get_chapter_count(mpctx);
575 return m_property_int_ro(prop, action, arg, count);
578 /// Current dvd angle (RW)
579 static int mp_property_angle(m_option_t *prop, int action, void *arg,
580 MPContext *mpctx)
582 struct MPOpts *opts = &mpctx->opts;
583 int angle = -1;
584 int angles;
586 if (mpctx->demuxer)
587 angle = demuxer_get_current_angle(mpctx->demuxer);
588 if (angle < 0)
589 return M_PROPERTY_UNAVAILABLE;
590 angles = demuxer_angles_count(mpctx->demuxer);
591 if (angles <= 1)
592 return M_PROPERTY_UNAVAILABLE;
594 switch (action) {
595 case M_PROPERTY_GET:
596 if (!arg)
597 return M_PROPERTY_ERROR;
598 *(int *) arg = angle;
599 return M_PROPERTY_OK;
600 case M_PROPERTY_PRINT: {
601 if (!arg)
602 return M_PROPERTY_ERROR;
603 *(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
604 return M_PROPERTY_OK;
606 case M_PROPERTY_SET:
607 if (!arg)
608 return M_PROPERTY_ERROR;
609 angle = *(int *)arg;
610 M_PROPERTY_CLAMP(prop, angle);
611 break;
612 case M_PROPERTY_STEP: {
613 int step = 0;
614 if (arg)
615 step = *(int *)arg;
616 if (!step)
617 step = 1;
618 angle += step;
619 if (angle < 1) //cycle
620 angle = angles;
621 else if (angle > angles)
622 angle = 1;
623 break;
625 default:
626 return M_PROPERTY_NOT_IMPLEMENTED;
628 angle = demuxer_set_angle(mpctx->demuxer, angle);
629 if (angle >= 0) {
630 struct sh_video *sh_video = mpctx->demuxer->video->sh;
631 if (sh_video)
632 resync_video_stream(sh_video);
634 struct sh_audio *sh_audio = mpctx->demuxer->audio->sh;
635 if (sh_audio)
636 resync_audio_stream(sh_audio);
639 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
640 "Angle: %d/%d", angle, angles);
641 return M_PROPERTY_OK;
644 /// Demuxer meta data
645 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
646 MPContext *mpctx)
648 m_property_action_t *ka;
649 char *meta;
650 static const m_option_t key_type =
652 "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL
654 if (!mpctx->demuxer)
655 return M_PROPERTY_UNAVAILABLE;
657 switch (action) {
658 case M_PROPERTY_GET:
659 if (!arg)
660 return M_PROPERTY_ERROR;
661 *(char ***)arg = mpctx->demuxer->info;
662 return M_PROPERTY_OK;
663 case M_PROPERTY_KEY_ACTION:
664 if (!arg)
665 return M_PROPERTY_ERROR;
666 ka = arg;
667 if (!(meta = demux_info_get(mpctx->demuxer, ka->key)))
668 return M_PROPERTY_UNKNOWN;
669 switch (ka->action) {
670 case M_PROPERTY_GET:
671 if (!ka->arg)
672 return M_PROPERTY_ERROR;
673 *(char **)ka->arg = meta;
674 return M_PROPERTY_OK;
675 case M_PROPERTY_GET_TYPE:
676 if (!ka->arg)
677 return M_PROPERTY_ERROR;
678 *(const m_option_t **)ka->arg = &key_type;
679 return M_PROPERTY_OK;
682 return M_PROPERTY_NOT_IMPLEMENTED;
685 static int mp_property_pause(m_option_t *prop, int action, void *arg,
686 void *ctx)
688 MPContext *mpctx = ctx;
690 switch (action) {
691 case M_PROPERTY_SET:
692 if (!arg)
693 return M_PROPERTY_ERROR;
694 if (mpctx->paused == (bool) * (int *)arg)
695 return M_PROPERTY_OK;
696 case M_PROPERTY_STEP:
697 if (mpctx->paused) {
698 unpause_player(mpctx);
699 } else {
700 pause_player(mpctx);
702 return M_PROPERTY_OK;
703 default:
704 return m_property_flag(prop, action, arg, &mpctx->paused);
709 /// Volume (RW)
710 static int mp_property_volume(m_option_t *prop, int action, void *arg,
711 MPContext *mpctx)
714 if (!mpctx->sh_audio)
715 return M_PROPERTY_UNAVAILABLE;
717 switch (action) {
718 case M_PROPERTY_GET:
719 if (!arg)
720 return M_PROPERTY_ERROR;
721 mixer_getbothvolume(&mpctx->mixer, arg);
722 return M_PROPERTY_OK;
723 case M_PROPERTY_PRINT: {
724 float vol;
725 if (!arg)
726 return M_PROPERTY_ERROR;
727 mixer_getbothvolume(&mpctx->mixer, &vol);
728 return m_property_float_range(prop, action, arg, &vol);
730 case M_PROPERTY_SET:
731 if (!arg)
732 return M_PROPERTY_ERROR;
733 M_PROPERTY_CLAMP(prop, *(float *) arg);
734 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
735 return M_PROPERTY_OK;
736 case M_PROPERTY_STEP:
737 if (arg && *(float *) arg <= 0)
738 mixer_decvolume(&mpctx->mixer);
739 else
740 mixer_incvolume(&mpctx->mixer);
741 return M_PROPERTY_OK;
742 default:
743 return M_PROPERTY_NOT_IMPLEMENTED;
747 /// Mute (RW)
748 static int mp_property_mute(m_option_t *prop, int action, void *arg,
749 MPContext *mpctx)
752 if (!mpctx->sh_audio)
753 return M_PROPERTY_UNAVAILABLE;
755 switch (action) {
756 case M_PROPERTY_SET:
757 if (!arg)
758 return M_PROPERTY_ERROR;
759 mixer_setmute(&mpctx->mixer, *(int *) arg);
760 return M_PROPERTY_OK;
761 case M_PROPERTY_STEP:
762 mixer_setmute(&mpctx->mixer, !mixer_getmute(&mpctx->mixer));
763 return M_PROPERTY_OK;
764 default:
765 return m_property_flag_ro(prop, action, arg,
766 mixer_getmute(&mpctx->mixer));
770 /// Audio delay (RW)
771 static int mp_property_audio_delay(m_option_t *prop, int action,
772 void *arg, MPContext *mpctx)
774 if (!(mpctx->sh_audio && mpctx->sh_video))
775 return M_PROPERTY_UNAVAILABLE;
776 switch (action) {
777 case M_PROPERTY_SET:
778 case M_PROPERTY_STEP: {
779 int ret;
780 float delay = audio_delay;
781 ret = m_property_delay(prop, action, arg, &audio_delay);
782 if (ret != M_PROPERTY_OK)
783 return ret;
784 if (mpctx->sh_audio)
785 mpctx->delay -= audio_delay - delay;
787 return M_PROPERTY_OK;
788 default:
789 return m_property_delay(prop, action, arg, &audio_delay);
793 /// Audio codec tag (RO)
794 static int mp_property_audio_format(m_option_t *prop, int action,
795 void *arg, MPContext *mpctx)
797 if (!mpctx->sh_audio)
798 return M_PROPERTY_UNAVAILABLE;
799 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
802 /// Audio codec name (RO)
803 static int mp_property_audio_codec(m_option_t *prop, int action,
804 void *arg, MPContext *mpctx)
806 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
807 return M_PROPERTY_UNAVAILABLE;
808 return m_property_string_ro(prop, action, arg,
809 mpctx->sh_audio->codec->name);
812 /// Audio bitrate (RO)
813 static int mp_property_audio_bitrate(m_option_t *prop, int action,
814 void *arg, MPContext *mpctx)
816 if (!mpctx->sh_audio)
817 return M_PROPERTY_UNAVAILABLE;
818 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
821 /// Samplerate (RO)
822 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
823 MPContext *mpctx)
825 if (!mpctx->sh_audio)
826 return M_PROPERTY_UNAVAILABLE;
827 switch (action) {
828 case M_PROPERTY_PRINT:
829 if (!arg)
830 return M_PROPERTY_ERROR;
831 *(char **)arg = talloc_asprintf(NULL, "%d kHz",
832 mpctx->sh_audio->samplerate / 1000);
833 return M_PROPERTY_OK;
835 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
838 /// Number of channels (RO)
839 static int mp_property_channels(m_option_t *prop, int action, void *arg,
840 MPContext *mpctx)
842 if (!mpctx->sh_audio)
843 return M_PROPERTY_UNAVAILABLE;
844 switch (action) {
845 case M_PROPERTY_PRINT:
846 if (!arg)
847 return M_PROPERTY_ERROR;
848 switch (mpctx->sh_audio->channels) {
849 case 1:
850 *(char **) arg = talloc_strdup(NULL, "mono");
851 break;
852 case 2:
853 *(char **) arg = talloc_strdup(NULL, "stereo");
854 break;
855 default:
856 *(char **) arg = talloc_asprintf(NULL, "%d channels",
857 mpctx->sh_audio->channels);
859 return M_PROPERTY_OK;
861 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
864 /// Balance (RW)
865 static int mp_property_balance(m_option_t *prop, int action, void *arg,
866 MPContext *mpctx)
868 float bal;
870 switch (action) {
871 case M_PROPERTY_GET:
872 if (!arg)
873 return M_PROPERTY_ERROR;
874 mixer_getbalance(&mpctx->mixer, arg);
875 return M_PROPERTY_OK;
876 case M_PROPERTY_PRINT: {
877 char **str = arg;
878 if (!arg)
879 return M_PROPERTY_ERROR;
880 mixer_getbalance(&mpctx->mixer, &bal);
881 if (bal == 0.f)
882 *str = talloc_strdup(NULL, "center");
883 else if (bal == -1.f)
884 *str = talloc_strdup(NULL, "left only");
885 else if (bal == 1.f)
886 *str = talloc_strdup(NULL, "right only");
887 else {
888 unsigned right = (bal + 1.f) / 2.f * 100.f;
889 *str = talloc_asprintf(NULL, "left %d%%, right %d%%",
890 100 - right, right);
892 return M_PROPERTY_OK;
894 case M_PROPERTY_STEP:
895 mixer_getbalance(&mpctx->mixer, &bal);
896 bal += (arg ? *(float *)arg : .1f);
897 M_PROPERTY_CLAMP(prop, bal);
898 mixer_setbalance(&mpctx->mixer, bal);
899 return M_PROPERTY_OK;
900 case M_PROPERTY_SET:
901 if (!arg)
902 return M_PROPERTY_ERROR;
903 M_PROPERTY_CLAMP(prop, *(float *)arg);
904 mixer_setbalance(&mpctx->mixer, *(float *)arg);
905 return M_PROPERTY_OK;
907 return M_PROPERTY_NOT_IMPLEMENTED;
910 /// Selected audio id (RW)
911 static int mp_property_audio(m_option_t *prop, int action, void *arg,
912 MPContext *mpctx)
914 int current_id, tmp;
915 if (!mpctx->demuxer || !mpctx->d_audio)
916 return M_PROPERTY_UNAVAILABLE;
917 struct sh_audio *sh = mpctx->sh_audio;
918 current_id = sh ? sh->aid : -2;
920 switch (action) {
921 case M_PROPERTY_GET:
922 if (!arg)
923 return M_PROPERTY_ERROR;
924 *(int *) arg = current_id;
925 return M_PROPERTY_OK;
926 case M_PROPERTY_PRINT:
927 if (!arg)
928 return M_PROPERTY_ERROR;
930 if (current_id < 0)
931 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
932 else if (sh && (sh->lang || sh->title)) {
933 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
934 if (sh->title)
935 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
936 current_id, lang, sh->title);
937 else
938 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
939 lang);
940 } else {
941 char lang[40];
942 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
943 if (0) ;
944 #ifdef CONFIG_DVDREAD
945 else if (mpctx->stream->type == STREAMTYPE_DVD) {
946 int code = dvd_lang_from_aid(mpctx->stream, current_id);
947 if (code) {
948 lang[0] = code >> 8;
949 lang[1] = code;
950 lang[2] = 0;
953 #endif
955 #ifdef CONFIG_DVDNAV
956 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
957 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
958 #endif
959 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, lang);
961 return M_PROPERTY_OK;
963 case M_PROPERTY_STEP:
964 case M_PROPERTY_SET:
965 if (action == M_PROPERTY_SET && arg)
966 tmp = *((int *) arg);
967 else
968 tmp = -1;
969 int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
970 if (new_id != current_id)
971 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
972 if (new_id != current_id && new_id >= 0) {
973 sh_audio_t *sh2;
974 sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
975 sh2->ds = mpctx->d_audio;
976 mpctx->sh_audio = sh2;
977 reinit_audio_chain(mpctx);
979 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
980 return M_PROPERTY_OK;
981 default:
982 return M_PROPERTY_NOT_IMPLEMENTED;
987 /// Selected video id (RW)
988 static int mp_property_video(m_option_t *prop, int action, void *arg,
989 MPContext *mpctx)
991 struct MPOpts *opts = &mpctx->opts;
992 int current_id, tmp;
993 if (!mpctx->demuxer || !mpctx->d_video)
994 return M_PROPERTY_UNAVAILABLE;
995 current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
997 switch (action) {
998 case M_PROPERTY_GET:
999 if (!arg)
1000 return M_PROPERTY_ERROR;
1001 *(int *) arg = current_id;
1002 return M_PROPERTY_OK;
1003 case M_PROPERTY_PRINT:
1004 if (!arg)
1005 return M_PROPERTY_ERROR;
1007 if (current_id < 0)
1008 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1009 else {
1010 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
1011 mp_gtext("unknown"));
1013 return M_PROPERTY_OK;
1015 case M_PROPERTY_STEP:
1016 case M_PROPERTY_SET:
1017 if (action == M_PROPERTY_SET && arg)
1018 tmp = *((int *) arg);
1019 else
1020 tmp = -1;
1021 int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
1022 if (new_id != current_id)
1023 uninit_player(mpctx, INITIALIZED_VCODEC |
1024 (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
1025 if (new_id != current_id && new_id >= 0) {
1026 sh_video_t *sh2;
1027 sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
1028 sh2->ds = mpctx->d_video;
1029 mpctx->sh_video = sh2;
1030 reinit_video_chain(mpctx);
1032 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
1033 return M_PROPERTY_OK;
1035 default:
1036 return M_PROPERTY_NOT_IMPLEMENTED;
1040 static int mp_property_program(m_option_t *prop, int action, void *arg,
1041 MPContext *mpctx)
1043 demux_program_t prog;
1045 switch (action) {
1046 case M_PROPERTY_STEP:
1047 case M_PROPERTY_SET:
1048 if (action == M_PROPERTY_SET && arg)
1049 prog.progid = *((int *) arg);
1050 else
1051 prog.progid = -1;
1052 if (demux_control(mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1053 &prog) == DEMUXER_CTRL_NOTIMPL)
1054 return M_PROPERTY_ERROR;
1056 if (prog.aid < 0 && prog.vid < 0) {
1057 mp_msg(MSGT_CPLAYER, MSGL_ERR,
1058 "Selected program contains no audio or video streams!\n");
1059 return M_PROPERTY_ERROR;
1061 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1062 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1063 return M_PROPERTY_OK;
1065 default:
1066 return M_PROPERTY_NOT_IMPLEMENTED;
1071 /// Fullscreen state (RW)
1072 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1073 MPContext *mpctx)
1076 if (!mpctx->video_out)
1077 return M_PROPERTY_UNAVAILABLE;
1079 switch (action) {
1080 case M_PROPERTY_SET:
1081 if (!arg)
1082 return M_PROPERTY_ERROR;
1083 M_PROPERTY_CLAMP(prop, *(int *) arg);
1084 if (vo_fs == !!*(int *) arg)
1085 return M_PROPERTY_OK;
1086 case M_PROPERTY_STEP:
1087 if (mpctx->video_out->config_ok)
1088 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1089 mpctx->opts.fullscreen = vo_fs;
1090 return M_PROPERTY_OK;
1091 default:
1092 return m_property_flag(prop, action, arg, &vo_fs);
1096 static int mp_property_deinterlace(m_option_t *prop, int action,
1097 void *arg, MPContext *mpctx)
1099 int deinterlace;
1100 vf_instance_t *vf;
1101 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1102 return M_PROPERTY_UNAVAILABLE;
1103 vf = mpctx->sh_video->vfilter;
1104 switch (action) {
1105 case M_PROPERTY_GET:
1106 if (!arg)
1107 return M_PROPERTY_ERROR;
1108 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1109 return M_PROPERTY_OK;
1110 case M_PROPERTY_SET:
1111 if (!arg)
1112 return M_PROPERTY_ERROR;
1113 M_PROPERTY_CLAMP(prop, *(int *) arg);
1114 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1115 return M_PROPERTY_OK;
1116 case M_PROPERTY_STEP:
1117 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1118 deinterlace = !deinterlace;
1119 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1120 return M_PROPERTY_OK;
1122 int value = 0;
1123 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1124 return m_property_flag_ro(prop, action, arg, value);
1127 static int colormatrix_property_helper(m_option_t *prop, int action,
1128 void *arg, MPContext *mpctx)
1130 int r = mp_property_generic_option(prop, action, arg, mpctx);
1131 // testing for an actual change is too much effort
1132 switch (action) {
1133 case M_PROPERTY_SET:
1134 case M_PROPERTY_STEP:
1135 if (mpctx->sh_video)
1136 set_video_colorspace(mpctx->sh_video);
1137 break;
1139 return r;
1142 static int mp_property_colormatrix(m_option_t *prop, int action, void *arg,
1143 MPContext *mpctx)
1145 struct MPOpts *opts = &mpctx->opts;
1146 switch (action) {
1147 case M_PROPERTY_PRINT:
1148 if (!arg)
1149 return M_PROPERTY_ERROR;
1150 struct mp_csp_details actual = { .format = -1 };
1151 char *req_csp = mp_csp_names[opts->requested_colorspace];
1152 char *real_csp = NULL;
1153 if (mpctx->sh_video) {
1154 struct vf_instance *vf = mpctx->sh_video->vfilter;
1155 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1156 real_csp = mp_csp_names[actual.format];
1157 } else {
1158 real_csp = "Unknown";
1161 char *res;
1162 if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) {
1163 // Caveat: doesn't handle the case when the autodetected colorspace
1164 // is different from the actual colorspace as used by the
1165 // VO - the OSD will display the VO colorspace without
1166 // indication that it doesn't match the requested colorspace.
1167 res = talloc_asprintf(NULL, "Auto (%s)", real_csp);
1168 } else if (opts->requested_colorspace == actual.format || !real_csp) {
1169 res = talloc_strdup(NULL, req_csp);
1170 } else
1171 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1172 req_csp, real_csp);
1173 *(char **)arg = res;
1174 return M_PROPERTY_OK;
1175 default:;
1176 return colormatrix_property_helper(prop, action, arg, mpctx);
1180 static int levels_property_helper(int offset, m_option_t *prop, int action,
1181 void *arg, MPContext *mpctx)
1183 char *optname = prop->priv;
1184 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
1185 bstr(optname));
1186 int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts);
1188 switch (action) {
1189 case M_PROPERTY_PRINT:
1190 if (!arg)
1191 return M_PROPERTY_ERROR;
1192 struct mp_csp_details actual = {0};
1193 int actual_level = -1;
1194 char *req_level = m_option_print(opt, valptr);
1195 char *real_level = NULL;
1196 if (mpctx->sh_video) {
1197 struct vf_instance *vf = mpctx->sh_video->vfilter;
1198 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1199 actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset);
1200 real_level = m_option_print(opt, &actual_level);
1201 } else {
1202 real_level = talloc_strdup(NULL, "Unknown");
1205 char *res;
1206 if (*valptr == MP_CSP_LEVELS_AUTO && real_level) {
1207 res = talloc_asprintf(NULL, "Auto (%s)", real_level);
1208 } else if (*valptr == actual_level || !real_level) {
1209 res = talloc_strdup(NULL, real_level);
1210 } else
1211 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1212 req_level, real_level);
1213 talloc_free(req_level);
1214 talloc_free(real_level);
1215 *(char **)arg = res;
1216 return M_PROPERTY_OK;
1217 default:;
1218 return colormatrix_property_helper(prop, action, arg, mpctx);
1222 static int mp_property_colormatrix_input_range(m_option_t *prop, int action,
1223 void *arg, MPContext *mpctx)
1225 return levels_property_helper(offsetof(struct mp_csp_details, levels_in),
1226 prop, action, arg, mpctx);
1229 static int mp_property_colormatrix_output_range(m_option_t *prop, int action,
1230 void *arg, MPContext *mpctx)
1232 return levels_property_helper(offsetof(struct mp_csp_details, levels_out),
1233 prop, action, arg, mpctx);
1236 static int mp_property_capture(m_option_t *prop, int action,
1237 void *arg, MPContext *mpctx)
1239 struct MPOpts *opts = &mpctx->opts;
1241 if (!mpctx->stream)
1242 return M_PROPERTY_UNAVAILABLE;
1244 if (!opts->capture_dump) {
1245 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1246 "Capturing not enabled (forgot -capture parameter?)\n");
1247 return M_PROPERTY_ERROR;
1250 int capturing = !!mpctx->stream->capture_file;
1252 int ret = m_property_flag(prop, action, arg, &capturing);
1253 if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1254 if (capturing) {
1255 mpctx->stream->capture_file = fopen(opts->stream_dump_name, "wb");
1256 if (!mpctx->stream->capture_file) {
1257 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1258 "Error opening capture file: %s\n", strerror(errno));
1259 ret = M_PROPERTY_ERROR;
1261 } else {
1262 fclose(mpctx->stream->capture_file);
1263 mpctx->stream->capture_file = NULL;
1267 return ret;
1270 /// Panscan (RW)
1271 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1272 MPContext *mpctx)
1275 if (!mpctx->video_out
1276 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1277 return M_PROPERTY_UNAVAILABLE;
1279 switch (action) {
1280 case M_PROPERTY_SET:
1281 if (!arg)
1282 return M_PROPERTY_ERROR;
1283 M_PROPERTY_CLAMP(prop, *(float *) arg);
1284 vo_panscan = *(float *) arg;
1285 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1286 return M_PROPERTY_OK;
1287 case M_PROPERTY_STEP:
1288 vo_panscan += (arg ? *(float *) arg : 0.1);
1289 if (vo_panscan > 1)
1290 vo_panscan = 1;
1291 else if (vo_panscan < 0)
1292 vo_panscan = 0;
1293 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1294 return M_PROPERTY_OK;
1295 default:
1296 return m_property_float_range(prop, action, arg, &vo_panscan);
1300 /// Helper to set vo flags.
1301 /** \ingroup PropertyImplHelper
1303 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1304 int vo_ctrl, int *vo_var, MPContext *mpctx)
1307 if (!mpctx->video_out)
1308 return M_PROPERTY_UNAVAILABLE;
1310 switch (action) {
1311 case M_PROPERTY_SET:
1312 if (!arg)
1313 return M_PROPERTY_ERROR;
1314 M_PROPERTY_CLAMP(prop, *(int *) arg);
1315 if (*vo_var == !!*(int *) arg)
1316 return M_PROPERTY_OK;
1317 case M_PROPERTY_STEP:
1318 if (mpctx->video_out->config_ok)
1319 vo_control(mpctx->video_out, vo_ctrl, 0);
1320 return M_PROPERTY_OK;
1321 default:
1322 return m_property_flag(prop, action, arg, vo_var);
1326 /// Window always on top (RW)
1327 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1328 MPContext *mpctx)
1330 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1331 &mpctx->opts.vo_ontop, mpctx);
1334 /// Display in the root window (RW)
1335 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1336 MPContext *mpctx)
1338 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1339 &vo_rootwin, mpctx);
1342 /// Show window borders (RW)
1343 static int mp_property_border(m_option_t *prop, int action, void *arg,
1344 MPContext *mpctx)
1346 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1347 &vo_border, mpctx);
1350 /// Framedropping state (RW)
1351 static int mp_property_framedropping(m_option_t *prop, int action,
1352 void *arg, MPContext *mpctx)
1355 if (!mpctx->sh_video)
1356 return M_PROPERTY_UNAVAILABLE;
1358 switch (action) {
1359 case M_PROPERTY_PRINT:
1360 if (!arg)
1361 return M_PROPERTY_ERROR;
1362 *(char **) arg = talloc_strdup(NULL, frame_dropping == 1 ?
1363 mp_gtext("enabled") :
1364 (frame_dropping == 2 ? mp_gtext("hard") :
1365 mp_gtext("disabled")));
1366 return M_PROPERTY_OK;
1367 default:
1368 return m_property_choice(prop, action, arg, &frame_dropping);
1372 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1373 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1374 MPContext *mpctx)
1376 int *gamma = (int *)((char *)&mpctx->opts + prop->offset);
1377 int r, val;
1379 if (!mpctx->sh_video)
1380 return M_PROPERTY_UNAVAILABLE;
1382 if (gamma[0] == 1000) {
1383 gamma[0] = 0;
1384 get_video_colors(mpctx->sh_video, prop->name, gamma);
1387 switch (action) {
1388 case M_PROPERTY_SET:
1389 if (!arg)
1390 return M_PROPERTY_ERROR;
1391 M_PROPERTY_CLAMP(prop, *(int *) arg);
1392 *gamma = *(int *) arg;
1393 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1394 if (r <= 0)
1395 break;
1396 return r;
1397 case M_PROPERTY_GET:
1398 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1399 if (!arg)
1400 return M_PROPERTY_ERROR;
1401 *(int *)arg = val;
1402 return M_PROPERTY_OK;
1404 break;
1405 case M_PROPERTY_STEP:
1406 *gamma += (arg ? *(int *) arg : 1);
1407 M_PROPERTY_CLAMP(prop, *gamma);
1408 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1409 if (r <= 0)
1410 break;
1411 return r;
1412 default:
1413 return M_PROPERTY_NOT_IMPLEMENTED;
1416 #ifdef CONFIG_TV
1417 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1418 int l = strlen(prop->name);
1419 char tv_prop[3 + l + 1];
1420 sprintf(tv_prop, "tv_%s", prop->name);
1421 return mp_property_do(tv_prop, action, arg, mpctx);
1423 #endif
1425 return M_PROPERTY_UNAVAILABLE;
1428 /// VSync (RW)
1429 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1430 MPContext *mpctx)
1432 return m_property_flag(prop, action, arg, &vo_vsync);
1435 /// Video codec tag (RO)
1436 static int mp_property_video_format(m_option_t *prop, int action,
1437 void *arg, MPContext *mpctx)
1439 char *meta;
1440 if (!mpctx->sh_video)
1441 return M_PROPERTY_UNAVAILABLE;
1442 switch (action) {
1443 case M_PROPERTY_PRINT:
1444 if (!arg)
1445 return M_PROPERTY_ERROR;
1446 switch (mpctx->sh_video->format) {
1447 case 0x10000001:
1448 meta = talloc_strdup(NULL, "mpeg1");
1449 break;
1450 case 0x10000002:
1451 meta = talloc_strdup(NULL, "mpeg2");
1452 break;
1453 case 0x10000004:
1454 meta = talloc_strdup(NULL, "mpeg4");
1455 break;
1456 case 0x10000005:
1457 meta = talloc_strdup(NULL, "h264");
1458 break;
1459 default:
1460 if (mpctx->sh_video->format >= 0x20202020) {
1461 meta = talloc_asprintf(NULL, "%.4s",
1462 (char *) &mpctx->sh_video->format);
1463 } else
1464 meta = talloc_asprintf(NULL, "0x%08X", mpctx->sh_video->format);
1466 *(char **)arg = meta;
1467 return M_PROPERTY_OK;
1469 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1472 /// Video codec name (RO)
1473 static int mp_property_video_codec(m_option_t *prop, int action,
1474 void *arg, MPContext *mpctx)
1476 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1477 return M_PROPERTY_UNAVAILABLE;
1478 return m_property_string_ro(prop, action, arg,
1479 mpctx->sh_video->codec->name);
1483 /// Video bitrate (RO)
1484 static int mp_property_video_bitrate(m_option_t *prop, int action,
1485 void *arg, MPContext *mpctx)
1487 if (!mpctx->sh_video)
1488 return M_PROPERTY_UNAVAILABLE;
1489 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1492 /// Video display width (RO)
1493 static int mp_property_width(m_option_t *prop, int action, void *arg,
1494 MPContext *mpctx)
1496 if (!mpctx->sh_video)
1497 return M_PROPERTY_UNAVAILABLE;
1498 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1501 /// Video display height (RO)
1502 static int mp_property_height(m_option_t *prop, int action, void *arg,
1503 MPContext *mpctx)
1505 if (!mpctx->sh_video)
1506 return M_PROPERTY_UNAVAILABLE;
1507 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1510 /// Video fps (RO)
1511 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1512 MPContext *mpctx)
1514 if (!mpctx->sh_video)
1515 return M_PROPERTY_UNAVAILABLE;
1516 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1519 /// Video aspect (RO)
1520 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1521 MPContext *mpctx)
1523 if (!mpctx->sh_video)
1524 return M_PROPERTY_UNAVAILABLE;
1525 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1529 /// Text subtitle position (RW)
1530 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1531 MPContext *mpctx)
1533 switch (action) {
1534 case M_PROPERTY_SET:
1535 if (!arg)
1536 return M_PROPERTY_ERROR;
1537 case M_PROPERTY_STEP:
1538 vo_osd_changed(OSDTYPE_SUBTITLE);
1539 default:
1540 return m_property_int_range(prop, action, arg, &sub_pos);
1544 /// Selected subtitles (RW)
1545 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1546 MPContext *mpctx)
1548 struct MPOpts *opts = &mpctx->opts;
1549 demux_stream_t *const d_sub = mpctx->d_sub;
1550 int source = -1, reset_spu av_unused = 0; // used under CONFIG_DVDREAD
1551 int source_pos = -1;
1553 update_global_sub_size(mpctx);
1554 const int global_sub_size = mpctx->global_sub_size;
1556 if (global_sub_size <= 0)
1557 return M_PROPERTY_UNAVAILABLE;
1559 switch (action) {
1560 case M_PROPERTY_GET:
1561 if (!arg)
1562 return M_PROPERTY_ERROR;
1563 *(int *) arg = mpctx->global_sub_pos;
1564 return M_PROPERTY_OK;
1565 case M_PROPERTY_PRINT:
1566 if (!arg)
1567 return M_PROPERTY_ERROR;
1568 char *sub_name = NULL;
1569 if (mpctx->subdata)
1570 sub_name = mpctx->subdata->filename;
1571 if (sub_source(mpctx) == SUB_SOURCE_SUBS && mpctx->osd->sh_sub)
1572 sub_name = mpctx->osd->sh_sub->title;
1573 if (!sub_name && mpctx->subdata)
1574 sub_name = mpctx->subdata->filename;
1575 if (sub_name) {
1576 const char *tmp = mp_basename(sub_name);
1578 *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
1579 mpctx->set_of_sub_pos + 1,
1580 strlen(tmp) < 20 ? "" : "...",
1581 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1582 return M_PROPERTY_OK;
1584 #ifdef CONFIG_DVDNAV
1585 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1586 if (vo_spudec && opts->sub_id >= 0) {
1587 unsigned char lang[3];
1588 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id,
1589 lang)) {
1590 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1591 opts->sub_id, lang);
1592 return M_PROPERTY_OK;
1596 #endif
1598 if ((d_sub->demuxer->type == DEMUXER_TYPE_MATROSKA
1599 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF
1600 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1601 || d_sub->demuxer->type == DEMUXER_TYPE_OGG)
1602 && d_sub->sh && opts->sub_id >= 0) {
1603 struct sh_sub *sh = d_sub->sh;
1604 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
1605 if (sh->title)
1606 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
1607 opts->sub_id, lang, sh->title);
1608 else
1609 *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
1610 opts->sub_id, lang);
1611 return M_PROPERTY_OK;
1614 if (vo_vobsub && vobsub_id >= 0) {
1615 const char *language = mp_gtext("unknown");
1616 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1617 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1618 vobsub_id, language ? language : mp_gtext("unknown"));
1619 return M_PROPERTY_OK;
1621 #ifdef CONFIG_DVDREAD
1622 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1623 && opts->sub_id >= 0) {
1624 char lang[3];
1625 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1626 lang[0] = code >> 8;
1627 lang[1] = code;
1628 lang[2] = 0;
1629 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1630 opts->sub_id, lang);
1631 return M_PROPERTY_OK;
1633 #endif
1634 if (opts->sub_id >= 0) {
1635 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
1636 mp_gtext("unknown"));
1637 return M_PROPERTY_OK;
1639 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1640 return M_PROPERTY_OK;
1642 case M_PROPERTY_SET:
1643 if (!arg)
1644 return M_PROPERTY_ERROR;
1645 if (*(int *) arg < -1)
1646 *(int *) arg = -1;
1647 else if (*(int *) arg >= global_sub_size)
1648 *(int *) arg = global_sub_size - 1;
1649 mpctx->global_sub_pos = *(int *) arg;
1650 break;
1651 case M_PROPERTY_STEP:
1652 if (!arg || *(int *)arg >= 0) {
1653 mpctx->global_sub_pos += 2;
1654 mpctx->global_sub_pos =
1655 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1656 } else {
1657 mpctx->global_sub_pos += global_sub_size + 1;
1658 mpctx->global_sub_pos =
1659 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1661 break;
1662 default:
1663 return M_PROPERTY_NOT_IMPLEMENTED;
1666 if (mpctx->global_sub_pos >= 0) {
1667 source = sub_source(mpctx);
1668 source_pos = sub_source_pos(mpctx);
1671 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1672 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1673 global_sub_size,
1674 mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1675 mpctx->sub_counts[SUB_SOURCE_SUBS],
1676 mpctx->sub_counts[SUB_SOURCE_DEMUX],
1677 mpctx->global_sub_pos, source);
1679 mpctx->set_of_sub_pos = -1;
1680 mpctx->subdata = NULL;
1682 vobsub_id = -1;
1683 opts->sub_id = -1;
1684 if (d_sub) {
1685 if (d_sub->id > -2)
1686 reset_spu = 1;
1687 d_sub->id = -2;
1689 uninit_player(mpctx, INITIALIZED_SUB);
1691 if (source == SUB_SOURCE_VOBSUB)
1692 vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1693 else if (source == SUB_SOURCE_SUBS) {
1694 mpctx->set_of_sub_pos = source_pos;
1695 if (opts->ass_enabled
1696 && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
1697 sub_init(mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos],
1698 mpctx->osd);
1699 mpctx->initialized_flags |= INITIALIZED_SUB;
1700 } else
1701 mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1702 vo_osd_changed(OSDTYPE_SUBTITLE);
1703 } else if (source == SUB_SOURCE_DEMUX) {
1704 opts->sub_id = source_pos;
1705 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1706 int i = 0;
1707 // default: assume 1:1 mapping of sid and stream id
1708 d_sub->id = opts->sub_id;
1709 d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
1710 ds_free_packs(d_sub);
1711 for (i = 0; i < MAX_S_STREAMS; i++) {
1712 sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
1713 if (sh && sh->sid == opts->sub_id) {
1714 d_sub->id = i;
1715 d_sub->sh = sh;
1716 break;
1719 if (d_sub->sh && d_sub->id >= 0) {
1720 sh_sub_t *sh = d_sub->sh;
1721 if (sh->type == 'v')
1722 init_vo_spudec(mpctx);
1723 else {
1724 sub_init(sh, mpctx->osd);
1725 mpctx->initialized_flags |= INITIALIZED_SUB;
1727 } else {
1728 d_sub->id = -2;
1729 d_sub->sh = NULL;
1733 #ifdef CONFIG_DVDREAD
1734 if (vo_spudec
1735 && (mpctx->stream->type == STREAMTYPE_DVD
1736 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1737 && opts->sub_id < 0 && reset_spu) {
1738 d_sub->id = -2;
1739 d_sub->sh = NULL;
1741 #endif
1743 update_subtitles(mpctx, 0, true);
1745 return M_PROPERTY_OK;
1748 /// Selected sub source (RW)
1749 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1750 MPContext *mpctx)
1752 int source;
1753 update_global_sub_size(mpctx);
1754 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1755 return M_PROPERTY_UNAVAILABLE;
1757 switch (action) {
1758 case M_PROPERTY_GET:
1759 if (!arg)
1760 return M_PROPERTY_ERROR;
1761 *(int *) arg = sub_source(mpctx);
1762 return M_PROPERTY_OK;
1763 case M_PROPERTY_PRINT:
1764 if (!arg)
1765 return M_PROPERTY_ERROR;
1766 char *sourcename;
1767 switch (sub_source(mpctx)) {
1768 case SUB_SOURCE_SUBS:
1769 sourcename = mp_gtext("file");
1770 break;
1771 case SUB_SOURCE_VOBSUB:
1772 sourcename = mp_gtext("vobsub");
1773 break;
1774 case SUB_SOURCE_DEMUX:
1775 sourcename = mp_gtext("embedded");
1776 break;
1777 default:
1778 sourcename = mp_gtext("disabled");
1780 *(char **)arg = talloc_strdup(NULL, sourcename);
1781 return M_PROPERTY_OK;
1782 case M_PROPERTY_SET:
1783 if (!arg)
1784 return M_PROPERTY_ERROR;
1785 M_PROPERTY_CLAMP(prop, *(int *)arg);
1786 if (*(int *) arg < 0)
1787 mpctx->global_sub_pos = -1;
1788 else if (*(int *) arg != sub_source(mpctx)) {
1789 int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1790 if (new_pos == -1)
1791 return M_PROPERTY_UNAVAILABLE;
1792 mpctx->global_sub_pos = new_pos;
1794 break;
1795 case M_PROPERTY_STEP: {
1796 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1);
1797 int step = (step_all > 0) ? 1 : -1;
1798 int cur_source = sub_source(mpctx);
1799 source = cur_source;
1800 while (step_all) {
1801 source += step;
1802 if (source >= SUB_SOURCES)
1803 source = -1;
1804 else if (source < -1)
1805 source = SUB_SOURCES - 1;
1806 if (source == cur_source || source == -1 ||
1807 mpctx->sub_counts[source])
1808 step_all -= step;
1810 if (source == cur_source)
1811 return M_PROPERTY_OK;
1812 if (source == -1)
1813 mpctx->global_sub_pos = -1;
1814 else
1815 mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1816 break;
1818 default:
1819 return M_PROPERTY_NOT_IMPLEMENTED;
1821 --mpctx->global_sub_pos;
1822 return mp_property_sub(prop, M_PROPERTY_STEP, NULL, mpctx);
1825 /// Selected subtitles from specific source (RW)
1826 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1827 MPContext *mpctx)
1829 int source, is_cur_source, offset, new_pos;
1830 update_global_sub_size(mpctx);
1831 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1832 return M_PROPERTY_UNAVAILABLE;
1834 if (!strcmp(prop->name, "sub_file"))
1835 source = SUB_SOURCE_SUBS;
1836 else if (!strcmp(prop->name, "sub_vob"))
1837 source = SUB_SOURCE_VOBSUB;
1838 else if (!strcmp(prop->name, "sub_demux"))
1839 source = SUB_SOURCE_DEMUX;
1840 else
1841 return M_PROPERTY_ERROR;
1843 offset = sub_pos_by_source(mpctx, source);
1844 if (offset < 0)
1845 return M_PROPERTY_UNAVAILABLE;
1847 is_cur_source = sub_source(mpctx) == source;
1848 new_pos = mpctx->global_sub_pos;
1849 switch (action) {
1850 case M_PROPERTY_GET:
1851 if (!arg)
1852 return M_PROPERTY_ERROR;
1853 if (is_cur_source) {
1854 *(int *) arg = sub_source_pos(mpctx);
1855 if (source == SUB_SOURCE_VOBSUB)
1856 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1857 } else
1858 *(int *) arg = -1;
1859 return M_PROPERTY_OK;
1860 case M_PROPERTY_PRINT:
1861 if (!arg)
1862 return M_PROPERTY_ERROR;
1863 if (is_cur_source)
1864 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1865 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1866 return M_PROPERTY_OK;
1867 case M_PROPERTY_SET:
1868 if (!arg)
1869 return M_PROPERTY_ERROR;
1870 if (*(int *) arg >= 0) {
1871 int index = *(int *)arg;
1872 if (source == SUB_SOURCE_VOBSUB)
1873 index = vobsub_get_index_by_id(vo_vobsub, index);
1874 new_pos = offset + index;
1875 if (index < 0 || index > mpctx->sub_counts[source]) {
1876 new_pos = -1;
1877 *(int *) arg = -1;
1879 } else
1880 new_pos = -1;
1881 break;
1882 case M_PROPERTY_STEP: {
1883 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1);
1884 int step = (step_all > 0) ? 1 : -1;
1885 int max_sub_pos_for_source = -1;
1886 if (!is_cur_source)
1887 new_pos = -1;
1888 while (step_all) {
1889 if (new_pos == -1) {
1890 if (step > 0)
1891 new_pos = offset;
1892 else if (max_sub_pos_for_source == -1) {
1893 // Find max pos for specific source
1894 new_pos = mpctx->global_sub_size - 1;
1895 while (new_pos >= 0 && sub_source(mpctx) != source)
1896 new_pos--;
1897 } else
1898 new_pos = max_sub_pos_for_source;
1899 } else {
1900 new_pos += step;
1901 if (new_pos < offset ||
1902 new_pos >= mpctx->global_sub_size ||
1903 sub_source(mpctx) != source)
1904 new_pos = -1;
1906 step_all -= step;
1908 break;
1910 default:
1911 return M_PROPERTY_NOT_IMPLEMENTED;
1913 return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1916 /// Subtitle delay (RW)
1917 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1918 MPContext *mpctx)
1920 if (!mpctx->sh_video)
1921 return M_PROPERTY_UNAVAILABLE;
1922 return m_property_delay(prop, action, arg, &mpctx->opts.sub_delay);
1925 /// Alignment of text subtitles (RW)
1926 static int mp_property_sub_alignment(m_option_t *prop, int action,
1927 void *arg, MPContext *mpctx)
1929 char *name[] = {
1930 _("top"), _("center"), _("bottom")
1933 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1934 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1935 return M_PROPERTY_UNAVAILABLE;
1937 switch (action) {
1938 case M_PROPERTY_PRINT:
1939 if (!arg)
1940 return M_PROPERTY_ERROR;
1941 M_PROPERTY_CLAMP(prop, sub_alignment);
1942 *(char **) arg = talloc_strdup(NULL, mp_gtext(name[sub_alignment]));
1943 return M_PROPERTY_OK;
1944 case M_PROPERTY_SET:
1945 if (!arg)
1946 return M_PROPERTY_ERROR;
1947 case M_PROPERTY_STEP:
1948 vo_osd_changed(OSDTYPE_SUBTITLE);
1949 default:
1950 return m_property_choice(prop, action, arg, &sub_alignment);
1954 /// Subtitle visibility (RW)
1955 static int mp_property_sub_visibility(m_option_t *prop, int action,
1956 void *arg, MPContext *mpctx)
1958 struct MPOpts *opts = &mpctx->opts;
1960 if (!mpctx->sh_video)
1961 return M_PROPERTY_UNAVAILABLE;
1963 switch (action) {
1964 case M_PROPERTY_SET:
1965 if (!arg)
1966 return M_PROPERTY_ERROR;
1967 case M_PROPERTY_STEP:
1968 vo_osd_changed(OSDTYPE_SUBTITLE);
1969 if (vo_spudec)
1970 vo_osd_changed(OSDTYPE_SPU);
1971 default:
1972 return m_property_flag(prop, action, arg, &opts->sub_visibility);
1976 #ifdef CONFIG_ASS
1977 /// Use margins for libass subtitles (RW)
1978 static int mp_property_ass_use_margins(m_option_t *prop, int action,
1979 void *arg, MPContext *mpctx)
1981 struct MPOpts *opts = &mpctx->opts;
1982 if (!mpctx->sh_video)
1983 return M_PROPERTY_UNAVAILABLE;
1985 switch (action) {
1986 case M_PROPERTY_SET:
1987 if (!arg)
1988 return M_PROPERTY_ERROR;
1989 case M_PROPERTY_STEP:
1990 vo_osd_changed(OSDTYPE_SUBTITLE);
1991 default:
1992 return m_property_flag(prop, action, arg, &opts->ass_use_margins);
1996 static int mp_property_ass_vsfilter_aspect_compat(m_option_t *prop, int action,
1997 void *arg, MPContext *mpctx)
1999 if (!mpctx->sh_video)
2000 return M_PROPERTY_UNAVAILABLE;
2002 switch (action) {
2003 case M_PROPERTY_SET:
2004 if (!arg)
2005 return M_PROPERTY_ERROR;
2006 case M_PROPERTY_STEP:
2007 vo_osd_changed(OSDTYPE_SUBTITLE);
2008 default:
2009 return m_property_flag(prop, action, arg,
2010 &mpctx->opts.ass_vsfilter_aspect_compat);
2014 #endif
2016 /// Show only forced subtitles (RW)
2017 static int mp_property_sub_forced_only(m_option_t *prop, int action,
2018 void *arg, MPContext *mpctx)
2020 if (!vo_spudec)
2021 return M_PROPERTY_UNAVAILABLE;
2023 switch (action) {
2024 case M_PROPERTY_SET:
2025 if (!arg)
2026 return M_PROPERTY_ERROR;
2027 case M_PROPERTY_STEP:
2028 m_property_flag(prop, action, arg, &forced_subs_only);
2029 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
2030 return M_PROPERTY_OK;
2031 default:
2032 return m_property_flag(prop, action, arg, &forced_subs_only);
2037 /// Subtitle scale (RW)
2038 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
2039 MPContext *mpctx)
2041 struct MPOpts *opts = &mpctx->opts;
2043 switch (action) {
2044 case M_PROPERTY_SET:
2045 if (!arg)
2046 return M_PROPERTY_ERROR;
2047 M_PROPERTY_CLAMP(prop, *(float *) arg);
2048 if (opts->ass_enabled)
2049 opts->ass_font_scale = *(float *) arg;
2050 text_font_scale_factor = *(float *) arg;
2051 vo_osd_changed(OSDTYPE_SUBTITLE);
2052 return M_PROPERTY_OK;
2053 case M_PROPERTY_STEP:
2054 if (opts->ass_enabled) {
2055 opts->ass_font_scale += (arg ? *(float *) arg : 0.1);
2056 M_PROPERTY_CLAMP(prop, opts->ass_font_scale);
2058 text_font_scale_factor += (arg ? *(float *) arg : 0.1);
2059 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
2060 vo_osd_changed(OSDTYPE_SUBTITLE);
2061 return M_PROPERTY_OK;
2062 default:
2063 if (opts->ass_enabled)
2064 return m_property_float_ro(prop, action, arg, opts->ass_font_scale);
2065 else
2066 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
2071 #ifdef CONFIG_TV
2073 /// TV color settings (RW)
2074 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
2075 MPContext *mpctx)
2077 int r, val;
2078 tvi_handle_t *tvh = mpctx->demuxer->priv;
2079 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
2080 return M_PROPERTY_UNAVAILABLE;
2082 switch (action) {
2083 case M_PROPERTY_SET:
2084 if (!arg)
2085 return M_PROPERTY_ERROR;
2086 M_PROPERTY_CLAMP(prop, *(int *) arg);
2087 return tv_set_color_options(tvh, prop->offset, *(int *) arg);
2088 case M_PROPERTY_GET:
2089 return tv_get_color_options(tvh, prop->offset, arg);
2090 case M_PROPERTY_STEP:
2091 if ((r = tv_get_color_options(tvh, prop->offset, &val)) >= 0) {
2092 if (!r)
2093 return M_PROPERTY_ERROR;
2094 val += (arg ? *(int *) arg : 1);
2095 M_PROPERTY_CLAMP(prop, val);
2096 return tv_set_color_options(tvh, prop->offset, val);
2098 return M_PROPERTY_ERROR;
2100 return M_PROPERTY_NOT_IMPLEMENTED;
2103 #endif
2105 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
2106 MPContext *mpctx)
2108 int val, result;
2109 int base_ioctl = prop->offset;
2111 for teletext's GET,SET,STEP ioctls this is not 0
2112 SET is GET+1
2113 STEP is GET+2
2115 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2116 return M_PROPERTY_UNAVAILABLE;
2117 if (!base_ioctl)
2118 return M_PROPERTY_ERROR;
2120 switch (action) {
2121 case M_PROPERTY_GET:
2122 if (!arg)
2123 return M_PROPERTY_ERROR;
2124 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2125 break;
2126 case M_PROPERTY_SET:
2127 if (!arg)
2128 return M_PROPERTY_ERROR;
2129 M_PROPERTY_CLAMP(prop, *(int *) arg);
2130 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2131 arg);
2132 break;
2133 case M_PROPERTY_STEP:
2134 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2135 val += (arg ? *(int *) arg : 1);
2136 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2137 &val);
2138 break;
2139 default:
2140 return M_PROPERTY_NOT_IMPLEMENTED;
2143 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2146 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2147 MPContext *mpctx)
2149 int result;
2150 int val;
2152 //with tvh==NULL will fail too
2153 result = mp_property_teletext_common(prop, action, arg, mpctx);
2154 if (result != M_PROPERTY_OK)
2155 return result;
2157 if (teletext_control(mpctx->demuxer->teletext,
2158 prop->offset, &val) == VBI_CONTROL_TRUE && val)
2159 mp_input_set_section(mpctx->input, "teletext");
2160 else
2161 mp_input_set_section(mpctx->input, "tv");
2162 return M_PROPERTY_OK;
2165 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2166 MPContext *mpctx)
2168 int result;
2169 int val;
2170 if (!mpctx->demuxer->teletext)
2171 return M_PROPERTY_UNAVAILABLE;
2172 switch (action) {
2173 case M_PROPERTY_STEP:
2174 //This should be handled separately
2175 val = (arg ? *(int *) arg : 1);
2176 result = teletext_control(mpctx->demuxer->teletext,
2177 TV_VBI_CONTROL_STEP_PAGE, &val);
2178 break;
2179 default:
2180 result = mp_property_teletext_common(prop, action, arg, mpctx);
2182 return result;
2186 /// All properties available in MPlayer.
2187 /** \ingroup Properties
2189 static const m_option_t mp_properties[] = {
2190 // General
2191 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2192 M_OPT_RANGE, 0, 3, NULL },
2193 { "loop", mp_property_loop, CONF_TYPE_INT,
2194 M_OPT_MIN, -1, 0, NULL },
2195 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2196 M_OPT_RANGE, 0.01, 100.0, NULL },
2197 { "filename", mp_property_filename, CONF_TYPE_STRING,
2198 0, 0, 0, NULL },
2199 { "path", mp_property_path, CONF_TYPE_STRING,
2200 0, 0, 0, NULL },
2201 { "media_title", mp_property_media_title, CONF_TYPE_STRING,
2202 0, 0, 0, NULL },
2203 { "stream_path", mp_property_stream_path, CONF_TYPE_STRING,
2204 0, 0, 0, NULL },
2205 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2206 0, 0, 0, NULL },
2207 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2208 M_OPT_MIN, 0, 0, NULL },
2209 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2210 M_OPT_MIN, 0, 0, NULL },
2211 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2212 M_OPT_MIN, 0, 0, NULL },
2213 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2214 M_OPT_MIN, 0, 0, NULL },
2215 { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2216 M_OPT_MIN, 0, 0, NULL },
2217 { "length", mp_property_length, CONF_TYPE_TIME,
2218 M_OPT_MIN, 0, 0, NULL },
2219 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2220 M_OPT_RANGE, 0, 100, NULL },
2221 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2222 M_OPT_MIN, 0, 0, NULL },
2223 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2224 M_OPT_MIN, 0, 0, NULL },
2225 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2226 0, 0, 0, NULL },
2227 { "angle", mp_property_angle, CONF_TYPE_INT,
2228 CONF_RANGE, -2, 10, NULL },
2229 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2230 0, 0, 0, NULL },
2231 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2232 M_OPT_RANGE, 0, 1, NULL },
2233 { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2234 M_OPT_RANGE, 0, 1, NULL },
2235 { "pts_association_mode", mp_property_generic_option, &m_option_type_choice,
2236 0, 0, 0, "pts-association-mode" },
2237 { "hr_seek", mp_property_generic_option, &m_option_type_choice,
2238 0, 0, 0, "hr-seek" },
2240 // Audio
2241 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2242 M_OPT_RANGE, 0, 100, NULL },
2243 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2244 M_OPT_RANGE, 0, 1, NULL },
2245 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2246 M_OPT_RANGE, -100, 100, NULL },
2247 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2248 0, 0, 0, NULL },
2249 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2250 0, 0, 0, NULL },
2251 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2252 0, 0, 0, NULL },
2253 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2254 0, 0, 0, NULL },
2255 { "channels", mp_property_channels, CONF_TYPE_INT,
2256 0, 0, 0, NULL },
2257 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2258 CONF_RANGE, -2, 65535, NULL },
2259 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2260 M_OPT_RANGE, -1, 1, NULL },
2262 // Video
2263 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2264 M_OPT_RANGE, 0, 1, NULL },
2265 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2266 M_OPT_RANGE, 0, 1, NULL },
2267 { "colormatrix", mp_property_colormatrix, &m_option_type_choice,
2268 0, 0, 0, "colormatrix" },
2269 { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice,
2270 0, 0, 0, "colormatrix-input-range" },
2271 { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice,
2272 0, 0, 0, "colormatrix-output-range" },
2273 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2274 M_OPT_RANGE, 0, 1, NULL },
2275 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2276 M_OPT_RANGE, 0, 1, NULL },
2277 { "border", mp_property_border, CONF_TYPE_FLAG,
2278 M_OPT_RANGE, 0, 1, NULL },
2279 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2280 M_OPT_RANGE, 0, 2, NULL },
2281 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2282 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_gamma)},
2283 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2284 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_brightness) },
2285 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2286 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_contrast) },
2287 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2288 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_saturation) },
2289 { "hue", mp_property_gamma, CONF_TYPE_INT,
2290 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_hue) },
2291 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2292 M_OPT_RANGE, 0, 1, NULL },
2293 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2294 M_OPT_RANGE, 0, 1, NULL },
2295 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2296 0, 0, 0, NULL },
2297 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2298 0, 0, 0, NULL },
2299 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2300 0, 0, 0, NULL },
2301 { "width", mp_property_width, CONF_TYPE_INT,
2302 0, 0, 0, NULL },
2303 { "height", mp_property_height, CONF_TYPE_INT,
2304 0, 0, 0, NULL },
2305 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2306 0, 0, 0, NULL },
2307 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2308 0, 0, 0, NULL },
2309 { "switch_video", mp_property_video, CONF_TYPE_INT,
2310 CONF_RANGE, -2, 65535, NULL },
2311 { "switch_program", mp_property_program, CONF_TYPE_INT,
2312 CONF_RANGE, -1, 65535, NULL },
2314 // Subs
2315 { "sub", mp_property_sub, CONF_TYPE_INT,
2316 M_OPT_MIN, -1, 0, NULL },
2317 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2318 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2319 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2320 M_OPT_MIN, -1, 0, NULL },
2321 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2322 M_OPT_MIN, -1, 0, NULL },
2323 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2324 M_OPT_MIN, -1, 0, NULL },
2325 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2326 0, 0, 0, NULL },
2327 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2328 M_OPT_RANGE, 0, 100, NULL },
2329 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2330 M_OPT_RANGE, 0, 2, NULL },
2331 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2332 M_OPT_RANGE, 0, 1, NULL },
2333 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2334 M_OPT_RANGE, 0, 1, NULL },
2335 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2336 M_OPT_RANGE, 0, 100, NULL },
2337 #ifdef CONFIG_ASS
2338 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2339 M_OPT_RANGE, 0, 1, NULL },
2340 { "ass_vsfilter_aspect_compat", mp_property_ass_vsfilter_aspect_compat,
2341 CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL },
2342 #endif
2344 #ifdef CONFIG_TV
2345 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2346 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS },
2347 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2348 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST },
2349 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2350 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION },
2351 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2352 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
2353 #endif
2354 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2355 M_OPT_RANGE, 100, 899, .offset = TV_VBI_CONTROL_GET_PAGE },
2356 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2357 M_OPT_RANGE, 0, 64, .offset = TV_VBI_CONTROL_GET_SUBPAGE },
2358 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2359 M_OPT_RANGE, 0, 1, .offset = TV_VBI_CONTROL_GET_MODE },
2360 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2361 M_OPT_RANGE, 0, 3, .offset = TV_VBI_CONTROL_GET_FORMAT },
2362 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2363 M_OPT_RANGE, 0, 2, .offset = TV_VBI_CONTROL_GET_HALF_PAGE },
2364 { NULL, NULL, NULL, 0, 0, 0, NULL }
2368 int mp_property_do(const char *name, int action, void *val, void *ctx)
2370 return m_property_do(mp_properties, name, action, val, ctx);
2373 char *mp_property_print(const char *name, void *ctx)
2375 char *ret = NULL;
2376 if (mp_property_do(name, M_PROPERTY_PRINT, &ret, ctx) <= 0)
2377 return NULL;
2378 return ret;
2381 char *property_expand_string(MPContext *mpctx, char *str)
2383 return m_properties_expand_string(mp_properties, str, mpctx);
2386 void property_print_help(void)
2388 m_properties_print_help_list(mp_properties);
2392 /* List of default ways to show a property on OSD.
2394 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2395 * a bar showing the current position between min/max values of the
2396 * property. In this case osd_msg is only used for terminal output
2397 * if there is no video; it'll be a label shown together with percentage.
2399 * Otherwise setting osd_msg will show the string on OSD, formatted with
2400 * the text value of the property as argument.
2402 static struct property_osd_display {
2403 /// property name
2404 const char *name;
2405 /// progressbar type
2406 int osd_progbar; // -1 is special value for seek indicators
2407 /// osd msg id if it must be shared
2408 int osd_id;
2409 /// osd msg template
2410 const char *osd_msg;
2411 } property_osd_display[] = {
2412 // general
2413 { "loop", 0, -1, _("Loop: %s") },
2414 { "chapter", -1, -1, NULL },
2415 { "capturing", 0, -1, _("Capturing: %s") },
2416 { "pts_association_mode", 0, -1, "PTS association mode: %s" },
2417 { "hr_seek", 0, -1, "hr-seek: %s" },
2418 { "speed", 0, -1, _("Speed: x %6s") },
2419 // audio
2420 { "volume", OSD_VOLUME, -1, _("Volume") },
2421 { "mute", 0, -1, _("Mute: %s") },
2422 { "audio_delay", 0, -1, _("A-V delay: %s") },
2423 { "switch_audio", 0, -1, _("Audio: %s") },
2424 { "balance", OSD_BALANCE, -1, _("Balance") },
2425 // video
2426 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2427 { "ontop", 0, -1, _("Stay on top: %s") },
2428 { "rootwin", 0, -1, _("Rootwin: %s") },
2429 { "border", 0, -1, _("Border: %s") },
2430 { "framedropping", 0, -1, _("Framedropping: %s") },
2431 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2432 { "colormatrix", 0, -1, _("YUV colormatrix: %s") },
2433 { "colormatrix_input_range", 0, -1, _("YUV input range: %s") },
2434 { "colormatrix_output_range", 0, -1, _("RGB output range: %s") },
2435 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2436 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2437 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2438 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2439 { "hue", OSD_HUE, -1, _("Hue") },
2440 { "vsync", 0, -1, _("VSync: %s") },
2441 // subs
2442 { "sub", 0, -1, _("Subtitles: %s") },
2443 { "sub_source", 0, -1, _("Sub source: %s") },
2444 { "sub_vob", 0, -1, _("Subtitles: %s") },
2445 { "sub_demux", 0, -1, _("Subtitles: %s") },
2446 { "sub_file", 0, -1, _("Subtitles: %s") },
2447 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2448 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2449 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2450 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2451 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2452 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2453 { "ass_vsfilter_aspect_compat", 0, -1,
2454 _("Subtitle VSFilter aspect compat: %s")},
2455 #ifdef CONFIG_TV
2456 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2457 { "tv_hue", OSD_HUE, -1, _("Hue") },
2458 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2459 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2460 #endif
2464 static int show_property_osd(MPContext *mpctx, const char *pname)
2466 struct MPOpts *opts = &mpctx->opts;
2467 int r;
2468 m_option_t *prop;
2469 struct property_osd_display *p;
2471 // look for the command
2472 for (p = property_osd_display; p->name; p++)
2473 if (!strcmp(p->name, pname))
2474 break;
2476 if (!p->name)
2477 return -1;
2479 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2480 return -1;
2482 if (p->osd_progbar == -1)
2483 mpctx->add_osd_seek_info = true;
2484 else if (p->osd_progbar) {
2485 if (prop->type == CONF_TYPE_INT) {
2486 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2487 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2488 prop->min, prop->max, r);
2489 } else if (prop->type == CONF_TYPE_FLOAT) {
2490 float f;
2491 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2492 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2493 prop->min, prop->max, f);
2494 } else {
2495 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2496 "Property use an unsupported type.\n");
2497 return -1;
2499 return 0;
2502 if (p->osd_msg) {
2503 char *val = mp_property_print(pname, mpctx);
2504 if (val) {
2505 int index = p - property_osd_display;
2506 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2507 1, opts->osd_duration, p->osd_msg, val);
2508 talloc_free(val);
2511 return 0;
2516 * Command to property bridge
2518 * It is used to handle most commands that just set a property
2519 * and optionally display something on the OSD.
2520 * Two kinds of commands are handled: adjust or toggle.
2522 * Adjust commands take 1 or 2 parameters: <value> <abs>
2523 * If <abs> is non-zero the property is set to the given value
2524 * otherwise it is adjusted.
2526 * Toggle commands take 0 or 1 parameters. With no parameter
2527 * or a value less than the property minimum it just steps the
2528 * property to its next or previous value respectively.
2529 * Otherwise it sets it to the given value.
2532 /// List of the commands that can be handled by setting a property.
2533 static struct {
2534 /// property name
2535 const char *name;
2536 /// cmd id
2537 int cmd;
2538 /// set/adjust or toggle command
2539 int toggle;
2540 } set_prop_cmd[] = {
2541 // general
2542 { "loop", MP_CMD_LOOP, 0},
2543 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2544 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2545 { "pause", MP_CMD_PAUSE, 0},
2546 { "capturing", MP_CMD_CAPTURING, 1},
2547 // audio
2548 { "volume", MP_CMD_VOLUME, 0},
2549 { "mute", MP_CMD_MUTE, 1},
2550 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2551 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2552 { "balance", MP_CMD_BALANCE, 0},
2553 // video
2554 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2555 { "panscan", MP_CMD_PANSCAN, 0},
2556 { "ontop", MP_CMD_VO_ONTOP, 1},
2557 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2558 { "border", MP_CMD_VO_BORDER, 1},
2559 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2560 { "gamma", MP_CMD_GAMMA, 0},
2561 { "brightness", MP_CMD_BRIGHTNESS, 0},
2562 { "contrast", MP_CMD_CONTRAST, 0},
2563 { "saturation", MP_CMD_SATURATION, 0},
2564 { "hue", MP_CMD_HUE, 0},
2565 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2566 // subs
2567 { "sub", MP_CMD_SUB_SELECT, 1},
2568 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2569 { "sub_vob", MP_CMD_SUB_VOB, 1},
2570 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2571 { "sub_file", MP_CMD_SUB_FILE, 1},
2572 { "sub_pos", MP_CMD_SUB_POS, 0},
2573 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2574 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2575 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2576 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2577 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2578 #ifdef CONFIG_ASS
2579 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2580 #endif
2581 #ifdef CONFIG_TV
2582 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2583 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2584 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2585 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2586 #endif
2590 /// Handle commands that set a property.
2591 static bool set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2593 int i, r;
2594 m_option_t *prop;
2595 const char *pname;
2597 // look for the command
2598 for (i = 0; set_prop_cmd[i].name; i++)
2599 if (set_prop_cmd[i].cmd == cmd->id)
2600 break;
2601 if (!(pname = set_prop_cmd[i].name))
2602 return 0;
2604 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2605 return 0;
2607 // toggle command
2608 if (set_prop_cmd[i].toggle) {
2609 // set to value
2610 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2611 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2612 else {
2613 int dir = 1;
2614 if (cmd->nargs > 0)
2615 dir = -1;
2616 r = mp_property_do(pname, M_PROPERTY_STEP, &dir, mpctx);
2618 } else if (cmd->args[1].v.i) //set
2619 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2620 else // adjust
2621 r = mp_property_do(pname, M_PROPERTY_STEP, &cmd->args[0].v, mpctx);
2623 if (r <= 0)
2624 return 1;
2626 show_property_osd(mpctx, pname);
2628 return 1;
2631 #ifdef CONFIG_DVDNAV
2632 static const struct {
2633 const char *name;
2634 const enum mp_command_type cmd;
2635 } mp_dvdnav_bindings[] = {
2636 { "up", MP_CMD_DVDNAV_UP },
2637 { "down", MP_CMD_DVDNAV_DOWN },
2638 { "left", MP_CMD_DVDNAV_LEFT },
2639 { "right", MP_CMD_DVDNAV_RIGHT },
2640 { "menu", MP_CMD_DVDNAV_MENU },
2641 { "select", MP_CMD_DVDNAV_SELECT },
2642 { "prev", MP_CMD_DVDNAV_PREVMENU },
2643 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2646 * keep old dvdnav sub-command options for a while in order not to
2647 * break slave-mode API too suddenly.
2649 { "1", MP_CMD_DVDNAV_UP },
2650 { "2", MP_CMD_DVDNAV_DOWN },
2651 { "3", MP_CMD_DVDNAV_LEFT },
2652 { "4", MP_CMD_DVDNAV_RIGHT },
2653 { "5", MP_CMD_DVDNAV_MENU },
2654 { "6", MP_CMD_DVDNAV_SELECT },
2655 { "7", MP_CMD_DVDNAV_PREVMENU },
2656 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2657 { NULL, 0 }
2659 #endif
2661 static const char *property_error_string(int error_value)
2663 switch (error_value) {
2664 case M_PROPERTY_ERROR:
2665 return "ERROR";
2666 case M_PROPERTY_UNAVAILABLE:
2667 return "PROPERTY_UNAVAILABLE";
2668 case M_PROPERTY_NOT_IMPLEMENTED:
2669 return "NOT_IMPLEMENTED";
2670 case M_PROPERTY_UNKNOWN:
2671 return "PROPERTY_UNKNOWN";
2672 case M_PROPERTY_DISABLED:
2673 return "DISABLED";
2675 return "UNKNOWN";
2678 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2680 int idx;
2681 int end = start + count;
2682 int after = mpctx->set_of_sub_size - end;
2683 sub_data **subs = mpctx->set_of_subtitles;
2684 struct sh_sub **ass_tracks = mpctx->set_of_ass_tracks;
2685 if (count < 0 || count > mpctx->set_of_sub_size ||
2686 start < 0 || start > mpctx->set_of_sub_size - count) {
2687 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2688 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2689 return;
2691 for (idx = start; idx < end; idx++) {
2692 sub_data *subd = subs[idx];
2693 char *filename = "";
2694 if (subd)
2695 filename = subd->filename;
2696 if (!subd)
2697 filename = ass_tracks[idx]->title;
2698 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2699 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2700 filename_recode(filename));
2701 sub_free(subd);
2702 subs[idx] = NULL;
2703 if (ass_tracks[idx]) {
2704 sub_switchoff(ass_tracks[idx], mpctx->osd);
2705 sub_uninit(ass_tracks[idx]);
2707 talloc_free(ass_tracks[idx]);
2708 ass_tracks[idx] = NULL;
2711 mpctx->global_sub_size -= count;
2712 mpctx->set_of_sub_size -= count;
2713 if (mpctx->set_of_sub_size <= 0)
2714 mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2716 memmove(subs + start, subs + end, after * sizeof(*subs));
2717 memset(subs + start + after, 0, count * sizeof(*subs));
2718 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2719 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2721 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2722 mpctx->global_sub_pos = -2;
2723 mpctx->subdata = NULL;
2724 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2725 } else if (mpctx->set_of_sub_pos >= end) {
2726 mpctx->set_of_sub_pos -= count;
2727 mpctx->global_sub_pos -= count;
2731 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2733 struct MPOpts *opts = &mpctx->opts;
2734 sh_audio_t *const sh_audio = mpctx->sh_audio;
2735 sh_video_t *const sh_video = mpctx->sh_video;
2736 int osd_duration = opts->osd_duration;
2737 int case_fallthrough_hack = 0;
2738 if (set_property_command(mpctx, cmd))
2739 goto old_pause_hack; // was handled already
2740 switch (cmd->id) {
2741 case MP_CMD_SEEK: {
2742 mpctx->add_osd_seek_info = true;
2743 float v = cmd->args[0].v.f;
2744 int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2745 int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0;
2746 if (abs == 2) { // Absolute seek to a timestamp in seconds
2747 queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact);
2748 mpctx->osd_function = v > get_current_time(mpctx) ?
2749 OSD_FFW : OSD_REW;
2750 } else if (abs) { /* Absolute seek by percentage */
2751 queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact);
2752 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2753 } else {
2754 queue_seek(mpctx, MPSEEK_RELATIVE, v, exact);
2755 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2757 break;
2760 case MP_CMD_SET_PROPERTY_OSD:
2761 case_fallthrough_hack = 1;
2763 case MP_CMD_SET_PROPERTY: {
2764 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2765 cmd->args[1].v.s, mpctx);
2766 if (r == M_PROPERTY_UNKNOWN)
2767 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2768 "Unknown property: '%s'\n", cmd->args[0].v.s);
2769 else if (r <= 0)
2770 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2771 "Failed to set property '%s' to '%s'.\n",
2772 cmd->args[0].v.s, cmd->args[1].v.s);
2773 else if (case_fallthrough_hack)
2774 show_property_osd(mpctx, cmd->args[0].v.s);
2775 if (r <= 0)
2776 mp_msg(MSGT_GLOBAL, MSGL_INFO,
2777 "ANS_ERROR=%s\n", property_error_string(r));
2778 break;
2781 case MP_CMD_STEP_PROPERTY_OSD:
2782 case_fallthrough_hack = 1;
2784 case MP_CMD_STEP_PROPERTY: {
2785 void *arg = NULL;
2786 int r, i;
2787 double d;
2788 off_t o;
2789 if (cmd->args[1].v.f) {
2790 m_option_t *prop;
2791 if ((r = mp_property_do(cmd->args[0].v.s,
2792 M_PROPERTY_GET_TYPE,
2793 &prop, mpctx)) <= 0)
2794 goto step_prop_err;
2795 if (prop->type == CONF_TYPE_INT ||
2796 prop->type == CONF_TYPE_FLAG)
2797 i = cmd->args[1].v.f, arg = &i;
2798 else if (prop->type == CONF_TYPE_FLOAT)
2799 arg = &cmd->args[1].v.f;
2800 else if (prop->type == CONF_TYPE_DOUBLE ||
2801 prop->type == CONF_TYPE_TIME)
2802 d = cmd->args[1].v.f, arg = &d;
2803 else if (prop->type == CONF_TYPE_POSITION)
2804 o = cmd->args[1].v.f, arg = &o;
2805 else
2806 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2807 "Ignoring step size stepping property '%s'.\n",
2808 cmd->args[0].v.s);
2810 r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_STEP,
2811 arg, mpctx);
2812 step_prop_err:
2813 if (r == M_PROPERTY_UNKNOWN)
2814 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2815 "Unknown property: '%s'\n", cmd->args[0].v.s);
2816 else if (r <= 0)
2817 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2818 "Failed to increment property '%s' by %f.\n",
2819 cmd->args[0].v.s, cmd->args[1].v.f);
2820 else if (case_fallthrough_hack)
2821 show_property_osd(mpctx, cmd->args[0].v.s);
2822 if (r <= 0)
2823 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2824 property_error_string(r));
2825 break;
2828 case MP_CMD_GET_PROPERTY: {
2829 char *tmp;
2830 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2831 &tmp, mpctx);
2832 if (r <= 0) {
2833 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2834 "Failed to get value of property '%s'.\n",
2835 cmd->args[0].v.s);
2836 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2837 property_error_string(r));
2838 break;
2840 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2841 cmd->args[0].v.s, tmp);
2842 talloc_free(tmp);
2843 break;
2846 case MP_CMD_EDL_MARK:
2847 if (edl_fd) {
2848 float v = get_current_time(mpctx);
2849 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2850 mpctx->begin_skip = v;
2851 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2852 "EDL skip start, press 'i' again to end block.\n");
2853 } else {
2854 if (mpctx->begin_skip > v)
2855 mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
2856 "EDL skip canceled, last start > stop\n");
2857 else {
2858 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2859 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2860 "EDL skip end, line written.\n");
2862 mpctx->begin_skip = MP_NOPTS_VALUE;
2865 break;
2867 case MP_CMD_SWITCH_RATIO:
2868 if (!sh_video)
2869 break;
2870 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2871 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2872 else
2873 opts->movie_aspect = cmd->args[0].v.f;
2874 video_reset_aspect(sh_video);
2875 break;
2877 case MP_CMD_SPEED_INCR: {
2878 float v = cmd->args[0].v.f;
2879 mp_property_do("speed", M_PROPERTY_STEP, &v, mpctx);
2880 show_property_osd(mpctx, "speed");
2881 break;
2884 case MP_CMD_SPEED_MULT:
2885 case_fallthrough_hack = true;
2887 case MP_CMD_SPEED_SET: {
2888 float v = cmd->args[0].v.f;
2889 if (case_fallthrough_hack)
2890 v *= mpctx->opts.playback_speed;
2891 mp_property_do("speed", M_PROPERTY_SET, &v, mpctx);
2892 show_property_osd(mpctx, "speed");
2893 break;
2896 case MP_CMD_FRAME_STEP:
2897 add_step_frame(mpctx);
2898 break;
2900 case MP_CMD_QUIT:
2901 exit_player_with_rc(mpctx, EXIT_QUIT,
2902 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2904 case MP_CMD_PLAY_TREE_STEP: {
2905 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2906 int force = cmd->args[1].v.i;
2909 if (!force && mpctx->playtree_iter) {
2910 play_tree_iter_t *i =
2911 play_tree_iter_new_copy(mpctx->playtree_iter);
2912 if (play_tree_iter_step(i, n, 0) ==
2913 PLAY_TREE_ITER_ENTRY)
2914 mpctx->stop_play =
2915 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2916 play_tree_iter_free(i);
2917 } else
2918 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2919 if (mpctx->stop_play)
2920 mpctx->play_tree_step = n;
2922 break;
2925 case MP_CMD_PLAY_TREE_UP_STEP: {
2926 int n = cmd->args[0].v.i > 0 ? 1 : -1;
2927 int force = cmd->args[1].v.i;
2929 if (!force && mpctx->playtree_iter) {
2930 play_tree_iter_t *i =
2931 play_tree_iter_new_copy(mpctx->playtree_iter);
2932 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
2933 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2934 play_tree_iter_free(i);
2935 } else
2936 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2937 break;
2940 case MP_CMD_PLAY_ALT_SRC_STEP:
2941 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
2942 int v = cmd->args[0].v.i;
2943 if (v > 0
2944 && mpctx->playtree_iter->file <
2945 mpctx->playtree_iter->num_files)
2946 mpctx->stop_play = PT_NEXT_SRC;
2947 else if (v < 0 && mpctx->playtree_iter->file > 1)
2948 mpctx->stop_play = PT_PREV_SRC;
2950 break;
2952 case MP_CMD_SUB_LOG:
2953 log_sub(mpctx);
2954 break;
2956 case MP_CMD_OSD: {
2957 int v = cmd->args[0].v.i;
2958 int max = (opts->term_osd
2959 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
2960 if (opts->osd_level > max)
2961 opts->osd_level = max;
2962 if (v < 0)
2963 opts->osd_level = (opts->osd_level + 1) % (max + 1);
2964 else
2965 opts->osd_level = v > max ? max : v;
2966 /* Show OSD state when disabled, but not when an explicit
2967 argument is given to the OSD command, i.e. in slave mode. */
2968 if (v == -1 && opts->osd_level <= 1)
2969 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
2970 "OSD: %s",
2971 opts->osd_level ? mp_gtext("enabled") :
2972 mp_gtext("disabled"));
2973 else
2974 rm_osd_msg(OSD_MSG_OSD_STATUS);
2975 break;
2978 case MP_CMD_OSD_SHOW_TEXT:
2979 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2980 (cmd->args[1].v.i <
2981 0 ? osd_duration : cmd->args[1].v.i),
2982 "%s", cmd->args[0].v.s);
2983 break;
2985 case MP_CMD_OSD_SHOW_PROPERTY_TEXT: {
2986 char *txt = m_properties_expand_string(mp_properties,
2987 cmd->args[0].v.s,
2988 mpctx);
2989 // if no argument supplied use default osd_duration, else <arg> ms.
2990 if (txt) {
2991 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2992 (cmd->args[1].v.i <
2993 0 ? osd_duration : cmd->args[1].v.i),
2994 "%s", txt);
2995 free(txt);
2997 break;
3000 case MP_CMD_LOADFILE: {
3001 play_tree_t *e = play_tree_new();
3002 play_tree_add_file(e, cmd->args[0].v.s);
3004 if (cmd->args[1].v.i) // append
3005 play_tree_append_entry(mpctx->playtree->child, e);
3006 else {
3007 // Go back to the starting point.
3008 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3009 != PLAY_TREE_ITER_END)
3010 /* NOP */;
3011 play_tree_free_list(mpctx->playtree->child, 1);
3012 play_tree_set_child(mpctx->playtree, e);
3013 pt_iter_goto_head(mpctx->playtree_iter);
3014 mpctx->stop_play = PT_NEXT_SRC;
3016 break;
3019 case MP_CMD_LOADLIST: {
3020 play_tree_t *e = parse_playlist_file(&mpctx->opts,
3021 bstr(cmd->args[0].v.s));
3022 if (!e)
3023 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
3024 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
3025 else {
3026 if (cmd->args[1].v.i) // append
3027 play_tree_append_entry(mpctx->playtree->child, e);
3028 else {
3029 // Go back to the starting point.
3030 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3031 != PLAY_TREE_ITER_END)
3032 /* NOP */;
3033 play_tree_free_list(mpctx->playtree->child, 1);
3034 play_tree_set_child(mpctx->playtree, e);
3035 pt_iter_goto_head(mpctx->playtree_iter);
3036 mpctx->stop_play = PT_NEXT_SRC;
3039 break;
3042 case MP_CMD_STOP:
3043 // Go back to the starting point.
3044 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
3045 PLAY_TREE_ITER_END)
3046 /* NOP */;
3047 mpctx->stop_play = PT_STOP;
3048 break;
3050 case MP_CMD_OSD_SHOW_PROGRESSION: {
3051 set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
3052 set_osd_progressmsg(OSD_MSG_TEXT, 1, osd_duration);
3053 break;
3056 #ifdef CONFIG_RADIO
3057 case MP_CMD_RADIO_STEP_CHANNEL:
3058 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3059 int v = cmd->args[0].v.i;
3060 if (v > 0)
3061 radio_step_channel(mpctx->demuxer->stream,
3062 RADIO_CHANNEL_HIGHER);
3063 else
3064 radio_step_channel(mpctx->demuxer->stream,
3065 RADIO_CHANNEL_LOWER);
3066 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3067 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3068 "Channel: %s",
3069 radio_get_channel_name(mpctx->demuxer->stream));
3072 break;
3074 case MP_CMD_RADIO_SET_CHANNEL:
3075 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3076 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
3077 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3078 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3079 "Channel: %s",
3080 radio_get_channel_name(mpctx->demuxer->stream));
3083 break;
3085 case MP_CMD_RADIO_SET_FREQ:
3086 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3087 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3088 break;
3090 case MP_CMD_RADIO_STEP_FREQ:
3091 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3092 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3093 break;
3094 #endif
3096 #ifdef CONFIG_TV
3097 case MP_CMD_TV_START_SCAN:
3098 if (mpctx->file_format == DEMUXER_TYPE_TV)
3099 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv), 1);
3100 break;
3101 case MP_CMD_TV_SET_FREQ:
3102 if (mpctx->file_format == DEMUXER_TYPE_TV)
3103 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3104 cmd->args[0].v.f * 16.0);
3105 #ifdef CONFIG_PVR
3106 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3107 pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f));
3108 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3109 pvr_get_current_channelname(mpctx->stream),
3110 pvr_get_current_stationname(mpctx->stream));
3112 #endif /* CONFIG_PVR */
3113 break;
3115 case MP_CMD_TV_STEP_FREQ:
3116 if (mpctx->file_format == DEMUXER_TYPE_TV)
3117 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3118 cmd->args[0].v.f * 16.0);
3119 #ifdef CONFIG_PVR
3120 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3121 pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f));
3122 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3123 pvr_get_current_channelname(mpctx->stream),
3124 pvr_get_current_frequency(mpctx->stream));
3126 #endif /* CONFIG_PVR */
3127 break;
3129 case MP_CMD_TV_SET_NORM:
3130 if (mpctx->file_format == DEMUXER_TYPE_TV)
3131 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3132 cmd->args[0].v.s);
3133 break;
3135 case MP_CMD_TV_STEP_CHANNEL:
3136 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3137 int v = cmd->args[0].v.i;
3138 if (v > 0) {
3139 tv_step_channel((tvi_handle_t *) (mpctx->
3140 demuxer->priv),
3141 TV_CHANNEL_HIGHER);
3142 } else {
3143 tv_step_channel((tvi_handle_t *) (mpctx->
3144 demuxer->priv),
3145 TV_CHANNEL_LOWER);
3147 if (tv_channel_list) {
3148 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3149 "Channel: %s", tv_channel_current->name);
3150 //vo_osd_changed(OSDTYPE_SUBTITLE);
3153 #ifdef CONFIG_PVR
3154 else if (mpctx->stream &&
3155 mpctx->stream->type == STREAMTYPE_PVR) {
3156 pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i);
3157 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3158 pvr_get_current_channelname(mpctx->stream),
3159 pvr_get_current_stationname(mpctx->stream));
3161 #endif /* CONFIG_PVR */
3162 #ifdef CONFIG_DVBIN
3163 if (mpctx->stream->type == STREAMTYPE_DVB) {
3164 int dir;
3165 int v = cmd->args[0].v.i;
3167 mpctx->last_dvb_step = v;
3168 if (v > 0)
3169 dir = DVB_CHANNEL_HIGHER;
3170 else
3171 dir = DVB_CHANNEL_LOWER;
3174 if (dvb_step_channel(mpctx->stream, dir)) {
3175 mpctx->stop_play = PT_NEXT_ENTRY;
3176 mpctx->dvbin_reopen = 1;
3179 #endif /* CONFIG_DVBIN */
3180 break;
3182 case MP_CMD_TV_SET_CHANNEL:
3183 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3184 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3185 cmd->args[0].v.s);
3186 if (tv_channel_list) {
3187 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3188 "Channel: %s", tv_channel_current->name);
3189 //vo_osd_changed(OSDTYPE_SUBTITLE);
3192 #ifdef CONFIG_PVR
3193 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3194 pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
3195 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3196 pvr_get_current_channelname(mpctx->stream),
3197 pvr_get_current_stationname(mpctx->stream));
3199 #endif /* CONFIG_PVR */
3200 break;
3202 #ifdef CONFIG_DVBIN
3203 case MP_CMD_DVB_SET_CHANNEL:
3204 if (mpctx->stream->type == STREAMTYPE_DVB) {
3205 mpctx->last_dvb_step = 1;
3207 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3208 cmd->args[0].v.i)) {
3209 mpctx->stop_play = PT_NEXT_ENTRY;
3210 mpctx->dvbin_reopen = 1;
3213 break;
3214 #endif /* CONFIG_DVBIN */
3216 case MP_CMD_TV_LAST_CHANNEL:
3217 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3218 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3219 if (tv_channel_list) {
3220 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3221 "Channel: %s", tv_channel_current->name);
3222 //vo_osd_changed(OSDTYPE_SUBTITLE);
3225 #ifdef CONFIG_PVR
3226 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3227 pvr_set_lastchannel(mpctx->stream);
3228 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3229 pvr_get_current_channelname(mpctx->stream),
3230 pvr_get_current_stationname(mpctx->stream));
3232 #endif /* CONFIG_PVR */
3233 break;
3235 case MP_CMD_TV_STEP_NORM:
3236 if (mpctx->file_format == DEMUXER_TYPE_TV)
3237 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3238 break;
3240 case MP_CMD_TV_STEP_CHANNEL_LIST:
3241 if (mpctx->file_format == DEMUXER_TYPE_TV)
3242 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3243 break;
3244 #endif /* CONFIG_TV */
3245 case MP_CMD_TV_TELETEXT_ADD_DEC:
3246 if (mpctx->demuxer->teletext)
3247 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_ADD_DEC,
3248 &(cmd->args[0].v.s));
3249 break;
3250 case MP_CMD_TV_TELETEXT_GO_LINK:
3251 if (mpctx->demuxer->teletext)
3252 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_GO_LINK,
3253 &(cmd->args[0].v.i));
3254 break;
3256 case MP_CMD_SUB_LOAD:
3257 if (sh_video) {
3258 int n = mpctx->set_of_sub_size;
3259 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3260 if (n != mpctx->set_of_sub_size) {
3261 mpctx->sub_counts[SUB_SOURCE_SUBS]++;
3262 ++mpctx->global_sub_size;
3265 break;
3267 case MP_CMD_SUB_REMOVE:
3268 if (sh_video) {
3269 int v = cmd->args[0].v.i;
3270 if (v < 0)
3271 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3272 else if (v < mpctx->set_of_sub_size)
3273 remove_subtitle_range(mpctx, v, 1);
3275 break;
3277 case MP_CMD_GET_SUB_VISIBILITY:
3278 if (sh_video) {
3279 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3280 "ANS_SUB_VISIBILITY=%d\n", opts->sub_visibility);
3282 break;
3284 case MP_CMD_SCREENSHOT:
3285 screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i);
3286 break;
3288 case MP_CMD_VF_CHANGE_RECTANGLE:
3289 if (!sh_video)
3290 break;
3291 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3292 break;
3294 case MP_CMD_GET_TIME_LENGTH:
3295 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3296 get_time_length(mpctx));
3297 break;
3299 case MP_CMD_GET_FILENAME: {
3300 char *inf = get_metadata(mpctx, META_NAME);
3301 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n", inf);
3302 talloc_free(inf);
3303 break;
3306 case MP_CMD_GET_VIDEO_CODEC: {
3307 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3308 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3309 talloc_free(inf);
3310 break;
3313 case MP_CMD_GET_VIDEO_BITRATE: {
3314 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3315 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3316 talloc_free(inf);
3317 break;
3320 case MP_CMD_GET_VIDEO_RESOLUTION: {
3321 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3322 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3323 talloc_free(inf);
3324 break;
3327 case MP_CMD_GET_AUDIO_CODEC: {
3328 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3329 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3330 talloc_free(inf);
3331 break;
3334 case MP_CMD_GET_AUDIO_BITRATE: {
3335 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3336 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3337 talloc_free(inf);
3338 break;
3341 case MP_CMD_GET_AUDIO_SAMPLES: {
3342 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3343 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3344 talloc_free(inf);
3345 break;
3348 case MP_CMD_GET_META_TITLE: {
3349 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3350 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3351 talloc_free(inf);
3352 break;
3355 case MP_CMD_GET_META_ARTIST: {
3356 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3357 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3358 talloc_free(inf);
3359 break;
3362 case MP_CMD_GET_META_ALBUM: {
3363 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3364 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3365 talloc_free(inf);
3366 break;
3369 case MP_CMD_GET_META_YEAR: {
3370 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3371 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3372 talloc_free(inf);
3373 break;
3376 case MP_CMD_GET_META_COMMENT: {
3377 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3378 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3379 talloc_free(inf);
3380 break;
3383 case MP_CMD_GET_META_TRACK: {
3384 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3385 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3386 talloc_free(inf);
3387 break;
3390 case MP_CMD_GET_META_GENRE: {
3391 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3392 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3393 talloc_free(inf);
3394 break;
3397 case MP_CMD_GET_VO_FULLSCREEN:
3398 if (mpctx->video_out && mpctx->video_out->config_ok)
3399 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3400 break;
3402 case MP_CMD_GET_PERCENT_POS:
3403 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3404 get_percent_pos(mpctx));
3405 break;
3407 case MP_CMD_GET_TIME_POS: {
3408 float pos = get_current_time(mpctx);
3409 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3410 break;
3413 case MP_CMD_RUN:
3414 #ifndef __MINGW32__
3415 if (!fork()) {
3416 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3417 exit(0);
3419 #endif
3420 break;
3422 case MP_CMD_KEYDOWN_EVENTS:
3423 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3424 break;
3426 case MP_CMD_SET_MOUSE_POS: {
3427 int pointer_x, pointer_y;
3428 double dx, dy;
3429 pointer_x = cmd->args[0].v.i;
3430 pointer_y = cmd->args[1].v.i;
3431 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3432 #ifdef CONFIG_DVDNAV
3433 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3434 && dx > 0.0 && dy > 0.0) {
3435 int button = -1;
3436 pointer_x = (int) (dx * (double) sh_video->disp_w);
3437 pointer_y = (int) (dy * (double) sh_video->disp_h);
3438 mp_dvdnav_update_mouse_pos(mpctx->stream,
3439 pointer_x, pointer_y, &button);
3440 if (opts->osd_level > 1 && button > 0)
3441 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3442 "Selected button number %d", button);
3444 #endif
3445 break;
3448 #ifdef CONFIG_DVDNAV
3449 case MP_CMD_DVDNAV: {
3450 int button = -1;
3451 int i;
3452 enum mp_command_type command = 0;
3453 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3454 break;
3456 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3457 if (cmd->args[0].v.s &&
3458 !strcasecmp(cmd->args[0].v.s,
3459 mp_dvdnav_bindings[i].name))
3460 command = mp_dvdnav_bindings[i].cmd;
3462 mp_dvdnav_handle_input(mpctx->stream, command, &button);
3463 if (opts->osd_level > 1 && button > 0)
3464 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3465 "Selected button number %d", button);
3466 break;
3469 case MP_CMD_SWITCH_TITLE:
3470 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3471 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3472 break;
3474 #endif
3476 case MP_CMD_AF_SWITCH:
3477 if (sh_audio) {
3478 af_uninit(mpctx->mixer.afilter);
3479 af_init(mpctx->mixer.afilter);
3481 case MP_CMD_AF_ADD:
3482 case MP_CMD_AF_DEL: {
3483 if (!sh_audio)
3484 break;
3485 char *af_args = strdup(cmd->args[0].v.s);
3486 char *af_commands = af_args;
3487 char *af_command;
3488 af_instance_t *af;
3489 while ((af_command = strsep(&af_commands, ",")) != NULL) {
3490 if (cmd->id == MP_CMD_AF_DEL) {
3491 af = af_get(mpctx->mixer.afilter, af_command);
3492 if (af != NULL)
3493 af_remove(mpctx->mixer.afilter, af);
3494 } else
3495 af_add(mpctx->mixer.afilter, af_command);
3497 reinit_audio_chain(mpctx);
3498 free(af_args);
3499 break;
3501 case MP_CMD_AF_CLR:
3502 if (!sh_audio)
3503 break;
3504 af_uninit(mpctx->mixer.afilter);
3505 af_init(mpctx->mixer.afilter);
3506 reinit_audio_chain(mpctx);
3507 break;
3508 case MP_CMD_AF_CMDLINE:
3509 if (sh_audio) {
3510 af_instance_t *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
3511 if (!af) {
3512 mp_msg(MSGT_CPLAYER, MSGL_WARN,
3513 "Filter '%s' not found in chain.\n", cmd->args[0].v.s);
3514 break;
3516 af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
3517 af_reinit(sh_audio->afilter, af);
3519 break;
3521 default:
3522 mp_msg(MSGT_CPLAYER, MSGL_V,
3523 "Received unknown cmd %s\n", cmd->name);
3526 old_pause_hack:
3527 switch (cmd->pausing) {
3528 case 1: // "pausing"
3529 pause_player(mpctx);
3530 break;
3531 case 3: // "pausing_toggle"
3532 if (mpctx->paused)
3533 unpause_player(mpctx);
3534 else
3535 pause_player(mpctx);
3536 break;