commands: playback speed: adjust video timing after change
[mplayer.git] / command.c
bloba190cdd1c1d62d17715f406478f0a3dec40f1313
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_UP:
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_UP:
297 case M_PROPERTY_STEP_DOWN:
298 opts->playback_speed += (arg ? *(float *) arg : 0.1) *
299 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
300 set:
301 M_PROPERTY_CLAMP(prop, opts->playback_speed);
302 if (opts->playback_speed == orig_speed)
303 return M_PROPERTY_OK;
304 // Adjust time until next frame flip for nosound mode
305 mpctx->time_frame *= orig_speed / opts->playback_speed;
306 if (mpctx->sh_audio) {
307 double a = ao_get_delay(mpctx->ao);
308 mpctx->delay += (opts->playback_speed - orig_speed) * a;
310 reinit_audio_chain(mpctx);
311 return M_PROPERTY_OK;
313 return m_property_float_range(prop, action, arg, &opts->playback_speed);
316 /// filename with path (RO)
317 static int mp_property_path(m_option_t *prop, int action, void *arg,
318 MPContext *mpctx)
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 /// Demuxer name (RO)
337 static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
338 MPContext *mpctx)
340 if (!mpctx->demuxer)
341 return M_PROPERTY_UNAVAILABLE;
342 return m_property_string_ro(prop, action, arg,
343 (char *) mpctx->demuxer->desc->name);
346 /// Position in the stream (RW)
347 static int mp_property_stream_pos(m_option_t *prop, int action, void *arg,
348 MPContext *mpctx)
350 if (!mpctx->demuxer || !mpctx->demuxer->stream)
351 return M_PROPERTY_UNAVAILABLE;
352 if (!arg)
353 return M_PROPERTY_ERROR;
354 switch (action) {
355 case M_PROPERTY_GET:
356 *(off_t *) arg = stream_tell(mpctx->demuxer->stream);
357 return M_PROPERTY_OK;
358 case M_PROPERTY_SET:
359 M_PROPERTY_CLAMP(prop, *(off_t *) arg);
360 stream_seek(mpctx->demuxer->stream, *(off_t *) arg);
361 return M_PROPERTY_OK;
363 return M_PROPERTY_NOT_IMPLEMENTED;
366 /// Stream start offset (RO)
367 static int mp_property_stream_start(m_option_t *prop, int action,
368 void *arg, MPContext *mpctx)
370 if (!mpctx->demuxer || !mpctx->demuxer->stream)
371 return M_PROPERTY_UNAVAILABLE;
372 switch (action) {
373 case M_PROPERTY_GET:
374 *(off_t *) arg = mpctx->demuxer->stream->start_pos;
375 return M_PROPERTY_OK;
377 return M_PROPERTY_NOT_IMPLEMENTED;
380 /// Stream end offset (RO)
381 static int mp_property_stream_end(m_option_t *prop, int action, void *arg,
382 MPContext *mpctx)
384 if (!mpctx->demuxer || !mpctx->demuxer->stream)
385 return M_PROPERTY_UNAVAILABLE;
386 switch (action) {
387 case M_PROPERTY_GET:
388 *(off_t *) arg = mpctx->demuxer->stream->end_pos;
389 return M_PROPERTY_OK;
391 return M_PROPERTY_NOT_IMPLEMENTED;
394 /// Stream length (RO)
395 static int mp_property_stream_length(m_option_t *prop, int action,
396 void *arg, MPContext *mpctx)
398 if (!mpctx->demuxer || !mpctx->demuxer->stream)
399 return M_PROPERTY_UNAVAILABLE;
400 switch (action) {
401 case M_PROPERTY_GET:
402 *(off_t *) arg =
403 mpctx->demuxer->stream->end_pos - mpctx->demuxer->stream->start_pos;
404 return M_PROPERTY_OK;
406 return M_PROPERTY_NOT_IMPLEMENTED;
409 /// Current stream position in seconds (RO)
410 static int mp_property_stream_time_pos(m_option_t *prop, int action,
411 void *arg, MPContext *mpctx)
413 if (!mpctx->demuxer || mpctx->demuxer->stream_pts == MP_NOPTS_VALUE)
414 return M_PROPERTY_UNAVAILABLE;
416 return m_property_time_ro(prop, action, arg, mpctx->demuxer->stream_pts);
420 /// Media length in seconds (RO)
421 static int mp_property_length(m_option_t *prop, int action, void *arg,
422 MPContext *mpctx)
424 double len;
426 if (!mpctx->demuxer ||
427 !(int) (len = get_time_length(mpctx)))
428 return M_PROPERTY_UNAVAILABLE;
430 return m_property_time_ro(prop, action, arg, len);
433 /// Current position in percent (RW)
434 static int mp_property_percent_pos(m_option_t *prop, int action,
435 void *arg, MPContext *mpctx)
437 int pos;
439 if (!mpctx->demuxer)
440 return M_PROPERTY_UNAVAILABLE;
442 switch (action) {
443 case M_PROPERTY_SET:
444 if (!arg)
445 return M_PROPERTY_ERROR;
446 M_PROPERTY_CLAMP(prop, *(int *)arg);
447 pos = *(int *)arg;
448 break;
449 case M_PROPERTY_STEP_UP:
450 case M_PROPERTY_STEP_DOWN:
451 pos = get_percent_pos(mpctx);
452 pos += (arg ? *(int *)arg : 10) *
453 (action == M_PROPERTY_STEP_UP ? 1 : -1);
454 M_PROPERTY_CLAMP(prop, pos);
455 break;
456 default:
457 return m_property_int_ro(prop, action, arg, get_percent_pos(mpctx));
460 queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0);
461 return M_PROPERTY_OK;
464 /// Current position in seconds (RW)
465 static int mp_property_time_pos(m_option_t *prop, int action,
466 void *arg, MPContext *mpctx)
468 if (!(mpctx->sh_video || mpctx->sh_audio))
469 return M_PROPERTY_UNAVAILABLE;
471 switch (action) {
472 case M_PROPERTY_SET:
473 if (!arg)
474 return M_PROPERTY_ERROR;
475 M_PROPERTY_CLAMP(prop, *(double *)arg);
476 queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, 0);
477 return M_PROPERTY_OK;
478 case M_PROPERTY_STEP_UP:
479 case M_PROPERTY_STEP_DOWN:
480 queue_seek(mpctx, MPSEEK_RELATIVE, (arg ? *(double *)arg : 10.0) *
481 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0), 0);
482 return M_PROPERTY_OK;
484 return m_property_time_ro(prop, action, arg, get_current_time(mpctx));
487 /// Current chapter (RW)
488 static int mp_property_chapter(m_option_t *prop, int action, void *arg,
489 MPContext *mpctx)
491 struct MPOpts *opts = &mpctx->opts;
492 int chapter = -1;
493 int step_all;
494 char *chapter_name = NULL;
496 if (mpctx->demuxer)
497 chapter = get_current_chapter(mpctx);
498 if (chapter < -1)
499 return M_PROPERTY_UNAVAILABLE;
501 switch (action) {
502 case M_PROPERTY_GET:
503 if (!arg)
504 return M_PROPERTY_ERROR;
505 *(int *) arg = chapter;
506 return M_PROPERTY_OK;
507 case M_PROPERTY_PRINT: {
508 if (!arg)
509 return M_PROPERTY_ERROR;
510 chapter_name = chapter_display_name(mpctx, chapter);
511 if (!chapter_name)
512 return M_PROPERTY_UNAVAILABLE;
513 *(char **) arg = chapter_name;
514 return M_PROPERTY_OK;
516 case M_PROPERTY_SET:
517 if (!arg)
518 return M_PROPERTY_ERROR;
519 M_PROPERTY_CLAMP(prop, *(int *)arg);
520 step_all = *(int *)arg - chapter;
521 chapter += step_all;
522 break;
523 case M_PROPERTY_STEP_UP:
524 case M_PROPERTY_STEP_DOWN: {
525 step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
526 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
527 chapter += step_all;
528 if (chapter < 0)
529 chapter = 0;
530 break;
532 default:
533 return M_PROPERTY_NOT_IMPLEMENTED;
536 double next_pts = 0;
537 queue_seek(mpctx, MPSEEK_NONE, 0, 0);
538 chapter = seek_chapter(mpctx, chapter, &next_pts);
539 if (chapter >= 0) {
540 if (next_pts > -1.0)
541 queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0);
542 chapter_name = chapter_display_name(mpctx, chapter);
543 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
544 "Chapter: %s", chapter_name);
545 } else if (step_all > 0)
546 queue_seek(mpctx, MPSEEK_RELATIVE, 1000000000, 0);
547 else
548 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
549 "Chapter: (%d) %s", 0, mp_gtext("unknown"));
550 talloc_free(chapter_name);
551 return M_PROPERTY_OK;
554 /// Number of chapters in file
555 static int mp_property_chapters(m_option_t *prop, int action, void *arg,
556 MPContext *mpctx)
558 if (!mpctx->demuxer)
559 return M_PROPERTY_UNAVAILABLE;
560 int count = get_chapter_count(mpctx);
561 return m_property_int_ro(prop, action, arg, count);
564 /// Current dvd angle (RW)
565 static int mp_property_angle(m_option_t *prop, int action, void *arg,
566 MPContext *mpctx)
568 struct MPOpts *opts = &mpctx->opts;
569 int angle = -1;
570 int angles;
572 if (mpctx->demuxer)
573 angle = demuxer_get_current_angle(mpctx->demuxer);
574 if (angle < 0)
575 return M_PROPERTY_UNAVAILABLE;
576 angles = demuxer_angles_count(mpctx->demuxer);
577 if (angles <= 1)
578 return M_PROPERTY_UNAVAILABLE;
580 switch (action) {
581 case M_PROPERTY_GET:
582 if (!arg)
583 return M_PROPERTY_ERROR;
584 *(int *) arg = angle;
585 return M_PROPERTY_OK;
586 case M_PROPERTY_PRINT: {
587 if (!arg)
588 return M_PROPERTY_ERROR;
589 *(char **) arg = talloc_asprintf(NULL, "%d/%d", angle, angles);
590 return M_PROPERTY_OK;
592 case M_PROPERTY_SET:
593 if (!arg)
594 return M_PROPERTY_ERROR;
595 angle = *(int *)arg;
596 M_PROPERTY_CLAMP(prop, angle);
597 break;
598 case M_PROPERTY_STEP_UP:
599 case M_PROPERTY_STEP_DOWN: {
600 int step = 0;
601 if (arg)
602 step = *(int *)arg;
603 if (!step)
604 step = 1;
605 step *= (action == M_PROPERTY_STEP_UP ? 1 : -1);
606 angle += step;
607 if (angle < 1) //cycle
608 angle = angles;
609 else if (angle > angles)
610 angle = 1;
611 break;
613 default:
614 return M_PROPERTY_NOT_IMPLEMENTED;
616 angle = demuxer_set_angle(mpctx->demuxer, angle);
617 if (angle >= 0) {
618 struct sh_video *sh_video = mpctx->demuxer->video->sh;
619 if (sh_video)
620 resync_video_stream(sh_video);
622 struct sh_audio *sh_audio = mpctx->demuxer->audio->sh;
623 if (sh_audio)
624 resync_audio_stream(sh_audio);
627 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
628 "Angle: %d/%d", angle, angles);
629 return M_PROPERTY_OK;
632 /// Demuxer meta data
633 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
634 MPContext *mpctx)
636 m_property_action_t *ka;
637 char *meta;
638 static const m_option_t key_type =
640 "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL
642 if (!mpctx->demuxer)
643 return M_PROPERTY_UNAVAILABLE;
645 switch (action) {
646 case M_PROPERTY_GET:
647 if (!arg)
648 return M_PROPERTY_ERROR;
649 *(char ***)arg = mpctx->demuxer->info;
650 return M_PROPERTY_OK;
651 case M_PROPERTY_KEY_ACTION:
652 if (!arg)
653 return M_PROPERTY_ERROR;
654 ka = arg;
655 if (!(meta = demux_info_get(mpctx->demuxer, ka->key)))
656 return M_PROPERTY_UNKNOWN;
657 switch (ka->action) {
658 case M_PROPERTY_GET:
659 if (!ka->arg)
660 return M_PROPERTY_ERROR;
661 *(char **)ka->arg = meta;
662 return M_PROPERTY_OK;
663 case M_PROPERTY_GET_TYPE:
664 if (!ka->arg)
665 return M_PROPERTY_ERROR;
666 *(const m_option_t **)ka->arg = &key_type;
667 return M_PROPERTY_OK;
670 return M_PROPERTY_NOT_IMPLEMENTED;
673 static int mp_property_pause(m_option_t *prop, int action, void *arg,
674 void *ctx)
676 MPContext *mpctx = ctx;
678 switch (action) {
679 case M_PROPERTY_SET:
680 if (!arg)
681 return M_PROPERTY_ERROR;
682 if (mpctx->paused == (bool) * (int *)arg)
683 return M_PROPERTY_OK;
684 case M_PROPERTY_STEP_UP:
685 case M_PROPERTY_STEP_DOWN:
686 if (mpctx->paused) {
687 unpause_player(mpctx);
688 } else {
689 pause_player(mpctx);
691 return M_PROPERTY_OK;
692 default:
693 return m_property_flag(prop, action, arg, &mpctx->paused);
698 /// Volume (RW)
699 static int mp_property_volume(m_option_t *prop, int action, void *arg,
700 MPContext *mpctx)
703 if (!mpctx->sh_audio)
704 return M_PROPERTY_UNAVAILABLE;
706 switch (action) {
707 case M_PROPERTY_GET:
708 if (!arg)
709 return M_PROPERTY_ERROR;
710 mixer_getbothvolume(&mpctx->mixer, arg);
711 return M_PROPERTY_OK;
712 case M_PROPERTY_PRINT: {
713 float vol;
714 if (!arg)
715 return M_PROPERTY_ERROR;
716 mixer_getbothvolume(&mpctx->mixer, &vol);
717 return m_property_float_range(prop, action, arg, &vol);
719 case M_PROPERTY_STEP_UP:
720 case M_PROPERTY_STEP_DOWN:
721 case M_PROPERTY_SET:
722 break;
723 default:
724 return M_PROPERTY_NOT_IMPLEMENTED;
727 switch (action) {
728 case M_PROPERTY_SET:
729 if (!arg)
730 return M_PROPERTY_ERROR;
731 M_PROPERTY_CLAMP(prop, *(float *) arg);
732 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
733 return M_PROPERTY_OK;
734 case M_PROPERTY_STEP_UP:
735 if (arg && *(float *) arg <= 0)
736 mixer_decvolume(&mpctx->mixer);
737 else
738 mixer_incvolume(&mpctx->mixer);
739 return M_PROPERTY_OK;
740 case M_PROPERTY_STEP_DOWN:
741 if (arg && *(float *) arg <= 0)
742 mixer_incvolume(&mpctx->mixer);
743 else
744 mixer_decvolume(&mpctx->mixer);
745 return M_PROPERTY_OK;
747 return M_PROPERTY_NOT_IMPLEMENTED;
750 /// Mute (RW)
751 static int mp_property_mute(m_option_t *prop, int action, void *arg,
752 MPContext *mpctx)
755 if (!mpctx->sh_audio)
756 return M_PROPERTY_UNAVAILABLE;
758 switch (action) {
759 case M_PROPERTY_SET:
760 if (!arg)
761 return M_PROPERTY_ERROR;
762 mixer_setmute(&mpctx->mixer, *(int *) arg);
763 return M_PROPERTY_OK;
764 case M_PROPERTY_STEP_UP:
765 case M_PROPERTY_STEP_DOWN:
766 mixer_setmute(&mpctx->mixer, !mixer_getmute(&mpctx->mixer));
767 return M_PROPERTY_OK;
768 default:
769 return m_property_flag_ro(prop, action, arg,
770 mixer_getmute(&mpctx->mixer));
774 /// Audio delay (RW)
775 static int mp_property_audio_delay(m_option_t *prop, int action,
776 void *arg, MPContext *mpctx)
778 if (!(mpctx->sh_audio && mpctx->sh_video))
779 return M_PROPERTY_UNAVAILABLE;
780 switch (action) {
781 case M_PROPERTY_SET:
782 case M_PROPERTY_STEP_UP:
783 case M_PROPERTY_STEP_DOWN: {
784 int ret;
785 float delay = audio_delay;
786 ret = m_property_delay(prop, action, arg, &audio_delay);
787 if (ret != M_PROPERTY_OK)
788 return ret;
789 if (mpctx->sh_audio)
790 mpctx->delay -= audio_delay - delay;
792 return M_PROPERTY_OK;
793 default:
794 return m_property_delay(prop, action, arg, &audio_delay);
798 /// Audio codec tag (RO)
799 static int mp_property_audio_format(m_option_t *prop, int action,
800 void *arg, MPContext *mpctx)
802 if (!mpctx->sh_audio)
803 return M_PROPERTY_UNAVAILABLE;
804 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
807 /// Audio codec name (RO)
808 static int mp_property_audio_codec(m_option_t *prop, int action,
809 void *arg, MPContext *mpctx)
811 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
812 return M_PROPERTY_UNAVAILABLE;
813 return m_property_string_ro(prop, action, arg,
814 mpctx->sh_audio->codec->name);
817 /// Audio bitrate (RO)
818 static int mp_property_audio_bitrate(m_option_t *prop, int action,
819 void *arg, MPContext *mpctx)
821 if (!mpctx->sh_audio)
822 return M_PROPERTY_UNAVAILABLE;
823 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
826 /// Samplerate (RO)
827 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
828 MPContext *mpctx)
830 if (!mpctx->sh_audio)
831 return M_PROPERTY_UNAVAILABLE;
832 switch (action) {
833 case M_PROPERTY_PRINT:
834 if (!arg)
835 return M_PROPERTY_ERROR;
836 *(char **)arg = talloc_asprintf(NULL, "%d kHz",
837 mpctx->sh_audio->samplerate / 1000);
838 return M_PROPERTY_OK;
840 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
843 /// Number of channels (RO)
844 static int mp_property_channels(m_option_t *prop, int action, void *arg,
845 MPContext *mpctx)
847 if (!mpctx->sh_audio)
848 return M_PROPERTY_UNAVAILABLE;
849 switch (action) {
850 case M_PROPERTY_PRINT:
851 if (!arg)
852 return M_PROPERTY_ERROR;
853 switch (mpctx->sh_audio->channels) {
854 case 1:
855 *(char **) arg = talloc_strdup(NULL, "mono");
856 break;
857 case 2:
858 *(char **) arg = talloc_strdup(NULL, "stereo");
859 break;
860 default:
861 *(char **) arg = talloc_asprintf(NULL, "%d channels",
862 mpctx->sh_audio->channels);
864 return M_PROPERTY_OK;
866 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
869 /// Balance (RW)
870 static int mp_property_balance(m_option_t *prop, int action, void *arg,
871 MPContext *mpctx)
873 float bal;
875 switch (action) {
876 case M_PROPERTY_GET:
877 if (!arg)
878 return M_PROPERTY_ERROR;
879 mixer_getbalance(&mpctx->mixer, arg);
880 return M_PROPERTY_OK;
881 case M_PROPERTY_PRINT: {
882 char **str = arg;
883 if (!arg)
884 return M_PROPERTY_ERROR;
885 mixer_getbalance(&mpctx->mixer, &bal);
886 if (bal == 0.f)
887 *str = talloc_strdup(NULL, "center");
888 else if (bal == -1.f)
889 *str = talloc_strdup(NULL, "left only");
890 else if (bal == 1.f)
891 *str = talloc_strdup(NULL, "right only");
892 else {
893 unsigned right = (bal + 1.f) / 2.f * 100.f;
894 *str = talloc_asprintf(NULL, "left %d%%, right %d%%",
895 100 - right, right);
897 return M_PROPERTY_OK;
899 case M_PROPERTY_STEP_UP:
900 case M_PROPERTY_STEP_DOWN:
901 mixer_getbalance(&mpctx->mixer, &bal);
902 bal += (arg ? *(float *)arg : .1f) *
903 (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
904 M_PROPERTY_CLAMP(prop, bal);
905 mixer_setbalance(&mpctx->mixer, bal);
906 return M_PROPERTY_OK;
907 case M_PROPERTY_SET:
908 if (!arg)
909 return M_PROPERTY_ERROR;
910 M_PROPERTY_CLAMP(prop, *(float *)arg);
911 mixer_setbalance(&mpctx->mixer, *(float *)arg);
912 return M_PROPERTY_OK;
914 return M_PROPERTY_NOT_IMPLEMENTED;
917 /// Selected audio id (RW)
918 static int mp_property_audio(m_option_t *prop, int action, void *arg,
919 MPContext *mpctx)
921 int current_id, tmp;
922 if (!mpctx->demuxer || !mpctx->d_audio)
923 return M_PROPERTY_UNAVAILABLE;
924 struct sh_audio *sh = mpctx->sh_audio;
925 current_id = sh ? sh->aid : -2;
927 switch (action) {
928 case M_PROPERTY_GET:
929 if (!arg)
930 return M_PROPERTY_ERROR;
931 *(int *) arg = current_id;
932 return M_PROPERTY_OK;
933 case M_PROPERTY_PRINT:
934 if (!arg)
935 return M_PROPERTY_ERROR;
937 if (current_id < 0)
938 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
939 else if (sh && (sh->lang || sh->title)) {
940 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
941 if (sh->title)
942 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
943 current_id, lang, sh->title);
944 else
945 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id,
946 lang);
947 } else {
948 char lang[40];
949 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
950 if (0) ;
951 #ifdef CONFIG_DVDREAD
952 else if (mpctx->stream->type == STREAMTYPE_DVD) {
953 int code = dvd_lang_from_aid(mpctx->stream, current_id);
954 if (code) {
955 lang[0] = code >> 8;
956 lang[1] = code;
957 lang[2] = 0;
960 #endif
962 #ifdef CONFIG_DVDNAV
963 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
964 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
965 #endif
966 *(char **)arg = talloc_asprintf(NULL, "(%d) %s", current_id, lang);
968 return M_PROPERTY_OK;
970 case M_PROPERTY_STEP_UP:
971 case M_PROPERTY_SET:
972 if (action == M_PROPERTY_SET && arg)
973 tmp = *((int *) arg);
974 else
975 tmp = -1;
976 int new_id = demuxer_switch_audio(mpctx->d_audio->demuxer, tmp);
977 if (new_id != current_id)
978 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
979 if (new_id != current_id && new_id >= 0) {
980 sh_audio_t *sh2;
981 sh2 = mpctx->d_audio->demuxer->a_streams[mpctx->d_audio->id];
982 sh2->ds = mpctx->d_audio;
983 mpctx->sh_audio = sh2;
984 reinit_audio_chain(mpctx);
986 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", new_id);
987 return M_PROPERTY_OK;
988 default:
989 return M_PROPERTY_NOT_IMPLEMENTED;
994 /// Selected video id (RW)
995 static int mp_property_video(m_option_t *prop, int action, void *arg,
996 MPContext *mpctx)
998 struct MPOpts *opts = &mpctx->opts;
999 int current_id, tmp;
1000 if (!mpctx->demuxer || !mpctx->d_video)
1001 return M_PROPERTY_UNAVAILABLE;
1002 current_id = mpctx->sh_video ? mpctx->sh_video->vid : -2;
1004 switch (action) {
1005 case M_PROPERTY_GET:
1006 if (!arg)
1007 return M_PROPERTY_ERROR;
1008 *(int *) arg = current_id;
1009 return M_PROPERTY_OK;
1010 case M_PROPERTY_PRINT:
1011 if (!arg)
1012 return M_PROPERTY_ERROR;
1014 if (current_id < 0)
1015 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1016 else {
1017 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", current_id,
1018 mp_gtext("unknown"));
1020 return M_PROPERTY_OK;
1022 case M_PROPERTY_STEP_UP:
1023 case M_PROPERTY_SET:
1024 if (action == M_PROPERTY_SET && arg)
1025 tmp = *((int *) arg);
1026 else
1027 tmp = -1;
1028 int new_id = demuxer_switch_video(mpctx->d_video->demuxer, tmp);
1029 if (new_id != current_id)
1030 uninit_player(mpctx, INITIALIZED_VCODEC |
1031 (opts->fixed_vo && new_id >= 0 ? 0 : INITIALIZED_VO));
1032 if (new_id != current_id && new_id >= 0) {
1033 sh_video_t *sh2;
1034 sh2 = mpctx->d_video->demuxer->v_streams[mpctx->d_video->id];
1035 sh2->ds = mpctx->d_video;
1036 mpctx->sh_video = sh2;
1037 reinit_video_chain(mpctx);
1039 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", new_id);
1040 return M_PROPERTY_OK;
1042 default:
1043 return M_PROPERTY_NOT_IMPLEMENTED;
1047 static int mp_property_program(m_option_t *prop, int action, void *arg,
1048 MPContext *mpctx)
1050 demux_program_t prog;
1052 switch (action) {
1053 case M_PROPERTY_STEP_UP:
1054 case M_PROPERTY_SET:
1055 if (action == M_PROPERTY_SET && arg)
1056 prog.progid = *((int *) arg);
1057 else
1058 prog.progid = -1;
1059 if (demux_control(mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
1060 &prog) == DEMUXER_CTRL_NOTIMPL)
1061 return M_PROPERTY_ERROR;
1063 if (prog.aid < 0 && prog.vid < 0) {
1064 mp_msg(MSGT_CPLAYER, MSGL_ERR,
1065 "Selected program contains no audio or video streams!\n");
1066 return M_PROPERTY_ERROR;
1068 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
1069 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
1070 return M_PROPERTY_OK;
1072 default:
1073 return M_PROPERTY_NOT_IMPLEMENTED;
1078 /// Fullscreen state (RW)
1079 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
1080 MPContext *mpctx)
1083 if (!mpctx->video_out)
1084 return M_PROPERTY_UNAVAILABLE;
1086 switch (action) {
1087 case M_PROPERTY_SET:
1088 if (!arg)
1089 return M_PROPERTY_ERROR;
1090 M_PROPERTY_CLAMP(prop, *(int *) arg);
1091 if (vo_fs == !!*(int *) arg)
1092 return M_PROPERTY_OK;
1093 case M_PROPERTY_STEP_UP:
1094 case M_PROPERTY_STEP_DOWN:
1095 if (mpctx->video_out->config_ok)
1096 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1097 mpctx->opts.fullscreen = vo_fs;
1098 return M_PROPERTY_OK;
1099 default:
1100 return m_property_flag(prop, action, arg, &vo_fs);
1104 static int mp_property_deinterlace(m_option_t *prop, int action,
1105 void *arg, MPContext *mpctx)
1107 int deinterlace;
1108 vf_instance_t *vf;
1109 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1110 return M_PROPERTY_UNAVAILABLE;
1111 vf = mpctx->sh_video->vfilter;
1112 switch (action) {
1113 case M_PROPERTY_GET:
1114 if (!arg)
1115 return M_PROPERTY_ERROR;
1116 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1117 return M_PROPERTY_OK;
1118 case M_PROPERTY_SET:
1119 if (!arg)
1120 return M_PROPERTY_ERROR;
1121 M_PROPERTY_CLAMP(prop, *(int *) arg);
1122 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1123 return M_PROPERTY_OK;
1124 case M_PROPERTY_STEP_UP:
1125 case M_PROPERTY_STEP_DOWN:
1126 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1127 deinterlace = !deinterlace;
1128 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1129 return M_PROPERTY_OK;
1131 int value = 0;
1132 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1133 return m_property_flag_ro(prop, action, arg, value);
1136 static int colormatrix_property_helper(m_option_t *prop, int action,
1137 void *arg, MPContext *mpctx)
1139 int r = mp_property_generic_option(prop, action, arg, mpctx);
1140 // testing for an actual change is too much effort
1141 switch (action) {
1142 case M_PROPERTY_SET:
1143 case M_PROPERTY_STEP_UP:
1144 case M_PROPERTY_STEP_DOWN:
1145 if (mpctx->sh_video)
1146 set_video_colorspace(mpctx->sh_video);
1147 break;
1149 return r;
1152 static int mp_property_colormatrix(m_option_t *prop, int action, void *arg,
1153 MPContext *mpctx)
1155 struct MPOpts *opts = &mpctx->opts;
1156 switch (action) {
1157 case M_PROPERTY_PRINT:
1158 if (!arg)
1159 return M_PROPERTY_ERROR;
1160 struct mp_csp_details actual = { .format = -1 };
1161 char *req_csp = mp_csp_names[opts->requested_colorspace];
1162 char *real_csp = NULL;
1163 if (mpctx->sh_video) {
1164 struct vf_instance *vf = mpctx->sh_video->vfilter;
1165 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1166 real_csp = mp_csp_names[actual.format];
1167 } else {
1168 real_csp = "Unknown";
1171 char *res;
1172 if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) {
1173 // Caveat: doesn't handle the case when the autodetected colorspace
1174 // is different from the actual colorspace as used by the
1175 // VO - the OSD will display the VO colorspace without
1176 // indication that it doesn't match the requested colorspace.
1177 res = talloc_asprintf(NULL, "Auto (%s)", real_csp);
1178 } else if (opts->requested_colorspace == actual.format || !real_csp) {
1179 res = talloc_strdup(NULL, req_csp);
1180 } else
1181 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1182 req_csp, real_csp);
1183 *(char **)arg = res;
1184 return M_PROPERTY_OK;
1185 default:;
1186 return colormatrix_property_helper(prop, action, arg, mpctx);
1190 static int levels_property_helper(int offset, m_option_t *prop, int action,
1191 void *arg, MPContext *mpctx)
1193 char *optname = prop->priv;
1194 const struct m_option *opt = m_config_get_option(mpctx->mconfig,
1195 bstr(optname));
1196 int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts);
1198 switch (action) {
1199 case M_PROPERTY_PRINT:
1200 if (!arg)
1201 return M_PROPERTY_ERROR;
1202 struct mp_csp_details actual = {0};
1203 int actual_level = -1;
1204 char *req_level = m_option_print(opt, valptr);
1205 char *real_level = NULL;
1206 if (mpctx->sh_video) {
1207 struct vf_instance *vf = mpctx->sh_video->vfilter;
1208 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) {
1209 actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset);
1210 real_level = m_option_print(opt, &actual_level);
1211 } else {
1212 real_level = talloc_strdup(NULL, "Unknown");
1215 char *res;
1216 if (*valptr == MP_CSP_LEVELS_AUTO && real_level) {
1217 res = talloc_asprintf(NULL, "Auto (%s)", real_level);
1218 } else if (*valptr == actual_level || !real_level) {
1219 res = talloc_strdup(NULL, real_level);
1220 } else
1221 res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"),
1222 req_level, real_level);
1223 talloc_free(req_level);
1224 talloc_free(real_level);
1225 *(char **)arg = res;
1226 return M_PROPERTY_OK;
1227 default:;
1228 return colormatrix_property_helper(prop, action, arg, mpctx);
1232 static int mp_property_colormatrix_input_range(m_option_t *prop, int action,
1233 void *arg, MPContext *mpctx)
1235 return levels_property_helper(offsetof(struct mp_csp_details, levels_in),
1236 prop, action, arg, mpctx);
1239 static int mp_property_colormatrix_output_range(m_option_t *prop, int action,
1240 void *arg, MPContext *mpctx)
1242 return levels_property_helper(offsetof(struct mp_csp_details, levels_out),
1243 prop, action, arg, mpctx);
1246 static int mp_property_capture(m_option_t *prop, int action,
1247 void *arg, MPContext *mpctx)
1249 struct MPOpts *opts = &mpctx->opts;
1251 if (!mpctx->stream)
1252 return M_PROPERTY_UNAVAILABLE;
1254 if (!opts->capture_dump) {
1255 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1256 "Capturing not enabled (forgot -capture parameter?)\n");
1257 return M_PROPERTY_ERROR;
1260 int capturing = !!mpctx->stream->capture_file;
1262 int ret = m_property_flag(prop, action, arg, &capturing);
1263 if (ret == M_PROPERTY_OK && capturing != !!mpctx->stream->capture_file) {
1264 if (capturing) {
1265 mpctx->stream->capture_file = fopen(opts->stream_dump_name, "wb");
1266 if (!mpctx->stream->capture_file) {
1267 mp_tmsg(MSGT_GLOBAL, MSGL_ERR,
1268 "Error opening capture file: %s\n", strerror(errno));
1269 ret = M_PROPERTY_ERROR;
1271 } else {
1272 fclose(mpctx->stream->capture_file);
1273 mpctx->stream->capture_file = NULL;
1277 return ret;
1280 /// Panscan (RW)
1281 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1282 MPContext *mpctx)
1285 if (!mpctx->video_out
1286 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1287 return M_PROPERTY_UNAVAILABLE;
1289 switch (action) {
1290 case M_PROPERTY_SET:
1291 if (!arg)
1292 return M_PROPERTY_ERROR;
1293 M_PROPERTY_CLAMP(prop, *(float *) arg);
1294 vo_panscan = *(float *) arg;
1295 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1296 return M_PROPERTY_OK;
1297 case M_PROPERTY_STEP_UP:
1298 case M_PROPERTY_STEP_DOWN:
1299 vo_panscan += (arg ? *(float *) arg : 0.1) *
1300 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1301 if (vo_panscan > 1)
1302 vo_panscan = 1;
1303 else if (vo_panscan < 0)
1304 vo_panscan = 0;
1305 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1306 return M_PROPERTY_OK;
1307 default:
1308 return m_property_float_range(prop, action, arg, &vo_panscan);
1312 /// Helper to set vo flags.
1313 /** \ingroup PropertyImplHelper
1315 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1316 int vo_ctrl, int *vo_var, MPContext *mpctx)
1319 if (!mpctx->video_out)
1320 return M_PROPERTY_UNAVAILABLE;
1322 switch (action) {
1323 case M_PROPERTY_SET:
1324 if (!arg)
1325 return M_PROPERTY_ERROR;
1326 M_PROPERTY_CLAMP(prop, *(int *) arg);
1327 if (*vo_var == !!*(int *) arg)
1328 return M_PROPERTY_OK;
1329 case M_PROPERTY_STEP_UP:
1330 case M_PROPERTY_STEP_DOWN:
1331 if (mpctx->video_out->config_ok)
1332 vo_control(mpctx->video_out, vo_ctrl, 0);
1333 return M_PROPERTY_OK;
1334 default:
1335 return m_property_flag(prop, action, arg, vo_var);
1339 /// Window always on top (RW)
1340 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1341 MPContext *mpctx)
1343 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1344 &mpctx->opts.vo_ontop, mpctx);
1347 /// Display in the root window (RW)
1348 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1349 MPContext *mpctx)
1351 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1352 &vo_rootwin, mpctx);
1355 /// Show window borders (RW)
1356 static int mp_property_border(m_option_t *prop, int action, void *arg,
1357 MPContext *mpctx)
1359 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1360 &vo_border, mpctx);
1363 /// Framedropping state (RW)
1364 static int mp_property_framedropping(m_option_t *prop, int action,
1365 void *arg, MPContext *mpctx)
1368 if (!mpctx->sh_video)
1369 return M_PROPERTY_UNAVAILABLE;
1371 switch (action) {
1372 case M_PROPERTY_PRINT:
1373 if (!arg)
1374 return M_PROPERTY_ERROR;
1375 *(char **) arg = talloc_strdup(NULL, frame_dropping == 1 ?
1376 mp_gtext("enabled") :
1377 (frame_dropping == 2 ? mp_gtext("hard") :
1378 mp_gtext("disabled")));
1379 return M_PROPERTY_OK;
1380 default:
1381 return m_property_choice(prop, action, arg, &frame_dropping);
1385 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1386 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1387 MPContext *mpctx)
1389 int *gamma = (int *)((char *)&mpctx->opts + prop->offset);
1390 int r, val;
1392 if (!mpctx->sh_video)
1393 return M_PROPERTY_UNAVAILABLE;
1395 if (gamma[0] == 1000) {
1396 gamma[0] = 0;
1397 get_video_colors(mpctx->sh_video, prop->name, gamma);
1400 switch (action) {
1401 case M_PROPERTY_SET:
1402 if (!arg)
1403 return M_PROPERTY_ERROR;
1404 M_PROPERTY_CLAMP(prop, *(int *) arg);
1405 *gamma = *(int *) arg;
1406 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1407 if (r <= 0)
1408 break;
1409 return r;
1410 case M_PROPERTY_GET:
1411 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1412 if (!arg)
1413 return M_PROPERTY_ERROR;
1414 *(int *)arg = val;
1415 return M_PROPERTY_OK;
1417 break;
1418 case M_PROPERTY_STEP_UP:
1419 case M_PROPERTY_STEP_DOWN:
1420 *gamma += (arg ? *(int *) arg : 1) *
1421 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1422 M_PROPERTY_CLAMP(prop, *gamma);
1423 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1424 if (r <= 0)
1425 break;
1426 return r;
1427 default:
1428 return M_PROPERTY_NOT_IMPLEMENTED;
1431 #ifdef CONFIG_TV
1432 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1433 int l = strlen(prop->name);
1434 char tv_prop[3 + l + 1];
1435 sprintf(tv_prop, "tv_%s", prop->name);
1436 return mp_property_do(tv_prop, action, arg, mpctx);
1438 #endif
1440 return M_PROPERTY_UNAVAILABLE;
1443 /// VSync (RW)
1444 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1445 MPContext *mpctx)
1447 return m_property_flag(prop, action, arg, &vo_vsync);
1450 /// Video codec tag (RO)
1451 static int mp_property_video_format(m_option_t *prop, int action,
1452 void *arg, MPContext *mpctx)
1454 char *meta;
1455 if (!mpctx->sh_video)
1456 return M_PROPERTY_UNAVAILABLE;
1457 switch (action) {
1458 case M_PROPERTY_PRINT:
1459 if (!arg)
1460 return M_PROPERTY_ERROR;
1461 switch (mpctx->sh_video->format) {
1462 case 0x10000001:
1463 meta = talloc_strdup(NULL, "mpeg1");
1464 break;
1465 case 0x10000002:
1466 meta = talloc_strdup(NULL, "mpeg2");
1467 break;
1468 case 0x10000004:
1469 meta = talloc_strdup(NULL, "mpeg4");
1470 break;
1471 case 0x10000005:
1472 meta = talloc_strdup(NULL, "h264");
1473 break;
1474 default:
1475 if (mpctx->sh_video->format >= 0x20202020) {
1476 meta = talloc_asprintf(NULL, "%.4s",
1477 (char *) &mpctx->sh_video->format);
1478 } else
1479 meta = talloc_asprintf(NULL, "0x%08X", mpctx->sh_video->format);
1481 *(char **)arg = meta;
1482 return M_PROPERTY_OK;
1484 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1487 /// Video codec name (RO)
1488 static int mp_property_video_codec(m_option_t *prop, int action,
1489 void *arg, MPContext *mpctx)
1491 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1492 return M_PROPERTY_UNAVAILABLE;
1493 return m_property_string_ro(prop, action, arg,
1494 mpctx->sh_video->codec->name);
1498 /// Video bitrate (RO)
1499 static int mp_property_video_bitrate(m_option_t *prop, int action,
1500 void *arg, MPContext *mpctx)
1502 if (!mpctx->sh_video)
1503 return M_PROPERTY_UNAVAILABLE;
1504 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1507 /// Video display width (RO)
1508 static int mp_property_width(m_option_t *prop, int action, void *arg,
1509 MPContext *mpctx)
1511 if (!mpctx->sh_video)
1512 return M_PROPERTY_UNAVAILABLE;
1513 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1516 /// Video display height (RO)
1517 static int mp_property_height(m_option_t *prop, int action, void *arg,
1518 MPContext *mpctx)
1520 if (!mpctx->sh_video)
1521 return M_PROPERTY_UNAVAILABLE;
1522 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1525 /// Video fps (RO)
1526 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1527 MPContext *mpctx)
1529 if (!mpctx->sh_video)
1530 return M_PROPERTY_UNAVAILABLE;
1531 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1534 /// Video aspect (RO)
1535 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1536 MPContext *mpctx)
1538 if (!mpctx->sh_video)
1539 return M_PROPERTY_UNAVAILABLE;
1540 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1544 /// Text subtitle position (RW)
1545 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1546 MPContext *mpctx)
1548 switch (action) {
1549 case M_PROPERTY_SET:
1550 if (!arg)
1551 return M_PROPERTY_ERROR;
1552 case M_PROPERTY_STEP_UP:
1553 case M_PROPERTY_STEP_DOWN:
1554 vo_osd_changed(OSDTYPE_SUBTITLE);
1555 default:
1556 return m_property_int_range(prop, action, arg, &sub_pos);
1560 /// Selected subtitles (RW)
1561 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1562 MPContext *mpctx)
1564 struct MPOpts *opts = &mpctx->opts;
1565 demux_stream_t *const d_sub = mpctx->d_sub;
1566 int source = -1, reset_spu av_unused = 0; // used under CONFIG_DVDREAD
1567 int source_pos = -1;
1569 update_global_sub_size(mpctx);
1570 const int global_sub_size = mpctx->global_sub_size;
1572 if (global_sub_size <= 0)
1573 return M_PROPERTY_UNAVAILABLE;
1575 switch (action) {
1576 case M_PROPERTY_GET:
1577 if (!arg)
1578 return M_PROPERTY_ERROR;
1579 *(int *) arg = mpctx->global_sub_pos;
1580 return M_PROPERTY_OK;
1581 case M_PROPERTY_PRINT:
1582 if (!arg)
1583 return M_PROPERTY_ERROR;
1584 char *sub_name = NULL;
1585 if (mpctx->subdata)
1586 sub_name = mpctx->subdata->filename;
1587 if (sub_source(mpctx) == SUB_SOURCE_SUBS && mpctx->osd->sh_sub)
1588 sub_name = mpctx->osd->sh_sub->title;
1589 if (!sub_name && mpctx->subdata)
1590 sub_name = mpctx->subdata->filename;
1591 if (sub_name) {
1592 const char *tmp = mp_basename(sub_name);
1594 *(char **) arg = talloc_asprintf(NULL, "(%d) %s%s",
1595 mpctx->set_of_sub_pos + 1,
1596 strlen(tmp) < 20 ? "" : "...",
1597 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1598 return M_PROPERTY_OK;
1600 #ifdef CONFIG_DVDNAV
1601 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1602 if (vo_spudec && opts->sub_id >= 0) {
1603 unsigned char lang[3];
1604 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id,
1605 lang)) {
1606 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1607 opts->sub_id, lang);
1608 return M_PROPERTY_OK;
1612 #endif
1614 if ((d_sub->demuxer->type == DEMUXER_TYPE_MATROSKA
1615 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF
1616 || d_sub->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1617 || d_sub->demuxer->type == DEMUXER_TYPE_OGG)
1618 && d_sub->sh && opts->sub_id >= 0) {
1619 struct sh_sub *sh = d_sub->sh;
1620 char *lang = sh->lang ? sh->lang : mp_gtext("unknown");
1621 if (sh->title)
1622 *(char **)arg = talloc_asprintf(NULL, "(%d) %s (\"%s\")",
1623 opts->sub_id, lang, sh->title);
1624 else
1625 *(char **)arg = talloc_asprintf(NULL, "(%d) %s",
1626 opts->sub_id, lang);
1627 return M_PROPERTY_OK;
1630 if (vo_vobsub && vobsub_id >= 0) {
1631 const char *language = mp_gtext("unknown");
1632 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1633 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1634 vobsub_id, language ? language : mp_gtext("unknown"));
1635 return M_PROPERTY_OK;
1637 #ifdef CONFIG_DVDREAD
1638 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1639 && opts->sub_id >= 0) {
1640 char lang[3];
1641 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1642 lang[0] = code >> 8;
1643 lang[1] = code;
1644 lang[2] = 0;
1645 *(char **) arg = talloc_asprintf(NULL, "(%d) %s",
1646 opts->sub_id, lang);
1647 return M_PROPERTY_OK;
1649 #endif
1650 if (opts->sub_id >= 0) {
1651 *(char **) arg = talloc_asprintf(NULL, "(%d) %s", opts->sub_id,
1652 mp_gtext("unknown"));
1653 return M_PROPERTY_OK;
1655 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1656 return M_PROPERTY_OK;
1658 case M_PROPERTY_SET:
1659 if (!arg)
1660 return M_PROPERTY_ERROR;
1661 if (*(int *) arg < -1)
1662 *(int *) arg = -1;
1663 else if (*(int *) arg >= global_sub_size)
1664 *(int *) arg = global_sub_size - 1;
1665 mpctx->global_sub_pos = *(int *) arg;
1666 break;
1667 case M_PROPERTY_STEP_UP:
1668 mpctx->global_sub_pos += 2;
1669 mpctx->global_sub_pos =
1670 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1671 break;
1672 case M_PROPERTY_STEP_DOWN:
1673 mpctx->global_sub_pos += global_sub_size + 1;
1674 mpctx->global_sub_pos =
1675 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1676 break;
1677 default:
1678 return M_PROPERTY_NOT_IMPLEMENTED;
1681 if (mpctx->global_sub_pos >= 0) {
1682 source = sub_source(mpctx);
1683 source_pos = sub_source_pos(mpctx);
1686 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1687 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1688 global_sub_size,
1689 mpctx->sub_counts[SUB_SOURCE_VOBSUB],
1690 mpctx->sub_counts[SUB_SOURCE_SUBS],
1691 mpctx->sub_counts[SUB_SOURCE_DEMUX],
1692 mpctx->global_sub_pos, source);
1694 mpctx->set_of_sub_pos = -1;
1695 mpctx->subdata = NULL;
1697 vobsub_id = -1;
1698 opts->sub_id = -1;
1699 if (d_sub) {
1700 if (d_sub->id > -2)
1701 reset_spu = 1;
1702 d_sub->id = -2;
1704 uninit_player(mpctx, INITIALIZED_SUB);
1706 if (source == SUB_SOURCE_VOBSUB)
1707 vobsub_id = vobsub_get_id_by_index(vo_vobsub, source_pos);
1708 else if (source == SUB_SOURCE_SUBS) {
1709 mpctx->set_of_sub_pos = source_pos;
1710 if (opts->ass_enabled
1711 && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos]) {
1712 sub_init(mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos],
1713 mpctx->osd);
1714 mpctx->initialized_flags |= INITIALIZED_SUB;
1715 } else
1716 mpctx->subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1717 vo_osd_changed(OSDTYPE_SUBTITLE);
1718 } else if (source == SUB_SOURCE_DEMUX) {
1719 opts->sub_id = source_pos;
1720 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1721 int i = 0;
1722 // default: assume 1:1 mapping of sid and stream id
1723 d_sub->id = opts->sub_id;
1724 d_sub->sh = mpctx->d_sub->demuxer->s_streams[d_sub->id];
1725 ds_free_packs(d_sub);
1726 for (i = 0; i < MAX_S_STREAMS; i++) {
1727 sh_sub_t *sh = mpctx->d_sub->demuxer->s_streams[i];
1728 if (sh && sh->sid == opts->sub_id) {
1729 d_sub->id = i;
1730 d_sub->sh = sh;
1731 break;
1734 if (d_sub->sh && d_sub->id >= 0) {
1735 sh_sub_t *sh = d_sub->sh;
1736 if (sh->type == 'v')
1737 init_vo_spudec(mpctx);
1738 else {
1739 sub_init(sh, mpctx->osd);
1740 mpctx->initialized_flags |= INITIALIZED_SUB;
1742 } else {
1743 d_sub->id = -2;
1744 d_sub->sh = NULL;
1748 #ifdef CONFIG_DVDREAD
1749 if (vo_spudec
1750 && (mpctx->stream->type == STREAMTYPE_DVD
1751 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1752 && opts->sub_id < 0 && reset_spu) {
1753 d_sub->id = -2;
1754 d_sub->sh = NULL;
1756 #endif
1758 update_subtitles(mpctx, 0, true);
1760 return M_PROPERTY_OK;
1763 /// Selected sub source (RW)
1764 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1765 MPContext *mpctx)
1767 int source;
1768 update_global_sub_size(mpctx);
1769 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1770 return M_PROPERTY_UNAVAILABLE;
1772 switch (action) {
1773 case M_PROPERTY_GET:
1774 if (!arg)
1775 return M_PROPERTY_ERROR;
1776 *(int *) arg = sub_source(mpctx);
1777 return M_PROPERTY_OK;
1778 case M_PROPERTY_PRINT:
1779 if (!arg)
1780 return M_PROPERTY_ERROR;
1781 char *sourcename;
1782 switch (sub_source(mpctx)) {
1783 case SUB_SOURCE_SUBS:
1784 sourcename = mp_gtext("file");
1785 break;
1786 case SUB_SOURCE_VOBSUB:
1787 sourcename = mp_gtext("vobsub");
1788 break;
1789 case SUB_SOURCE_DEMUX:
1790 sourcename = mp_gtext("embedded");
1791 break;
1792 default:
1793 sourcename = mp_gtext("disabled");
1795 *(char **)arg = talloc_strdup(NULL, sourcename);
1796 return M_PROPERTY_OK;
1797 case M_PROPERTY_SET:
1798 if (!arg)
1799 return M_PROPERTY_ERROR;
1800 M_PROPERTY_CLAMP(prop, *(int *)arg);
1801 if (*(int *) arg < 0)
1802 mpctx->global_sub_pos = -1;
1803 else if (*(int *) arg != sub_source(mpctx)) {
1804 int new_pos = sub_pos_by_source(mpctx, *(int *)arg);
1805 if (new_pos == -1)
1806 return M_PROPERTY_UNAVAILABLE;
1807 mpctx->global_sub_pos = new_pos;
1809 break;
1810 case M_PROPERTY_STEP_UP:
1811 case M_PROPERTY_STEP_DOWN: {
1812 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1813 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1814 int step = (step_all > 0) ? 1 : -1;
1815 int cur_source = sub_source(mpctx);
1816 source = cur_source;
1817 while (step_all) {
1818 source += step;
1819 if (source >= SUB_SOURCES)
1820 source = -1;
1821 else if (source < -1)
1822 source = SUB_SOURCES - 1;
1823 if (source == cur_source || source == -1 ||
1824 mpctx->sub_counts[source])
1825 step_all -= step;
1827 if (source == cur_source)
1828 return M_PROPERTY_OK;
1829 if (source == -1)
1830 mpctx->global_sub_pos = -1;
1831 else
1832 mpctx->global_sub_pos = sub_pos_by_source(mpctx, source);
1833 break;
1835 default:
1836 return M_PROPERTY_NOT_IMPLEMENTED;
1838 --mpctx->global_sub_pos;
1839 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1842 /// Selected subtitles from specific source (RW)
1843 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1844 MPContext *mpctx)
1846 int source, is_cur_source, offset, new_pos;
1847 update_global_sub_size(mpctx);
1848 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1849 return M_PROPERTY_UNAVAILABLE;
1851 if (!strcmp(prop->name, "sub_file"))
1852 source = SUB_SOURCE_SUBS;
1853 else if (!strcmp(prop->name, "sub_vob"))
1854 source = SUB_SOURCE_VOBSUB;
1855 else if (!strcmp(prop->name, "sub_demux"))
1856 source = SUB_SOURCE_DEMUX;
1857 else
1858 return M_PROPERTY_ERROR;
1860 offset = sub_pos_by_source(mpctx, source);
1861 if (offset < 0)
1862 return M_PROPERTY_UNAVAILABLE;
1864 is_cur_source = sub_source(mpctx) == source;
1865 new_pos = mpctx->global_sub_pos;
1866 switch (action) {
1867 case M_PROPERTY_GET:
1868 if (!arg)
1869 return M_PROPERTY_ERROR;
1870 if (is_cur_source) {
1871 *(int *) arg = sub_source_pos(mpctx);
1872 if (source == SUB_SOURCE_VOBSUB)
1873 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1874 } else
1875 *(int *) arg = -1;
1876 return M_PROPERTY_OK;
1877 case M_PROPERTY_PRINT:
1878 if (!arg)
1879 return M_PROPERTY_ERROR;
1880 if (is_cur_source)
1881 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1882 *(char **) arg = talloc_strdup(NULL, mp_gtext("disabled"));
1883 return M_PROPERTY_OK;
1884 case M_PROPERTY_SET:
1885 if (!arg)
1886 return M_PROPERTY_ERROR;
1887 if (*(int *) arg >= 0) {
1888 int index = *(int *)arg;
1889 if (source == SUB_SOURCE_VOBSUB)
1890 index = vobsub_get_index_by_id(vo_vobsub, index);
1891 new_pos = offset + index;
1892 if (index < 0 || index > mpctx->sub_counts[source]) {
1893 new_pos = -1;
1894 *(int *) arg = -1;
1896 } else
1897 new_pos = -1;
1898 break;
1899 case M_PROPERTY_STEP_UP:
1900 case M_PROPERTY_STEP_DOWN: {
1901 int step_all = (arg && *(int *)arg != 0 ? *(int *)arg : 1)
1902 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1903 int step = (step_all > 0) ? 1 : -1;
1904 int max_sub_pos_for_source = -1;
1905 if (!is_cur_source)
1906 new_pos = -1;
1907 while (step_all) {
1908 if (new_pos == -1) {
1909 if (step > 0)
1910 new_pos = offset;
1911 else if (max_sub_pos_for_source == -1) {
1912 // Find max pos for specific source
1913 new_pos = mpctx->global_sub_size - 1;
1914 while (new_pos >= 0 && sub_source(mpctx) != source)
1915 new_pos--;
1916 } else
1917 new_pos = max_sub_pos_for_source;
1918 } else {
1919 new_pos += step;
1920 if (new_pos < offset ||
1921 new_pos >= mpctx->global_sub_size ||
1922 sub_source(mpctx) != source)
1923 new_pos = -1;
1925 step_all -= step;
1927 break;
1929 default:
1930 return M_PROPERTY_NOT_IMPLEMENTED;
1932 return mp_property_sub(prop, M_PROPERTY_SET, &new_pos, mpctx);
1935 /// Subtitle delay (RW)
1936 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1937 MPContext *mpctx)
1939 if (!mpctx->sh_video)
1940 return M_PROPERTY_UNAVAILABLE;
1941 return m_property_delay(prop, action, arg, &sub_delay);
1944 /// Alignment of text subtitles (RW)
1945 static int mp_property_sub_alignment(m_option_t *prop, int action,
1946 void *arg, MPContext *mpctx)
1948 char *name[] = {
1949 _("top"), _("center"), _("bottom")
1952 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1953 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1954 return M_PROPERTY_UNAVAILABLE;
1956 switch (action) {
1957 case M_PROPERTY_PRINT:
1958 if (!arg)
1959 return M_PROPERTY_ERROR;
1960 M_PROPERTY_CLAMP(prop, sub_alignment);
1961 *(char **) arg = talloc_strdup(NULL, mp_gtext(name[sub_alignment]));
1962 return M_PROPERTY_OK;
1963 case M_PROPERTY_SET:
1964 if (!arg)
1965 return M_PROPERTY_ERROR;
1966 case M_PROPERTY_STEP_UP:
1967 case M_PROPERTY_STEP_DOWN:
1968 vo_osd_changed(OSDTYPE_SUBTITLE);
1969 default:
1970 return m_property_choice(prop, action, arg, &sub_alignment);
1974 /// Subtitle visibility (RW)
1975 static int mp_property_sub_visibility(m_option_t *prop, int action,
1976 void *arg, MPContext *mpctx)
1978 struct MPOpts *opts = &mpctx->opts;
1980 if (!mpctx->sh_video)
1981 return M_PROPERTY_UNAVAILABLE;
1983 switch (action) {
1984 case M_PROPERTY_SET:
1985 if (!arg)
1986 return M_PROPERTY_ERROR;
1987 case M_PROPERTY_STEP_UP:
1988 case M_PROPERTY_STEP_DOWN:
1989 vo_osd_changed(OSDTYPE_SUBTITLE);
1990 if (vo_spudec)
1991 vo_osd_changed(OSDTYPE_SPU);
1992 default:
1993 return m_property_flag(prop, action, arg, &opts->sub_visibility);
1997 #ifdef CONFIG_ASS
1998 /// Use margins for libass subtitles (RW)
1999 static int mp_property_ass_use_margins(m_option_t *prop, int action,
2000 void *arg, MPContext *mpctx)
2002 struct MPOpts *opts = &mpctx->opts;
2003 if (!mpctx->sh_video)
2004 return M_PROPERTY_UNAVAILABLE;
2006 switch (action) {
2007 case M_PROPERTY_SET:
2008 if (!arg)
2009 return M_PROPERTY_ERROR;
2010 case M_PROPERTY_STEP_UP:
2011 case M_PROPERTY_STEP_DOWN:
2012 vo_osd_changed(OSDTYPE_SUBTITLE);
2013 default:
2014 return m_property_flag(prop, action, arg, &opts->ass_use_margins);
2018 static int mp_property_ass_vsfilter_aspect_compat(m_option_t *prop, int action,
2019 void *arg, MPContext *mpctx)
2021 if (!mpctx->sh_video)
2022 return M_PROPERTY_UNAVAILABLE;
2024 switch (action) {
2025 case M_PROPERTY_SET:
2026 if (!arg)
2027 return M_PROPERTY_ERROR;
2028 case M_PROPERTY_STEP_UP:
2029 case M_PROPERTY_STEP_DOWN:
2030 vo_osd_changed(OSDTYPE_SUBTITLE);
2031 default:
2032 return m_property_flag(prop, action, arg,
2033 &mpctx->opts.ass_vsfilter_aspect_compat);
2037 #endif
2039 /// Show only forced subtitles (RW)
2040 static int mp_property_sub_forced_only(m_option_t *prop, int action,
2041 void *arg, MPContext *mpctx)
2043 if (!vo_spudec)
2044 return M_PROPERTY_UNAVAILABLE;
2046 switch (action) {
2047 case M_PROPERTY_SET:
2048 if (!arg)
2049 return M_PROPERTY_ERROR;
2050 case M_PROPERTY_STEP_UP:
2051 case M_PROPERTY_STEP_DOWN:
2052 m_property_flag(prop, action, arg, &forced_subs_only);
2053 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
2054 return M_PROPERTY_OK;
2055 default:
2056 return m_property_flag(prop, action, arg, &forced_subs_only);
2061 /// Subtitle scale (RW)
2062 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
2063 MPContext *mpctx)
2065 struct MPOpts *opts = &mpctx->opts;
2067 switch (action) {
2068 case M_PROPERTY_SET:
2069 if (!arg)
2070 return M_PROPERTY_ERROR;
2071 M_PROPERTY_CLAMP(prop, *(float *) arg);
2072 if (opts->ass_enabled)
2073 opts->ass_font_scale = *(float *) arg;
2074 text_font_scale_factor = *(float *) arg;
2075 vo_osd_changed(OSDTYPE_SUBTITLE);
2076 return M_PROPERTY_OK;
2077 case M_PROPERTY_STEP_UP:
2078 case M_PROPERTY_STEP_DOWN:
2079 if (opts->ass_enabled) {
2080 opts->ass_font_scale += (arg ? *(float *) arg : 0.1) *
2081 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2082 M_PROPERTY_CLAMP(prop, opts->ass_font_scale);
2084 text_font_scale_factor += (arg ? *(float *) arg : 0.1) *
2085 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
2086 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
2087 vo_osd_changed(OSDTYPE_SUBTITLE);
2088 return M_PROPERTY_OK;
2089 default:
2090 if (opts->ass_enabled)
2091 return m_property_float_ro(prop, action, arg, opts->ass_font_scale);
2092 else
2093 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
2098 #ifdef CONFIG_TV
2100 /// TV color settings (RW)
2101 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
2102 MPContext *mpctx)
2104 int r, val;
2105 tvi_handle_t *tvh = mpctx->demuxer->priv;
2106 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
2107 return M_PROPERTY_UNAVAILABLE;
2109 switch (action) {
2110 case M_PROPERTY_SET:
2111 if (!arg)
2112 return M_PROPERTY_ERROR;
2113 M_PROPERTY_CLAMP(prop, *(int *) arg);
2114 return tv_set_color_options(tvh, prop->offset, *(int *) arg);
2115 case M_PROPERTY_GET:
2116 return tv_get_color_options(tvh, prop->offset, arg);
2117 case M_PROPERTY_STEP_UP:
2118 case M_PROPERTY_STEP_DOWN:
2119 if ((r = tv_get_color_options(tvh, prop->offset, &val)) >= 0) {
2120 if (!r)
2121 return M_PROPERTY_ERROR;
2122 val += (arg ? *(int *) arg : 1) *
2123 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2124 M_PROPERTY_CLAMP(prop, val);
2125 return tv_set_color_options(tvh, prop->offset, val);
2127 return M_PROPERTY_ERROR;
2129 return M_PROPERTY_NOT_IMPLEMENTED;
2132 #endif
2134 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
2135 MPContext *mpctx)
2137 int val, result;
2138 int base_ioctl = prop->offset;
2140 for teletext's GET,SET,STEP ioctls this is not 0
2141 SET is GET+1
2142 STEP is GET+2
2144 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
2145 return M_PROPERTY_UNAVAILABLE;
2146 if (!base_ioctl)
2147 return M_PROPERTY_ERROR;
2149 switch (action) {
2150 case M_PROPERTY_GET:
2151 if (!arg)
2152 return M_PROPERTY_ERROR;
2153 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
2154 break;
2155 case M_PROPERTY_SET:
2156 if (!arg)
2157 return M_PROPERTY_ERROR;
2158 M_PROPERTY_CLAMP(prop, *(int *) arg);
2159 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2160 arg);
2161 break;
2162 case M_PROPERTY_STEP_UP:
2163 case M_PROPERTY_STEP_DOWN:
2164 result = teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
2165 val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2166 -1 : 1);
2167 result = teletext_control(mpctx->demuxer->teletext, base_ioctl + 1,
2168 &val);
2169 break;
2170 default:
2171 return M_PROPERTY_NOT_IMPLEMENTED;
2174 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
2177 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
2178 MPContext *mpctx)
2180 int result;
2181 int val;
2183 //with tvh==NULL will fail too
2184 result = mp_property_teletext_common(prop, action, arg, mpctx);
2185 if (result != M_PROPERTY_OK)
2186 return result;
2188 if (teletext_control(mpctx->demuxer->teletext,
2189 prop->offset, &val) == VBI_CONTROL_TRUE && val)
2190 mp_input_set_section(mpctx->input, "teletext");
2191 else
2192 mp_input_set_section(mpctx->input, "tv");
2193 return M_PROPERTY_OK;
2196 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2197 MPContext *mpctx)
2199 int result;
2200 int val;
2201 if (!mpctx->demuxer->teletext)
2202 return M_PROPERTY_UNAVAILABLE;
2203 switch (action) {
2204 case M_PROPERTY_STEP_UP:
2205 case M_PROPERTY_STEP_DOWN:
2206 //This should be handled separately
2207 val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ?
2208 -1 : 1);
2209 result = teletext_control(mpctx->demuxer->teletext,
2210 TV_VBI_CONTROL_STEP_PAGE, &val);
2211 break;
2212 default:
2213 result = mp_property_teletext_common(prop, action, arg, mpctx);
2215 return result;
2219 /// All properties available in MPlayer.
2220 /** \ingroup Properties
2222 static const m_option_t mp_properties[] = {
2223 // General
2224 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2225 M_OPT_RANGE, 0, 3, NULL },
2226 { "loop", mp_property_loop, CONF_TYPE_INT,
2227 M_OPT_MIN, -1, 0, NULL },
2228 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2229 M_OPT_RANGE, 0.01, 100.0, NULL },
2230 { "filename", mp_property_filename, CONF_TYPE_STRING,
2231 0, 0, 0, NULL },
2232 { "path", mp_property_path, CONF_TYPE_STRING,
2233 0, 0, 0, NULL },
2234 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2235 0, 0, 0, NULL },
2236 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2237 M_OPT_MIN, 0, 0, NULL },
2238 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2239 M_OPT_MIN, 0, 0, NULL },
2240 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2241 M_OPT_MIN, 0, 0, NULL },
2242 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2243 M_OPT_MIN, 0, 0, NULL },
2244 { "stream_time_pos", mp_property_stream_time_pos, CONF_TYPE_TIME,
2245 M_OPT_MIN, 0, 0, NULL },
2246 { "length", mp_property_length, CONF_TYPE_TIME,
2247 M_OPT_MIN, 0, 0, NULL },
2248 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2249 M_OPT_RANGE, 0, 100, NULL },
2250 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2251 M_OPT_MIN, 0, 0, NULL },
2252 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2253 M_OPT_MIN, 0, 0, NULL },
2254 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2255 0, 0, 0, NULL },
2256 { "angle", mp_property_angle, CONF_TYPE_INT,
2257 CONF_RANGE, -2, 10, NULL },
2258 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2259 0, 0, 0, NULL },
2260 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2261 M_OPT_RANGE, 0, 1, NULL },
2262 { "capturing", mp_property_capture, CONF_TYPE_FLAG,
2263 M_OPT_RANGE, 0, 1, NULL },
2264 { "pts_association_mode", mp_property_generic_option, &m_option_type_choice,
2265 0, 0, 0, "pts-association-mode" },
2266 { "hr_seek", mp_property_generic_option, &m_option_type_choice,
2267 0, 0, 0, "hr-seek" },
2269 // Audio
2270 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2271 M_OPT_RANGE, 0, 100, NULL },
2272 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2273 M_OPT_RANGE, 0, 1, NULL },
2274 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2275 M_OPT_RANGE, -100, 100, NULL },
2276 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2277 0, 0, 0, NULL },
2278 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2279 0, 0, 0, NULL },
2280 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2281 0, 0, 0, NULL },
2282 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2283 0, 0, 0, NULL },
2284 { "channels", mp_property_channels, CONF_TYPE_INT,
2285 0, 0, 0, NULL },
2286 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2287 CONF_RANGE, -2, 65535, NULL },
2288 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2289 M_OPT_RANGE, -1, 1, NULL },
2291 // Video
2292 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2293 M_OPT_RANGE, 0, 1, NULL },
2294 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2295 M_OPT_RANGE, 0, 1, NULL },
2296 { "colormatrix", mp_property_colormatrix, &m_option_type_choice,
2297 0, 0, 0, "colormatrix" },
2298 { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice,
2299 0, 0, 0, "colormatrix-input-range" },
2300 { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice,
2301 0, 0, 0, "colormatrix-output-range" },
2302 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2303 M_OPT_RANGE, 0, 1, NULL },
2304 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2305 M_OPT_RANGE, 0, 1, NULL },
2306 { "border", mp_property_border, CONF_TYPE_FLAG,
2307 M_OPT_RANGE, 0, 1, NULL },
2308 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2309 M_OPT_RANGE, 0, 2, NULL },
2310 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2311 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_gamma)},
2312 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2313 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_brightness) },
2314 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2315 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_contrast) },
2316 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2317 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_saturation) },
2318 { "hue", mp_property_gamma, CONF_TYPE_INT,
2319 M_OPT_RANGE, -100, 100, .offset = offsetof(struct MPOpts, vo_gamma_hue) },
2320 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2321 M_OPT_RANGE, 0, 1, NULL },
2322 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2323 M_OPT_RANGE, 0, 1, NULL },
2324 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2325 0, 0, 0, NULL },
2326 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2327 0, 0, 0, NULL },
2328 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2329 0, 0, 0, NULL },
2330 { "width", mp_property_width, CONF_TYPE_INT,
2331 0, 0, 0, NULL },
2332 { "height", mp_property_height, CONF_TYPE_INT,
2333 0, 0, 0, NULL },
2334 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2335 0, 0, 0, NULL },
2336 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2337 0, 0, 0, NULL },
2338 { "switch_video", mp_property_video, CONF_TYPE_INT,
2339 CONF_RANGE, -2, 65535, NULL },
2340 { "switch_program", mp_property_program, CONF_TYPE_INT,
2341 CONF_RANGE, -1, 65535, NULL },
2343 // Subs
2344 { "sub", mp_property_sub, CONF_TYPE_INT,
2345 M_OPT_MIN, -1, 0, NULL },
2346 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2347 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2348 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2349 M_OPT_MIN, -1, 0, NULL },
2350 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2351 M_OPT_MIN, -1, 0, NULL },
2352 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2353 M_OPT_MIN, -1, 0, NULL },
2354 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2355 0, 0, 0, NULL },
2356 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2357 M_OPT_RANGE, 0, 100, NULL },
2358 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2359 M_OPT_RANGE, 0, 2, NULL },
2360 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2361 M_OPT_RANGE, 0, 1, NULL },
2362 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2363 M_OPT_RANGE, 0, 1, NULL },
2364 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2365 M_OPT_RANGE, 0, 100, NULL },
2366 #ifdef CONFIG_ASS
2367 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2368 M_OPT_RANGE, 0, 1, NULL },
2369 { "ass_vsfilter_aspect_compat", mp_property_ass_vsfilter_aspect_compat,
2370 CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL },
2371 #endif
2373 #ifdef CONFIG_TV
2374 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2375 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_BRIGHTNESS },
2376 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2377 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_CONTRAST },
2378 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2379 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_SATURATION },
2380 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2381 M_OPT_RANGE, -100, 100, .offset = TV_COLOR_HUE },
2382 #endif
2383 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2384 M_OPT_RANGE, 100, 899, .offset = TV_VBI_CONTROL_GET_PAGE },
2385 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2386 M_OPT_RANGE, 0, 64, .offset = TV_VBI_CONTROL_GET_SUBPAGE },
2387 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2388 M_OPT_RANGE, 0, 1, .offset = TV_VBI_CONTROL_GET_MODE },
2389 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2390 M_OPT_RANGE, 0, 3, .offset = TV_VBI_CONTROL_GET_FORMAT },
2391 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2392 M_OPT_RANGE, 0, 2, .offset = TV_VBI_CONTROL_GET_HALF_PAGE },
2393 { NULL, NULL, NULL, 0, 0, 0, NULL }
2397 int mp_property_do(const char *name, int action, void *val, void *ctx)
2399 return m_property_do(mp_properties, name, action, val, ctx);
2402 char *mp_property_print(const char *name, void *ctx)
2404 char *ret = NULL;
2405 if (mp_property_do(name, M_PROPERTY_PRINT, &ret, ctx) <= 0)
2406 return NULL;
2407 return ret;
2410 char *property_expand_string(MPContext *mpctx, char *str)
2412 return m_properties_expand_string(mp_properties, str, mpctx);
2415 void property_print_help(void)
2417 m_properties_print_help_list(mp_properties);
2421 /* List of default ways to show a property on OSD.
2423 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2424 * a bar showing the current position between min/max values of the
2425 * property. In this case osd_msg is only used for terminal output
2426 * if there is no video; it'll be a label shown together with percentage.
2428 * Otherwise setting osd_msg will show the string on OSD, formatted with
2429 * the text value of the property as argument.
2431 static struct property_osd_display {
2432 /// property name
2433 const char *name;
2434 /// progressbar type
2435 int osd_progbar; // -1 is special value for seek indicators
2436 /// osd msg id if it must be shared
2437 int osd_id;
2438 /// osd msg template
2439 const char *osd_msg;
2440 } property_osd_display[] = {
2441 // general
2442 { "loop", 0, -1, _("Loop: %s") },
2443 { "chapter", -1, -1, NULL },
2444 { "capturing", 0, -1, _("Capturing: %s") },
2445 { "pts_association_mode", 0, -1, "PTS association mode: %s" },
2446 { "hr_seek", 0, -1, "hr-seek: %s" },
2447 { "speed", 0, -1, _("Speed: x %6s") },
2448 // audio
2449 { "volume", OSD_VOLUME, -1, _("Volume") },
2450 { "mute", 0, -1, _("Mute: %s") },
2451 { "audio_delay", 0, -1, _("A-V delay: %s") },
2452 { "switch_audio", 0, -1, _("Audio: %s") },
2453 { "balance", OSD_BALANCE, -1, _("Balance") },
2454 // video
2455 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2456 { "ontop", 0, -1, _("Stay on top: %s") },
2457 { "rootwin", 0, -1, _("Rootwin: %s") },
2458 { "border", 0, -1, _("Border: %s") },
2459 { "framedropping", 0, -1, _("Framedropping: %s") },
2460 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2461 { "colormatrix", 0, -1, _("YUV colormatrix: %s") },
2462 { "colormatrix_input_range", 0, -1, _("YUV input range: %s") },
2463 { "colormatrix_output_range", 0, -1, _("RGB output range: %s") },
2464 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2465 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2466 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2467 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2468 { "hue", OSD_HUE, -1, _("Hue") },
2469 { "vsync", 0, -1, _("VSync: %s") },
2470 // subs
2471 { "sub", 0, -1, _("Subtitles: %s") },
2472 { "sub_source", 0, -1, _("Sub source: %s") },
2473 { "sub_vob", 0, -1, _("Subtitles: %s") },
2474 { "sub_demux", 0, -1, _("Subtitles: %s") },
2475 { "sub_file", 0, -1, _("Subtitles: %s") },
2476 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2477 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2478 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2479 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2480 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2481 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2482 { "ass_vsfilter_aspect_compat", 0, -1,
2483 _("Subtitle VSFilter aspect compat: %s")},
2484 #ifdef CONFIG_TV
2485 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2486 { "tv_hue", OSD_HUE, -1, _("Hue") },
2487 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2488 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2489 #endif
2493 static int show_property_osd(MPContext *mpctx, const char *pname)
2495 struct MPOpts *opts = &mpctx->opts;
2496 int r;
2497 m_option_t *prop;
2498 struct property_osd_display *p;
2500 // look for the command
2501 for (p = property_osd_display; p->name; p++)
2502 if (!strcmp(p->name, pname))
2503 break;
2505 if (!p->name)
2506 return -1;
2508 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2509 return -1;
2511 if (p->osd_progbar == -1)
2512 mpctx->add_osd_seek_info = true;
2513 else if (p->osd_progbar) {
2514 if (prop->type == CONF_TYPE_INT) {
2515 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2516 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2517 prop->min, prop->max, r);
2518 } else if (prop->type == CONF_TYPE_FLOAT) {
2519 float f;
2520 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2521 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2522 prop->min, prop->max, f);
2523 } else {
2524 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2525 "Property use an unsupported type.\n");
2526 return -1;
2528 return 0;
2531 if (p->osd_msg) {
2532 char *val = mp_property_print(pname, mpctx);
2533 if (val) {
2534 int index = p - property_osd_display;
2535 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2536 1, opts->osd_duration, p->osd_msg, val);
2537 talloc_free(val);
2540 return 0;
2545 * Command to property bridge
2547 * It is used to handle most commands that just set a property
2548 * and optionally display something on the OSD.
2549 * Two kinds of commands are handled: adjust or toggle.
2551 * Adjust commands take 1 or 2 parameters: <value> <abs>
2552 * If <abs> is non-zero the property is set to the given value
2553 * otherwise it is adjusted.
2555 * Toggle commands take 0 or 1 parameters. With no parameter
2556 * or a value less than the property minimum it just steps the
2557 * property to its next or previous value respectively.
2558 * Otherwise it sets it to the given value.
2561 /// List of the commands that can be handled by setting a property.
2562 static struct {
2563 /// property name
2564 const char *name;
2565 /// cmd id
2566 int cmd;
2567 /// set/adjust or toggle command
2568 int toggle;
2569 } set_prop_cmd[] = {
2570 // general
2571 { "loop", MP_CMD_LOOP, 0},
2572 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2573 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2574 { "pause", MP_CMD_PAUSE, 0},
2575 { "capturing", MP_CMD_CAPTURING, 1},
2576 // audio
2577 { "volume", MP_CMD_VOLUME, 0},
2578 { "mute", MP_CMD_MUTE, 1},
2579 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2580 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2581 { "balance", MP_CMD_BALANCE, 0},
2582 // video
2583 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2584 { "panscan", MP_CMD_PANSCAN, 0},
2585 { "ontop", MP_CMD_VO_ONTOP, 1},
2586 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2587 { "border", MP_CMD_VO_BORDER, 1},
2588 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2589 { "gamma", MP_CMD_GAMMA, 0},
2590 { "brightness", MP_CMD_BRIGHTNESS, 0},
2591 { "contrast", MP_CMD_CONTRAST, 0},
2592 { "saturation", MP_CMD_SATURATION, 0},
2593 { "hue", MP_CMD_HUE, 0},
2594 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2595 // subs
2596 { "sub", MP_CMD_SUB_SELECT, 1},
2597 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2598 { "sub_vob", MP_CMD_SUB_VOB, 1},
2599 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2600 { "sub_file", MP_CMD_SUB_FILE, 1},
2601 { "sub_pos", MP_CMD_SUB_POS, 0},
2602 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2603 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2604 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2605 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2606 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2607 #ifdef CONFIG_ASS
2608 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2609 #endif
2610 #ifdef CONFIG_TV
2611 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2612 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2613 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2614 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2615 #endif
2619 /// Handle commands that set a property.
2620 static bool set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2622 int i, r;
2623 m_option_t *prop;
2624 const char *pname;
2626 // look for the command
2627 for (i = 0; set_prop_cmd[i].name; i++)
2628 if (set_prop_cmd[i].cmd == cmd->id)
2629 break;
2630 if (!(pname = set_prop_cmd[i].name))
2631 return 0;
2633 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2634 return 0;
2636 // toggle command
2637 if (set_prop_cmd[i].toggle) {
2638 // set to value
2639 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2640 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2641 else if (cmd->nargs > 0)
2642 r = mp_property_do(pname, M_PROPERTY_STEP_DOWN, NULL, mpctx);
2643 else
2644 r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2645 } else if (cmd->args[1].v.i) //set
2646 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2647 else // adjust
2648 r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2650 if (r <= 0)
2651 return 1;
2653 show_property_osd(mpctx, pname);
2655 return 1;
2658 #ifdef CONFIG_DVDNAV
2659 static const struct {
2660 const char *name;
2661 const enum mp_command_type cmd;
2662 } mp_dvdnav_bindings[] = {
2663 { "up", MP_CMD_DVDNAV_UP },
2664 { "down", MP_CMD_DVDNAV_DOWN },
2665 { "left", MP_CMD_DVDNAV_LEFT },
2666 { "right", MP_CMD_DVDNAV_RIGHT },
2667 { "menu", MP_CMD_DVDNAV_MENU },
2668 { "select", MP_CMD_DVDNAV_SELECT },
2669 { "prev", MP_CMD_DVDNAV_PREVMENU },
2670 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2673 * keep old dvdnav sub-command options for a while in order not to
2674 * break slave-mode API too suddenly.
2676 { "1", MP_CMD_DVDNAV_UP },
2677 { "2", MP_CMD_DVDNAV_DOWN },
2678 { "3", MP_CMD_DVDNAV_LEFT },
2679 { "4", MP_CMD_DVDNAV_RIGHT },
2680 { "5", MP_CMD_DVDNAV_MENU },
2681 { "6", MP_CMD_DVDNAV_SELECT },
2682 { "7", MP_CMD_DVDNAV_PREVMENU },
2683 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2684 { NULL, 0 }
2686 #endif
2688 static const char *property_error_string(int error_value)
2690 switch (error_value) {
2691 case M_PROPERTY_ERROR:
2692 return "ERROR";
2693 case M_PROPERTY_UNAVAILABLE:
2694 return "PROPERTY_UNAVAILABLE";
2695 case M_PROPERTY_NOT_IMPLEMENTED:
2696 return "NOT_IMPLEMENTED";
2697 case M_PROPERTY_UNKNOWN:
2698 return "PROPERTY_UNKNOWN";
2699 case M_PROPERTY_DISABLED:
2700 return "DISABLED";
2702 return "UNKNOWN";
2705 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2707 int idx;
2708 int end = start + count;
2709 int after = mpctx->set_of_sub_size - end;
2710 sub_data **subs = mpctx->set_of_subtitles;
2711 struct sh_sub **ass_tracks = mpctx->set_of_ass_tracks;
2712 if (count < 0 || count > mpctx->set_of_sub_size ||
2713 start < 0 || start > mpctx->set_of_sub_size - count) {
2714 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2715 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2716 return;
2718 for (idx = start; idx < end; idx++) {
2719 sub_data *subd = subs[idx];
2720 char *filename = "";
2721 if (subd)
2722 filename = subd->filename;
2723 if (!subd)
2724 filename = ass_tracks[idx]->title;
2725 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2726 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2727 filename_recode(filename));
2728 sub_free(subd);
2729 subs[idx] = NULL;
2730 if (ass_tracks[idx]) {
2731 sub_switchoff(ass_tracks[idx], mpctx->osd);
2732 sub_uninit(ass_tracks[idx]);
2734 talloc_free(ass_tracks[idx]);
2735 ass_tracks[idx] = NULL;
2738 mpctx->global_sub_size -= count;
2739 mpctx->set_of_sub_size -= count;
2740 if (mpctx->set_of_sub_size <= 0)
2741 mpctx->sub_counts[SUB_SOURCE_SUBS] = 0;
2743 memmove(subs + start, subs + end, after * sizeof(*subs));
2744 memset(subs + start + after, 0, count * sizeof(*subs));
2745 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2746 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2748 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2749 mpctx->global_sub_pos = -2;
2750 mpctx->subdata = NULL;
2751 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2752 } else if (mpctx->set_of_sub_pos >= end) {
2753 mpctx->set_of_sub_pos -= count;
2754 mpctx->global_sub_pos -= count;
2758 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2760 struct MPOpts *opts = &mpctx->opts;
2761 sh_audio_t *const sh_audio = mpctx->sh_audio;
2762 sh_video_t *const sh_video = mpctx->sh_video;
2763 int osd_duration = opts->osd_duration;
2764 int case_fallthrough_hack = 0;
2765 if (set_property_command(mpctx, cmd))
2766 goto old_pause_hack; // was handled already
2767 switch (cmd->id) {
2768 case MP_CMD_SEEK: {
2769 mpctx->add_osd_seek_info = true;
2770 float v = cmd->args[0].v.f;
2771 int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2772 int exact = (cmd->nargs > 2) ? cmd->args[2].v.i : 0;
2773 if (abs == 2) { // Absolute seek to a timestamp in seconds
2774 queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact);
2775 mpctx->osd_function = v > get_current_time(mpctx) ?
2776 OSD_FFW : OSD_REW;
2777 } else if (abs) { /* Absolute seek by percentage */
2778 queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact);
2779 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2780 } else {
2781 queue_seek(mpctx, MPSEEK_RELATIVE, v, exact);
2782 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2784 break;
2787 case MP_CMD_SET_PROPERTY_OSD:
2788 case_fallthrough_hack = 1;
2790 case MP_CMD_SET_PROPERTY: {
2791 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2792 cmd->args[1].v.s, mpctx);
2793 if (r == M_PROPERTY_UNKNOWN)
2794 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2795 "Unknown property: '%s'\n", cmd->args[0].v.s);
2796 else if (r <= 0)
2797 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2798 "Failed to set property '%s' to '%s'.\n",
2799 cmd->args[0].v.s, cmd->args[1].v.s);
2800 else if (case_fallthrough_hack)
2801 show_property_osd(mpctx, cmd->args[0].v.s);
2802 if (r <= 0)
2803 mp_msg(MSGT_GLOBAL, MSGL_INFO,
2804 "ANS_ERROR=%s\n", property_error_string(r));
2805 break;
2808 case MP_CMD_STEP_PROPERTY_OSD:
2809 case_fallthrough_hack = 1;
2811 case MP_CMD_STEP_PROPERTY: {
2812 void *arg = NULL;
2813 int r, i;
2814 double d;
2815 off_t o;
2816 if (cmd->args[1].v.f) {
2817 m_option_t *prop;
2818 if ((r = mp_property_do(cmd->args[0].v.s,
2819 M_PROPERTY_GET_TYPE,
2820 &prop, mpctx)) <= 0)
2821 goto step_prop_err;
2822 if (prop->type == CONF_TYPE_INT ||
2823 prop->type == CONF_TYPE_FLAG)
2824 i = cmd->args[1].v.f, arg = &i;
2825 else if (prop->type == CONF_TYPE_FLOAT)
2826 arg = &cmd->args[1].v.f;
2827 else if (prop->type == CONF_TYPE_DOUBLE ||
2828 prop->type == CONF_TYPE_TIME)
2829 d = cmd->args[1].v.f, arg = &d;
2830 else if (prop->type == CONF_TYPE_POSITION)
2831 o = cmd->args[1].v.f, arg = &o;
2832 else
2833 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2834 "Ignoring step size stepping property '%s'.\n",
2835 cmd->args[0].v.s);
2837 r = mp_property_do(cmd->args[0].v.s,
2838 cmd->args[2].v.i < 0 ?
2839 M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2840 arg, mpctx);
2841 step_prop_err:
2842 if (r == M_PROPERTY_UNKNOWN)
2843 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2844 "Unknown property: '%s'\n", cmd->args[0].v.s);
2845 else if (r <= 0)
2846 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2847 "Failed to increment property '%s' by %f.\n",
2848 cmd->args[0].v.s, cmd->args[1].v.f);
2849 else if (case_fallthrough_hack)
2850 show_property_osd(mpctx, cmd->args[0].v.s);
2851 if (r <= 0)
2852 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2853 property_error_string(r));
2854 break;
2857 case MP_CMD_GET_PROPERTY: {
2858 char *tmp;
2859 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2860 &tmp, mpctx);
2861 if (r <= 0) {
2862 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2863 "Failed to get value of property '%s'.\n",
2864 cmd->args[0].v.s);
2865 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n",
2866 property_error_string(r));
2867 break;
2869 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2870 cmd->args[0].v.s, tmp);
2871 talloc_free(tmp);
2872 break;
2875 case MP_CMD_EDL_MARK:
2876 if (edl_fd) {
2877 float v = get_current_time(mpctx);
2878 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2879 mpctx->begin_skip = v;
2880 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2881 "EDL skip start, press 'i' again to end block.\n");
2882 } else {
2883 if (mpctx->begin_skip > v)
2884 mp_tmsg(MSGT_CPLAYER, MSGL_WARN,
2885 "EDL skip canceled, last start > stop\n");
2886 else {
2887 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2888 mp_tmsg(MSGT_CPLAYER, MSGL_INFO,
2889 "EDL skip end, line written.\n");
2891 mpctx->begin_skip = MP_NOPTS_VALUE;
2894 break;
2896 case MP_CMD_SWITCH_RATIO:
2897 if (!sh_video)
2898 break;
2899 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2900 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2901 else
2902 opts->movie_aspect = cmd->args[0].v.f;
2903 video_reset_aspect(sh_video);
2904 break;
2906 case MP_CMD_SPEED_INCR: {
2907 float v = cmd->args[0].v.f;
2908 mp_property_do("speed", M_PROPERTY_STEP_UP, &v, mpctx);
2909 show_property_osd(mpctx, "speed");
2910 break;
2913 case MP_CMD_SPEED_MULT:
2914 case_fallthrough_hack = true;
2916 case MP_CMD_SPEED_SET: {
2917 float v = cmd->args[0].v.f;
2918 if (case_fallthrough_hack)
2919 v *= mpctx->opts.playback_speed;
2920 mp_property_do("speed", M_PROPERTY_SET, &v, mpctx);
2921 show_property_osd(mpctx, "speed");
2922 break;
2925 case MP_CMD_FRAME_STEP:
2926 add_step_frame(mpctx);
2927 break;
2929 case MP_CMD_QUIT:
2930 exit_player_with_rc(mpctx, EXIT_QUIT,
2931 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2933 case MP_CMD_PLAY_TREE_STEP: {
2934 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2935 int force = cmd->args[1].v.i;
2938 if (!force && mpctx->playtree_iter) {
2939 play_tree_iter_t *i =
2940 play_tree_iter_new_copy(mpctx->playtree_iter);
2941 if (play_tree_iter_step(i, n, 0) ==
2942 PLAY_TREE_ITER_ENTRY)
2943 mpctx->stop_play =
2944 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2945 play_tree_iter_free(i);
2946 } else
2947 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2948 if (mpctx->stop_play)
2949 mpctx->play_tree_step = n;
2951 break;
2954 case MP_CMD_PLAY_TREE_UP_STEP: {
2955 int n = cmd->args[0].v.i > 0 ? 1 : -1;
2956 int force = cmd->args[1].v.i;
2958 if (!force && mpctx->playtree_iter) {
2959 play_tree_iter_t *i =
2960 play_tree_iter_new_copy(mpctx->playtree_iter);
2961 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
2962 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2963 play_tree_iter_free(i);
2964 } else
2965 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2966 break;
2969 case MP_CMD_PLAY_ALT_SRC_STEP:
2970 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
2971 int v = cmd->args[0].v.i;
2972 if (v > 0
2973 && mpctx->playtree_iter->file <
2974 mpctx->playtree_iter->num_files)
2975 mpctx->stop_play = PT_NEXT_SRC;
2976 else if (v < 0 && mpctx->playtree_iter->file > 1)
2977 mpctx->stop_play = PT_PREV_SRC;
2979 break;
2981 case MP_CMD_SUB_STEP:
2982 if (sh_video) {
2983 int movement = cmd->args[0].v.i;
2984 step_sub(mpctx->subdata, mpctx->video_pts, movement);
2985 #if 0
2986 // currently not implemented with libass
2987 if (mpctx->osd->ass_track)
2988 sub_delay +=
2989 ass_step_sub(mpctx->osd->ass_track,
2990 (mpctx->video_pts +
2991 sub_delay) * 1000 + .5, movement) / 1000.;
2992 #endif
2993 set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration,
2994 "Sub delay: %d ms", ROUND(sub_delay * 1000));
2996 break;
2998 case MP_CMD_SUB_LOG:
2999 log_sub(mpctx);
3000 break;
3002 case MP_CMD_OSD: {
3003 int v = cmd->args[0].v.i;
3004 int max = (opts->term_osd
3005 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
3006 if (opts->osd_level > max)
3007 opts->osd_level = max;
3008 if (v < 0)
3009 opts->osd_level = (opts->osd_level + 1) % (max + 1);
3010 else
3011 opts->osd_level = v > max ? max : v;
3012 /* Show OSD state when disabled, but not when an explicit
3013 argument is given to the OSD command, i.e. in slave mode. */
3014 if (v == -1 && opts->osd_level <= 1)
3015 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
3016 "OSD: %s",
3017 opts->osd_level ? mp_gtext("enabled") :
3018 mp_gtext("disabled"));
3019 else
3020 rm_osd_msg(OSD_MSG_OSD_STATUS);
3021 break;
3024 case MP_CMD_OSD_SHOW_TEXT:
3025 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3026 (cmd->args[1].v.i <
3027 0 ? osd_duration : cmd->args[1].v.i),
3028 "%s", cmd->args[0].v.s);
3029 break;
3031 case MP_CMD_OSD_SHOW_PROPERTY_TEXT: {
3032 char *txt = m_properties_expand_string(mp_properties,
3033 cmd->args[0].v.s,
3034 mpctx);
3035 // if no argument supplied use default osd_duration, else <arg> ms.
3036 if (txt) {
3037 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
3038 (cmd->args[1].v.i <
3039 0 ? osd_duration : cmd->args[1].v.i),
3040 "%s", txt);
3041 free(txt);
3043 break;
3046 case MP_CMD_LOADFILE: {
3047 play_tree_t *e = play_tree_new();
3048 play_tree_add_file(e, cmd->args[0].v.s);
3050 if (cmd->args[1].v.i) // append
3051 play_tree_append_entry(mpctx->playtree->child, e);
3052 else {
3053 // Go back to the starting point.
3054 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3055 != PLAY_TREE_ITER_END)
3056 /* NOP */;
3057 play_tree_free_list(mpctx->playtree->child, 1);
3058 play_tree_set_child(mpctx->playtree, e);
3059 pt_iter_goto_head(mpctx->playtree_iter);
3060 mpctx->stop_play = PT_NEXT_SRC;
3062 break;
3065 case MP_CMD_LOADLIST: {
3066 play_tree_t *e = parse_playlist_file(mpctx->mconfig,
3067 bstr(cmd->args[0].v.s));
3068 if (!e)
3069 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
3070 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
3071 else {
3072 if (cmd->args[1].v.i) // append
3073 play_tree_append_entry(mpctx->playtree->child, e);
3074 else {
3075 // Go back to the starting point.
3076 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1)
3077 != PLAY_TREE_ITER_END)
3078 /* NOP */;
3079 play_tree_free_list(mpctx->playtree->child, 1);
3080 play_tree_set_child(mpctx->playtree, e);
3081 pt_iter_goto_head(mpctx->playtree_iter);
3082 mpctx->stop_play = PT_NEXT_SRC;
3085 break;
3088 case MP_CMD_STOP:
3089 // Go back to the starting point.
3090 while (play_tree_iter_up_step(mpctx->playtree_iter, 0, 1) !=
3091 PLAY_TREE_ITER_END)
3092 /* NOP */;
3093 mpctx->stop_play = PT_STOP;
3094 break;
3096 case MP_CMD_OSD_SHOW_PROGRESSION: {
3097 set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
3098 set_osd_progressmsg(OSD_MSG_TEXT, 1, osd_duration);
3099 break;
3102 #ifdef CONFIG_RADIO
3103 case MP_CMD_RADIO_STEP_CHANNEL:
3104 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3105 int v = cmd->args[0].v.i;
3106 if (v > 0)
3107 radio_step_channel(mpctx->demuxer->stream,
3108 RADIO_CHANNEL_HIGHER);
3109 else
3110 radio_step_channel(mpctx->demuxer->stream,
3111 RADIO_CHANNEL_LOWER);
3112 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3113 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3114 "Channel: %s",
3115 radio_get_channel_name(mpctx->demuxer->stream));
3118 break;
3120 case MP_CMD_RADIO_SET_CHANNEL:
3121 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
3122 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
3123 if (radio_get_channel_name(mpctx->demuxer->stream)) {
3124 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
3125 "Channel: %s",
3126 radio_get_channel_name(mpctx->demuxer->stream));
3129 break;
3131 case MP_CMD_RADIO_SET_FREQ:
3132 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3133 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3134 break;
3136 case MP_CMD_RADIO_STEP_FREQ:
3137 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
3138 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
3139 break;
3140 #endif
3142 #ifdef CONFIG_TV
3143 case MP_CMD_TV_START_SCAN:
3144 if (mpctx->file_format == DEMUXER_TYPE_TV)
3145 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv), 1);
3146 break;
3147 case MP_CMD_TV_SET_FREQ:
3148 if (mpctx->file_format == DEMUXER_TYPE_TV)
3149 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3150 cmd->args[0].v.f * 16.0);
3151 #ifdef CONFIG_PVR
3152 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3153 pvr_set_freq(mpctx->stream, ROUND(cmd->args[0].v.f));
3154 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3155 pvr_get_current_channelname(mpctx->stream),
3156 pvr_get_current_stationname(mpctx->stream));
3158 #endif /* CONFIG_PVR */
3159 break;
3161 case MP_CMD_TV_STEP_FREQ:
3162 if (mpctx->file_format == DEMUXER_TYPE_TV)
3163 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
3164 cmd->args[0].v.f * 16.0);
3165 #ifdef CONFIG_PVR
3166 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3167 pvr_force_freq_step(mpctx->stream, ROUND(cmd->args[0].v.f));
3168 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
3169 pvr_get_current_channelname(mpctx->stream),
3170 pvr_get_current_frequency(mpctx->stream));
3172 #endif /* CONFIG_PVR */
3173 break;
3175 case MP_CMD_TV_SET_NORM:
3176 if (mpctx->file_format == DEMUXER_TYPE_TV)
3177 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
3178 cmd->args[0].v.s);
3179 break;
3181 case MP_CMD_TV_STEP_CHANNEL:
3182 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3183 int v = cmd->args[0].v.i;
3184 if (v > 0) {
3185 tv_step_channel((tvi_handle_t *) (mpctx->
3186 demuxer->priv),
3187 TV_CHANNEL_HIGHER);
3188 } else {
3189 tv_step_channel((tvi_handle_t *) (mpctx->
3190 demuxer->priv),
3191 TV_CHANNEL_LOWER);
3193 if (tv_channel_list) {
3194 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3195 "Channel: %s", tv_channel_current->name);
3196 //vo_osd_changed(OSDTYPE_SUBTITLE);
3199 #ifdef CONFIG_PVR
3200 else if (mpctx->stream &&
3201 mpctx->stream->type == STREAMTYPE_PVR) {
3202 pvr_set_channel_step(mpctx->stream, cmd->args[0].v.i);
3203 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3204 pvr_get_current_channelname(mpctx->stream),
3205 pvr_get_current_stationname(mpctx->stream));
3207 #endif /* CONFIG_PVR */
3208 #ifdef CONFIG_DVBIN
3209 if (mpctx->stream->type == STREAMTYPE_DVB) {
3210 int dir;
3211 int v = cmd->args[0].v.i;
3213 mpctx->last_dvb_step = v;
3214 if (v > 0)
3215 dir = DVB_CHANNEL_HIGHER;
3216 else
3217 dir = DVB_CHANNEL_LOWER;
3220 if (dvb_step_channel(mpctx->stream, dir)) {
3221 mpctx->stop_play = PT_NEXT_ENTRY;
3222 mpctx->dvbin_reopen = 1;
3225 #endif /* CONFIG_DVBIN */
3226 break;
3228 case MP_CMD_TV_SET_CHANNEL:
3229 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3230 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3231 cmd->args[0].v.s);
3232 if (tv_channel_list) {
3233 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3234 "Channel: %s", tv_channel_current->name);
3235 //vo_osd_changed(OSDTYPE_SUBTITLE);
3238 #ifdef CONFIG_PVR
3239 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3240 pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
3241 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3242 pvr_get_current_channelname(mpctx->stream),
3243 pvr_get_current_stationname(mpctx->stream));
3245 #endif /* CONFIG_PVR */
3246 break;
3248 #ifdef CONFIG_DVBIN
3249 case MP_CMD_DVB_SET_CHANNEL:
3250 if (mpctx->stream->type == STREAMTYPE_DVB) {
3251 mpctx->last_dvb_step = 1;
3253 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3254 cmd->args[0].v.i)) {
3255 mpctx->stop_play = PT_NEXT_ENTRY;
3256 mpctx->dvbin_reopen = 1;
3259 break;
3260 #endif /* CONFIG_DVBIN */
3262 case MP_CMD_TV_LAST_CHANNEL:
3263 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3264 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3265 if (tv_channel_list) {
3266 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3267 "Channel: %s", tv_channel_current->name);
3268 //vo_osd_changed(OSDTYPE_SUBTITLE);
3271 #ifdef CONFIG_PVR
3272 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3273 pvr_set_lastchannel(mpctx->stream);
3274 set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3275 pvr_get_current_channelname(mpctx->stream),
3276 pvr_get_current_stationname(mpctx->stream));
3278 #endif /* CONFIG_PVR */
3279 break;
3281 case MP_CMD_TV_STEP_NORM:
3282 if (mpctx->file_format == DEMUXER_TYPE_TV)
3283 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3284 break;
3286 case MP_CMD_TV_STEP_CHANNEL_LIST:
3287 if (mpctx->file_format == DEMUXER_TYPE_TV)
3288 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3289 break;
3290 #endif /* CONFIG_TV */
3291 case MP_CMD_TV_TELETEXT_ADD_DEC:
3292 if (mpctx->demuxer->teletext)
3293 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_ADD_DEC,
3294 &(cmd->args[0].v.s));
3295 break;
3296 case MP_CMD_TV_TELETEXT_GO_LINK:
3297 if (mpctx->demuxer->teletext)
3298 teletext_control(mpctx->demuxer->teletext, TV_VBI_CONTROL_GO_LINK,
3299 &(cmd->args[0].v.i));
3300 break;
3302 case MP_CMD_SUB_LOAD:
3303 if (sh_video) {
3304 int n = mpctx->set_of_sub_size;
3305 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3306 if (n != mpctx->set_of_sub_size) {
3307 mpctx->sub_counts[SUB_SOURCE_SUBS]++;
3308 ++mpctx->global_sub_size;
3311 break;
3313 case MP_CMD_SUB_REMOVE:
3314 if (sh_video) {
3315 int v = cmd->args[0].v.i;
3316 if (v < 0)
3317 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3318 else if (v < mpctx->set_of_sub_size)
3319 remove_subtitle_range(mpctx, v, 1);
3321 break;
3323 case MP_CMD_GET_SUB_VISIBILITY:
3324 if (sh_video) {
3325 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3326 "ANS_SUB_VISIBILITY=%d\n", opts->sub_visibility);
3328 break;
3330 case MP_CMD_SCREENSHOT:
3331 screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i);
3332 break;
3334 case MP_CMD_VF_CHANGE_RECTANGLE:
3335 if (!sh_video)
3336 break;
3337 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3338 break;
3340 case MP_CMD_GET_TIME_LENGTH:
3341 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3342 get_time_length(mpctx));
3343 break;
3345 case MP_CMD_GET_FILENAME: {
3346 char *inf = get_metadata(mpctx, META_NAME);
3347 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n", inf);
3348 talloc_free(inf);
3349 break;
3352 case MP_CMD_GET_VIDEO_CODEC: {
3353 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3354 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3355 talloc_free(inf);
3356 break;
3359 case MP_CMD_GET_VIDEO_BITRATE: {
3360 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3361 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3362 talloc_free(inf);
3363 break;
3366 case MP_CMD_GET_VIDEO_RESOLUTION: {
3367 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3368 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3369 talloc_free(inf);
3370 break;
3373 case MP_CMD_GET_AUDIO_CODEC: {
3374 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3375 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3376 talloc_free(inf);
3377 break;
3380 case MP_CMD_GET_AUDIO_BITRATE: {
3381 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3382 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3383 talloc_free(inf);
3384 break;
3387 case MP_CMD_GET_AUDIO_SAMPLES: {
3388 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3389 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3390 talloc_free(inf);
3391 break;
3394 case MP_CMD_GET_META_TITLE: {
3395 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3396 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3397 talloc_free(inf);
3398 break;
3401 case MP_CMD_GET_META_ARTIST: {
3402 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3403 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3404 talloc_free(inf);
3405 break;
3408 case MP_CMD_GET_META_ALBUM: {
3409 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3410 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3411 talloc_free(inf);
3412 break;
3415 case MP_CMD_GET_META_YEAR: {
3416 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3417 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3418 talloc_free(inf);
3419 break;
3422 case MP_CMD_GET_META_COMMENT: {
3423 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3424 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3425 talloc_free(inf);
3426 break;
3429 case MP_CMD_GET_META_TRACK: {
3430 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3431 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3432 talloc_free(inf);
3433 break;
3436 case MP_CMD_GET_META_GENRE: {
3437 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3438 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3439 talloc_free(inf);
3440 break;
3443 case MP_CMD_GET_VO_FULLSCREEN:
3444 if (mpctx->video_out && mpctx->video_out->config_ok)
3445 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3446 break;
3448 case MP_CMD_GET_PERCENT_POS:
3449 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3450 get_percent_pos(mpctx));
3451 break;
3453 case MP_CMD_GET_TIME_POS: {
3454 float pos = get_current_time(mpctx);
3455 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3456 break;
3459 case MP_CMD_RUN:
3460 #ifndef __MINGW32__
3461 if (!fork()) {
3462 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3463 exit(0);
3465 #endif
3466 break;
3468 case MP_CMD_KEYDOWN_EVENTS:
3469 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3470 break;
3472 case MP_CMD_SET_MOUSE_POS: {
3473 int pointer_x, pointer_y;
3474 double dx, dy;
3475 pointer_x = cmd->args[0].v.i;
3476 pointer_y = cmd->args[1].v.i;
3477 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3478 #ifdef CONFIG_DVDNAV
3479 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3480 && dx > 0.0 && dy > 0.0) {
3481 int button = -1;
3482 pointer_x = (int) (dx * (double) sh_video->disp_w);
3483 pointer_y = (int) (dy * (double) sh_video->disp_h);
3484 mp_dvdnav_update_mouse_pos(mpctx->stream,
3485 pointer_x, pointer_y, &button);
3486 if (opts->osd_level > 1 && button > 0)
3487 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3488 "Selected button number %d", button);
3490 #endif
3491 break;
3494 #ifdef CONFIG_DVDNAV
3495 case MP_CMD_DVDNAV: {
3496 int button = -1;
3497 int i;
3498 enum mp_command_type command = 0;
3499 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3500 break;
3502 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3503 if (cmd->args[0].v.s &&
3504 !strcasecmp(cmd->args[0].v.s,
3505 mp_dvdnav_bindings[i].name))
3506 command = mp_dvdnav_bindings[i].cmd;
3508 mp_dvdnav_handle_input(mpctx->stream, command, &button);
3509 if (opts->osd_level > 1 && button > 0)
3510 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3511 "Selected button number %d", button);
3512 break;
3515 case MP_CMD_SWITCH_TITLE:
3516 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3517 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3518 break;
3520 #endif
3522 case MP_CMD_AF_SWITCH:
3523 if (sh_audio) {
3524 af_uninit(mpctx->mixer.afilter);
3525 af_init(mpctx->mixer.afilter);
3527 case MP_CMD_AF_ADD:
3528 case MP_CMD_AF_DEL: {
3529 if (!sh_audio)
3530 break;
3531 char *af_args = strdup(cmd->args[0].v.s);
3532 char *af_commands = af_args;
3533 char *af_command;
3534 af_instance_t *af;
3535 while ((af_command = strsep(&af_commands, ",")) != NULL) {
3536 if (cmd->id == MP_CMD_AF_DEL) {
3537 af = af_get(mpctx->mixer.afilter, af_command);
3538 if (af != NULL)
3539 af_remove(mpctx->mixer.afilter, af);
3540 } else
3541 af_add(mpctx->mixer.afilter, af_command);
3543 reinit_audio_chain(mpctx);
3544 free(af_args);
3545 break;
3547 case MP_CMD_AF_CLR:
3548 if (!sh_audio)
3549 break;
3550 af_uninit(mpctx->mixer.afilter);
3551 af_init(mpctx->mixer.afilter);
3552 reinit_audio_chain(mpctx);
3553 break;
3554 case MP_CMD_AF_CMDLINE:
3555 if (sh_audio) {
3556 af_instance_t *af = af_get(sh_audio->afilter, cmd->args[0].v.s);
3557 if (!af) {
3558 mp_msg(MSGT_CPLAYER, MSGL_WARN,
3559 "Filter '%s' not found in chain.\n", cmd->args[0].v.s);
3560 break;
3562 af->control(af, AF_CONTROL_COMMAND_LINE, cmd->args[1].v.s);
3563 af_reinit(sh_audio->afilter, af);
3565 break;
3567 default:
3568 mp_msg(MSGT_CPLAYER, MSGL_V,
3569 "Received unknown cmd %s\n", cmd->name);
3572 old_pause_hack:
3573 switch (cmd->pausing) {
3574 case 1: // "pausing"
3575 pause_player(mpctx);
3576 break;
3577 case 3: // "pausing_toggle"
3578 if (mpctx->paused)
3579 unpause_player(mpctx);
3580 else
3581 pause_player(mpctx);
3582 break;