Add -stop-screensaver flag for Windows
[mplayer/kovensky.git] / command.c
blobff5ef389a8d4f6cf471b8207832603b6f8ce212d
1 #include <stdlib.h>
2 #include <inttypes.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <stdbool.h>
7 #include "config.h"
8 #include "talloc.h"
9 #include "command.h"
10 #include "input/input.h"
11 #include "stream/stream.h"
12 #include "libmpdemux/demuxer.h"
13 #include "libmpdemux/stheader.h"
14 #include "codec-cfg.h"
15 #include "mplayer.h"
16 #include "libvo/sub.h"
17 #include "m_option.h"
18 #include "m_property.h"
19 #include "help_mp.h"
20 #include "metadata.h"
21 #include "libmpcodecs/vf.h"
22 #include "libmpcodecs/vd.h"
23 #include "mp_osd.h"
24 #include "libvo/video_out.h"
25 #include "libvo/font_load.h"
26 #include "playtree.h"
27 #include "libao2/audio_out.h"
28 #include "mpcommon.h"
29 #include "mixer.h"
30 #include "libmpcodecs/dec_video.h"
31 #include "libmpcodecs/dec_teletext.h"
32 #include "vobsub.h"
33 #include "spudec.h"
34 #include "get_path.h"
35 #include "ass_mp.h"
36 #include "stream/tv.h"
37 #include "stream/stream_radio.h"
38 #include "stream/pvr.h"
39 #ifdef CONFIG_DVBIN
40 #include "stream/dvbin.h"
41 #endif
42 #ifdef CONFIG_DVDREAD
43 #include "stream/stream_dvd.h"
44 #endif
45 #include "stream/stream_dvdnav.h"
46 #include "m_struct.h"
47 #include "libmenu/menu.h"
49 #include "mp_core.h"
50 #include "mp_fifo.h"
51 #include "libavutil/avstring.h"
53 #define ROUND(x) ((int)((x)<0 ? (x)-0.5 : (x)+0.5))
55 extern int use_menu;
57 static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
58 double *dx, double *dy)
60 struct MPOpts *opts = &mpctx->opts;
61 struct vo *vo = mpctx->video_out;
62 //remove the borders, if any, and rescale to the range [0,1],[0,1]
63 if (vo_fs) { //we are in full-screen mode
64 if (opts->vo_screenwidth > vo->dwidth) //there are borders along the x axis
65 ix -= (opts->vo_screenwidth - vo->dwidth) / 2;
66 if (opts->vo_screenheight > vo->dheight) //there are borders along the y axis (usual way)
67 iy -= (opts->vo_screenheight - vo->dheight) / 2;
69 if (ix < 0 || ix > vo->dwidth) {
70 *dx = *dy = -1.0;
71 return;
72 } //we are on one of the borders
73 if (iy < 0 || iy > vo->dheight) {
74 *dx = *dy = -1.0;
75 return;
76 } //we are on one of the borders
79 *dx = (double) ix / (double) vo->dwidth;
80 *dy = (double) iy / (double) vo->dheight;
82 mp_msg(MSGT_CPLAYER, MSGL_V,
83 "\r\nrescaled coordinates: %.3f, %.3f, screen (%d x %d), vodisplay: (%d, %d), fullscreen: %d\r\n",
84 *dx, *dy, opts->vo_screenwidth, opts->vo_screenheight, vo->dwidth,
85 vo->dheight, vo_fs);
88 static int sub_source_by_pos(MPContext *mpctx, int pos)
90 int source = -1;
91 int top = -1;
92 int i;
93 for (i = 0; i < SUB_SOURCES; i++) {
94 int j = mpctx->global_sub_indices[i];
95 if ((j >= 0) && (j > top) && (pos >= j)) {
96 source = i;
97 top = j;
100 return source;
103 static int sub_source(MPContext *mpctx)
105 return sub_source_by_pos(mpctx, mpctx->global_sub_pos);
109 * \brief Log the currently displayed subtitle to a file
111 * Logs the current or last displayed subtitle together with filename
112 * and time information to ~/.mplayer/subtitle_log
114 * Intended purpose is to allow convenient marking of bogus subtitles
115 * which need to be fixed while watching the movie.
118 static void log_sub(struct MPContext *mpctx)
120 char *fname;
121 FILE *f;
122 int i;
124 if (subdata == NULL || vo_sub_last == NULL)
125 return;
126 fname = get_path("subtitle_log");
127 f = fopen(fname, "a");
128 if (!f)
129 return;
130 fprintf(f, "----------------------------------------------------------\n");
131 if (subdata->sub_uses_time) {
132 fprintf(f,
133 "N: %s S: %02ld:%02ld:%02ld.%02ld E: %02ld:%02ld:%02ld.%02ld\n",
134 mpctx->filename, vo_sub_last->start / 360000,
135 (vo_sub_last->start / 6000) % 60,
136 (vo_sub_last->start / 100) % 60, vo_sub_last->start % 100,
137 vo_sub_last->end / 360000, (vo_sub_last->end / 6000) % 60,
138 (vo_sub_last->end / 100) % 60, vo_sub_last->end % 100);
139 } else {
140 fprintf(f, "N: %s S: %ld E: %ld\n", mpctx->filename,
141 vo_sub_last->start, vo_sub_last->end);
143 for (i = 0; i < vo_sub_last->lines; i++) {
144 fprintf(f, "%s\n", vo_sub_last->text[i]);
146 fclose(f);
150 /// \defgroup Properties
151 ///@{
153 /// \defgroup GeneralProperties General properties
154 /// \ingroup Properties
155 ///@{
157 /// OSD level (RW)
158 static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
159 MPContext *mpctx)
161 return m_property_choice(prop, action, arg, &mpctx->opts.osd_level);
164 /// Loop (RW)
165 static int mp_property_loop(m_option_t *prop, int action, void *arg,
166 MPContext *mpctx)
168 struct MPOpts *opts = &mpctx->opts;
169 switch (action) {
170 case M_PROPERTY_PRINT:
171 if (!arg) return M_PROPERTY_ERROR;
172 if (opts->loop_times < 0)
173 *(char**)arg = strdup("off");
174 else if (opts->loop_times == 0)
175 *(char**)arg = strdup("inf");
176 else
177 break;
178 return M_PROPERTY_OK;
180 return m_property_int_range(prop, action, arg, &opts->loop_times);
183 /// Playback speed (RW)
184 static int mp_property_playback_speed(m_option_t *prop, int action,
185 void *arg, MPContext *mpctx)
187 struct MPOpts *opts = &mpctx->opts;
188 switch (action) {
189 case M_PROPERTY_SET:
190 if (!arg)
191 return M_PROPERTY_ERROR;
192 M_PROPERTY_CLAMP(prop, *(float *) arg);
193 opts->playback_speed = *(float *) arg;
194 build_afilter_chain(mpctx, mpctx->sh_audio, &ao_data);
195 return M_PROPERTY_OK;
196 case M_PROPERTY_STEP_UP:
197 case M_PROPERTY_STEP_DOWN:
198 opts->playback_speed += (arg ? *(float *) arg : 0.1) *
199 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
200 M_PROPERTY_CLAMP(prop, opts->playback_speed);
201 build_afilter_chain(mpctx, mpctx->sh_audio, &ao_data);
202 return M_PROPERTY_OK;
204 return m_property_float_range(prop, action, arg, &opts->playback_speed);
207 /// filename with path (RO)
208 static int mp_property_path(m_option_t *prop, int action, void *arg,
209 MPContext *mpctx)
211 return m_property_string_ro(prop, action, arg, mpctx->filename);
214 /// filename without path (RO)
215 static int mp_property_filename(m_option_t *prop, int action, void *arg,
216 MPContext *mpctx)
218 char *f;
219 if (!mpctx->filename)
220 return M_PROPERTY_UNAVAILABLE;
221 if (((f = strrchr(mpctx->filename, '/'))
222 || (f = strrchr(mpctx->filename, '\\'))) && f[1])
223 f++;
224 else
225 f = mpctx->filename;
226 return m_property_string_ro(prop, action, arg, f);
229 /// Demuxer name (RO)
230 static int mp_property_demuxer(m_option_t *prop, int action, void *arg,
231 MPContext *mpctx)
233 if (!mpctx->demuxer)
234 return M_PROPERTY_UNAVAILABLE;
235 return m_property_string_ro(prop, action, arg,
236 (char *) mpctx->demuxer->desc->name);
239 /// Position in the stream (RW)
240 static int mp_property_stream_pos(m_option_t *prop, int action, void *arg,
241 MPContext *mpctx)
243 if (!mpctx->demuxer || !mpctx->demuxer->stream)
244 return M_PROPERTY_UNAVAILABLE;
245 if (!arg)
246 return M_PROPERTY_ERROR;
247 switch (action) {
248 case M_PROPERTY_GET:
249 *(off_t *) arg = stream_tell(mpctx->demuxer->stream);
250 return M_PROPERTY_OK;
251 case M_PROPERTY_SET:
252 M_PROPERTY_CLAMP(prop, *(off_t *) arg);
253 stream_seek(mpctx->demuxer->stream, *(off_t *) arg);
254 return M_PROPERTY_OK;
256 return M_PROPERTY_NOT_IMPLEMENTED;
259 /// Stream start offset (RO)
260 static int mp_property_stream_start(m_option_t *prop, int action,
261 void *arg, MPContext *mpctx)
263 if (!mpctx->demuxer || !mpctx->demuxer->stream)
264 return M_PROPERTY_UNAVAILABLE;
265 switch (action) {
266 case M_PROPERTY_GET:
267 *(off_t *) arg = mpctx->demuxer->stream->start_pos;
268 return M_PROPERTY_OK;
270 return M_PROPERTY_NOT_IMPLEMENTED;
273 /// Stream end offset (RO)
274 static int mp_property_stream_end(m_option_t *prop, int action, void *arg,
275 MPContext *mpctx)
277 if (!mpctx->demuxer || !mpctx->demuxer->stream)
278 return M_PROPERTY_UNAVAILABLE;
279 switch (action) {
280 case M_PROPERTY_GET:
281 *(off_t *) arg = mpctx->demuxer->stream->end_pos;
282 return M_PROPERTY_OK;
284 return M_PROPERTY_NOT_IMPLEMENTED;
287 /// Stream length (RO)
288 static int mp_property_stream_length(m_option_t *prop, int action,
289 void *arg, MPContext *mpctx)
291 if (!mpctx->demuxer || !mpctx->demuxer->stream)
292 return M_PROPERTY_UNAVAILABLE;
293 switch (action) {
294 case M_PROPERTY_GET:
295 *(off_t *) arg =
296 mpctx->demuxer->stream->end_pos - mpctx->demuxer->stream->start_pos;
297 return M_PROPERTY_OK;
299 return M_PROPERTY_NOT_IMPLEMENTED;
302 /// Media length in seconds (RO)
303 static int mp_property_length(m_option_t *prop, int action, void *arg,
304 MPContext *mpctx)
306 double len;
308 if (!mpctx->demuxer ||
309 !(int) (len = demuxer_get_time_length(mpctx->demuxer)))
310 return M_PROPERTY_UNAVAILABLE;
312 return m_property_time_ro(prop, action, arg, len);
315 /// Current position in percent (RW)
316 static int mp_property_percent_pos(m_option_t *prop, int action,
317 void *arg, MPContext *mpctx) {
318 int pos;
320 if (!mpctx->demuxer)
321 return M_PROPERTY_UNAVAILABLE;
323 switch(action) {
324 case M_PROPERTY_SET:
325 if(!arg) return M_PROPERTY_ERROR;
326 M_PROPERTY_CLAMP(prop, *(int*)arg);
327 pos = *(int*)arg;
328 break;
329 case M_PROPERTY_STEP_UP:
330 case M_PROPERTY_STEP_DOWN:
331 pos = demuxer_get_percent_pos(mpctx->demuxer);
332 pos += (arg ? *(int*)arg : 10) *
333 (action == M_PROPERTY_STEP_UP ? 1 : -1);
334 M_PROPERTY_CLAMP(prop, pos);
335 break;
336 default:
337 return m_property_int_ro(prop, action, arg,
338 demuxer_get_percent_pos(mpctx->demuxer));
341 mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR;
342 mpctx->rel_seek_secs = pos / 100.0;
343 return M_PROPERTY_OK;
346 /// Current position in seconds (RW)
347 static int mp_property_time_pos(m_option_t *prop, int action,
348 void *arg, MPContext *mpctx) {
349 if (!(mpctx->sh_video || (mpctx->sh_audio && mpctx->audio_out)))
350 return M_PROPERTY_UNAVAILABLE;
352 switch(action) {
353 case M_PROPERTY_SET:
354 if(!arg) return M_PROPERTY_ERROR;
355 M_PROPERTY_CLAMP(prop, *(double*)arg);
356 mpctx->abs_seek_pos = SEEK_ABSOLUTE;
357 mpctx->rel_seek_secs = *(double*)arg;
358 return M_PROPERTY_OK;
359 case M_PROPERTY_STEP_UP:
360 case M_PROPERTY_STEP_DOWN:
361 mpctx->rel_seek_secs += (arg ? *(double*)arg : 10.0) *
362 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
363 return M_PROPERTY_OK;
365 return m_property_time_ro(prop, action, arg,
366 mpctx->sh_video ? mpctx->sh_video->pts :
367 playing_audio_pts(mpctx));
370 /// Current chapter (RW)
371 static int mp_property_chapter(m_option_t *prop, int action, void *arg,
372 MPContext *mpctx)
374 struct MPOpts *opts = &mpctx->opts;
375 int chapter = -1;
376 int step_all;
377 char *chapter_name = NULL;
379 if (mpctx->demuxer)
380 chapter = get_current_chapter(mpctx);
381 if (chapter < 0)
382 return M_PROPERTY_UNAVAILABLE;
384 switch (action) {
385 case M_PROPERTY_GET:
386 if (!arg)
387 return M_PROPERTY_ERROR;
388 *(int *) arg = chapter;
389 return M_PROPERTY_OK;
390 case M_PROPERTY_PRINT: {
391 if (!arg)
392 return M_PROPERTY_ERROR;
393 chapter_name = chapter_display_name(mpctx, chapter);
394 if (!chapter_name)
395 return M_PROPERTY_UNAVAILABLE;
396 *(char **) arg = chapter_name;
397 return M_PROPERTY_OK;
399 case M_PROPERTY_SET:
400 if (!arg)
401 return M_PROPERTY_ERROR;
402 M_PROPERTY_CLAMP(prop, *(int*)arg);
403 step_all = *(int *)arg - chapter;
404 chapter += step_all;
405 break;
406 case M_PROPERTY_STEP_UP:
407 case M_PROPERTY_STEP_DOWN: {
408 step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
409 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
410 chapter += step_all;
411 if (chapter < 0)
412 chapter = 0;
413 break;
415 default:
416 return M_PROPERTY_NOT_IMPLEMENTED;
419 double next_pts = 0;
420 chapter = seek_chapter(mpctx, chapter, &next_pts, &chapter_name);
421 mpctx->rel_seek_secs = 0;
422 mpctx->abs_seek_pos = 0;
423 if (chapter >= 0) {
424 if (next_pts > -1.0) {
425 mpctx->abs_seek_pos = SEEK_ABSOLUTE;
426 mpctx->rel_seek_secs = next_pts;
428 if (chapter_name)
429 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
430 "Chapter: (%d) %s", chapter + 1, chapter_name);
432 else if (step_all > 0)
433 mpctx->rel_seek_secs = 1000000000.;
434 else
435 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
436 "Chapter: (%d) %s", 0, mp_gtext("unknown"));
437 if (chapter_name)
438 talloc_free(chapter_name);
439 return M_PROPERTY_OK;
442 /// Number of chapters in file
443 static int mp_property_chapters(m_option_t *prop, int action, void *arg,
444 MPContext *mpctx)
446 if (!mpctx->demuxer)
447 return M_PROPERTY_UNAVAILABLE;
448 if (mpctx->demuxer->num_chapters == 0)
449 stream_control(mpctx->demuxer->stream, STREAM_CTRL_GET_NUM_CHAPTERS, &mpctx->demuxer->num_chapters);
450 return m_property_int_ro(prop, action, arg, mpctx->demuxer->num_chapters);
453 /// Current dvd angle (RW)
454 static int mp_property_angle(m_option_t *prop, int action, void *arg,
455 MPContext *mpctx)
457 struct MPOpts *opts = &mpctx->opts;
458 int angle = -1;
459 int angles;
460 char *angle_name = NULL;
462 if (mpctx->demuxer)
463 angle = demuxer_get_current_angle(mpctx->demuxer);
464 if (angle < 0)
465 return M_PROPERTY_UNAVAILABLE;
466 angles = demuxer_angles_count(mpctx->demuxer);
467 if (angles <= 1)
468 return M_PROPERTY_UNAVAILABLE;
470 switch (action) {
471 case M_PROPERTY_GET:
472 if (!arg)
473 return M_PROPERTY_ERROR;
474 *(int *) arg = angle;
475 return M_PROPERTY_OK;
476 case M_PROPERTY_PRINT: {
477 if (!arg)
478 return M_PROPERTY_ERROR;
479 angle_name = calloc(1, 64);
480 if (!angle_name)
481 return M_PROPERTY_UNAVAILABLE;
482 snprintf(angle_name, 64, "%d/%d", angle, angles);
483 *(char **) arg = angle_name;
484 return M_PROPERTY_OK;
486 case M_PROPERTY_SET:
487 if (!arg)
488 return M_PROPERTY_ERROR;
489 angle = *(int *)arg;
490 M_PROPERTY_CLAMP(prop, angle);
491 break;
492 case M_PROPERTY_STEP_UP:
493 case M_PROPERTY_STEP_DOWN: {
494 int step = 0;
495 if(arg)
496 step = *(int*)arg;
497 if(!step)
498 step = 1;
499 step *= (action == M_PROPERTY_STEP_UP ? 1 : -1);
500 angle += step;
501 if (angle < 1) //cycle
502 angle = angles;
503 break;
505 default:
506 return M_PROPERTY_NOT_IMPLEMENTED;
508 angle = demuxer_set_angle(mpctx->demuxer, angle);
509 set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration,
510 "Angle: %d/%d", angle, angles);
511 if (angle_name)
512 free(angle_name);
513 return M_PROPERTY_OK;
516 /// Demuxer meta data
517 static int mp_property_metadata(m_option_t *prop, int action, void *arg,
518 MPContext *mpctx) {
519 m_property_action_t* ka;
520 char* meta;
521 static const m_option_t key_type =
522 { "metadata", NULL, CONF_TYPE_STRING, 0, 0, 0, NULL };
523 if (!mpctx->demuxer)
524 return M_PROPERTY_UNAVAILABLE;
526 switch(action) {
527 case M_PROPERTY_GET:
528 if(!arg) return M_PROPERTY_ERROR;
529 *(char***)arg = mpctx->demuxer->info;
530 return M_PROPERTY_OK;
531 case M_PROPERTY_KEY_ACTION:
532 if(!arg) return M_PROPERTY_ERROR;
533 ka = arg;
534 if(!(meta = demux_info_get(mpctx->demuxer,ka->key)))
535 return M_PROPERTY_UNKNOWN;
536 switch(ka->action) {
537 case M_PROPERTY_GET:
538 if(!ka->arg) return M_PROPERTY_ERROR;
539 *(char**)ka->arg = meta;
540 return M_PROPERTY_OK;
541 case M_PROPERTY_GET_TYPE:
542 if(!ka->arg) return M_PROPERTY_ERROR;
543 *(m_option_t**)ka->arg = &key_type;
544 return M_PROPERTY_OK;
547 return M_PROPERTY_NOT_IMPLEMENTED;
550 static int mp_property_pause(m_option_t *prop, int action, void *arg,
551 void *ctx)
553 MPContext *mpctx = ctx;
555 switch (action) {
556 case M_PROPERTY_SET:
557 if (!arg)
558 return M_PROPERTY_ERROR;
559 if (mpctx->paused == (bool)*(int *) arg)
560 return M_PROPERTY_OK;
561 case M_PROPERTY_STEP_UP:
562 case M_PROPERTY_STEP_DOWN:
563 if (mpctx->paused) {
564 unpause_player(mpctx);
565 mpctx->osd_function = OSD_PLAY;
567 else {
568 pause_player(mpctx);
569 mpctx->osd_function = OSD_PAUSE;
571 return M_PROPERTY_OK;
572 default:
573 return m_property_flag(prop, action, arg, &mpctx->paused);
578 ///@}
580 /// \defgroup AudioProperties Audio properties
581 /// \ingroup Properties
582 ///@{
584 /// Volume (RW)
585 static int mp_property_volume(m_option_t *prop, int action, void *arg,
586 MPContext *mpctx)
589 if (!mpctx->sh_audio)
590 return M_PROPERTY_UNAVAILABLE;
592 switch (action) {
593 case M_PROPERTY_GET:
594 if (!arg)
595 return M_PROPERTY_ERROR;
596 mixer_getbothvolume(&mpctx->mixer, arg);
597 return M_PROPERTY_OK;
598 case M_PROPERTY_PRINT:{
599 float vol;
600 if (!arg)
601 return M_PROPERTY_ERROR;
602 mixer_getbothvolume(&mpctx->mixer, &vol);
603 return m_property_float_range(prop, action, arg, &vol);
605 case M_PROPERTY_STEP_UP:
606 case M_PROPERTY_STEP_DOWN:
607 case M_PROPERTY_SET:
608 break;
609 default:
610 return M_PROPERTY_NOT_IMPLEMENTED;
613 if (mpctx->edl_muted)
614 return M_PROPERTY_DISABLED;
615 mpctx->user_muted = 0;
617 switch (action) {
618 case M_PROPERTY_SET:
619 if (!arg)
620 return M_PROPERTY_ERROR;
621 M_PROPERTY_CLAMP(prop, *(float *) arg);
622 mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg);
623 return M_PROPERTY_OK;
624 case M_PROPERTY_STEP_UP:
625 if (arg && *(float *) arg <= 0)
626 mixer_decvolume(&mpctx->mixer);
627 else
628 mixer_incvolume(&mpctx->mixer);
629 return M_PROPERTY_OK;
630 case M_PROPERTY_STEP_DOWN:
631 if (arg && *(float *) arg <= 0)
632 mixer_incvolume(&mpctx->mixer);
633 else
634 mixer_decvolume(&mpctx->mixer);
635 return M_PROPERTY_OK;
637 return M_PROPERTY_NOT_IMPLEMENTED;
640 /// Mute (RW)
641 static int mp_property_mute(m_option_t *prop, int action, void *arg,
642 MPContext *mpctx)
645 if (!mpctx->sh_audio)
646 return M_PROPERTY_UNAVAILABLE;
648 switch (action) {
649 case M_PROPERTY_SET:
650 if (mpctx->edl_muted)
651 return M_PROPERTY_DISABLED;
652 if (!arg)
653 return M_PROPERTY_ERROR;
654 if ((!!*(int *) arg) != mpctx->mixer.muted)
655 mixer_mute(&mpctx->mixer);
656 mpctx->user_muted = mpctx->mixer.muted;
657 return M_PROPERTY_OK;
658 case M_PROPERTY_STEP_UP:
659 case M_PROPERTY_STEP_DOWN:
660 if (mpctx->edl_muted)
661 return M_PROPERTY_DISABLED;
662 mixer_mute(&mpctx->mixer);
663 mpctx->user_muted = mpctx->mixer.muted;
664 return M_PROPERTY_OK;
665 case M_PROPERTY_PRINT:
666 if (!arg)
667 return M_PROPERTY_ERROR;
668 if (mpctx->edl_muted) {
669 *(char **) arg = strdup(mp_gtext("enabled (EDL)"));
670 return M_PROPERTY_OK;
672 default:
673 return m_property_flag(prop, action, arg, &mpctx->mixer.muted);
678 /// Audio delay (RW)
679 static int mp_property_audio_delay(m_option_t *prop, int action,
680 void *arg, MPContext *mpctx)
682 if (!(mpctx->sh_audio && mpctx->sh_video))
683 return M_PROPERTY_UNAVAILABLE;
684 switch (action) {
685 case M_PROPERTY_SET:
686 case M_PROPERTY_STEP_UP:
687 case M_PROPERTY_STEP_DOWN: {
688 int ret;
689 float delay = audio_delay;
690 ret = m_property_delay(prop, action, arg, &audio_delay);
691 if (ret != M_PROPERTY_OK)
692 return ret;
693 if (mpctx->sh_audio)
694 mpctx->delay -= audio_delay - delay;
696 return M_PROPERTY_OK;
697 default:
698 return m_property_delay(prop, action, arg, &audio_delay);
702 /// Audio codec tag (RO)
703 static int mp_property_audio_format(m_option_t *prop, int action,
704 void *arg, MPContext *mpctx)
706 if (!mpctx->sh_audio)
707 return M_PROPERTY_UNAVAILABLE;
708 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->format);
711 /// Audio codec name (RO)
712 static int mp_property_audio_codec(m_option_t *prop, int action,
713 void *arg, MPContext *mpctx)
715 if (!mpctx->sh_audio || !mpctx->sh_audio->codec)
716 return M_PROPERTY_UNAVAILABLE;
717 return m_property_string_ro(prop, action, arg, mpctx->sh_audio->codec->name);
720 /// Audio bitrate (RO)
721 static int mp_property_audio_bitrate(m_option_t *prop, int action,
722 void *arg, MPContext *mpctx)
724 if (!mpctx->sh_audio)
725 return M_PROPERTY_UNAVAILABLE;
726 return m_property_bitrate(prop, action, arg, mpctx->sh_audio->i_bps);
729 /// Samplerate (RO)
730 static int mp_property_samplerate(m_option_t *prop, int action, void *arg,
731 MPContext *mpctx)
733 if (!mpctx->sh_audio)
734 return M_PROPERTY_UNAVAILABLE;
735 switch(action) {
736 case M_PROPERTY_PRINT:
737 if(!arg) return M_PROPERTY_ERROR;
738 *(char**)arg = malloc(16);
739 sprintf(*(char**)arg,"%d kHz",mpctx->sh_audio->samplerate/1000);
740 return M_PROPERTY_OK;
742 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->samplerate);
745 /// Number of channels (RO)
746 static int mp_property_channels(m_option_t *prop, int action, void *arg,
747 MPContext *mpctx)
749 if (!mpctx->sh_audio)
750 return M_PROPERTY_UNAVAILABLE;
751 switch (action) {
752 case M_PROPERTY_PRINT:
753 if (!arg)
754 return M_PROPERTY_ERROR;
755 switch (mpctx->sh_audio->channels) {
756 case 1:
757 *(char **) arg = strdup("mono");
758 break;
759 case 2:
760 *(char **) arg = strdup("stereo");
761 break;
762 default:
763 *(char **) arg = malloc(32);
764 sprintf(*(char **) arg, "%d channels", mpctx->sh_audio->channels);
766 return M_PROPERTY_OK;
768 return m_property_int_ro(prop, action, arg, mpctx->sh_audio->channels);
771 /// Balance (RW)
772 static int mp_property_balance(m_option_t *prop, int action, void *arg,
773 MPContext *mpctx)
775 float bal;
777 if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2)
778 return M_PROPERTY_UNAVAILABLE;
780 switch (action) {
781 case M_PROPERTY_GET:
782 if (!arg)
783 return M_PROPERTY_ERROR;
784 mixer_getbalance(&mpctx->mixer, arg);
785 return M_PROPERTY_OK;
786 case M_PROPERTY_PRINT: {
787 char** str = arg;
788 if (!arg)
789 return M_PROPERTY_ERROR;
790 mixer_getbalance(&mpctx->mixer, &bal);
791 if (bal == 0.f)
792 *str = strdup("center");
793 else if (bal == -1.f)
794 *str = strdup("left only");
795 else if (bal == 1.f)
796 *str = strdup("right only");
797 else {
798 unsigned right = (bal + 1.f) / 2.f * 100.f;
799 *str = malloc(sizeof("left xxx%, right xxx%"));
800 sprintf(*str, "left %d%%, right %d%%", 100 - right, right);
802 return M_PROPERTY_OK;
804 case M_PROPERTY_STEP_UP:
805 case M_PROPERTY_STEP_DOWN:
806 mixer_getbalance(&mpctx->mixer, &bal);
807 bal += (arg ? *(float*)arg : .1f) *
808 (action == M_PROPERTY_STEP_UP ? 1.f : -1.f);
809 M_PROPERTY_CLAMP(prop, bal);
810 mixer_setbalance(&mpctx->mixer, bal);
811 return M_PROPERTY_OK;
812 case M_PROPERTY_SET:
813 if (!arg)
814 return M_PROPERTY_ERROR;
815 M_PROPERTY_CLAMP(prop, *(float*)arg);
816 mixer_setbalance(&mpctx->mixer, *(float*)arg);
817 return M_PROPERTY_OK;
819 return M_PROPERTY_NOT_IMPLEMENTED;
822 /// Selected audio id (RW)
823 static int mp_property_audio(m_option_t *prop, int action, void *arg,
824 MPContext *mpctx)
826 struct MPOpts *opts = &mpctx->opts;
827 int current_id, tmp;
828 if (!mpctx->demuxer || !mpctx->demuxer->audio)
829 return M_PROPERTY_UNAVAILABLE;
830 current_id = mpctx->demuxer->audio->id;
832 switch (action) {
833 case M_PROPERTY_GET:
834 if (!arg)
835 return M_PROPERTY_ERROR;
836 *(int *) arg = current_id;
837 return M_PROPERTY_OK;
838 case M_PROPERTY_PRINT:
839 if (!arg)
840 return M_PROPERTY_ERROR;
842 if (current_id < 0)
843 *(char **) arg = strdup(mp_gtext("disabled"));
844 else {
845 char lang[40];
846 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
847 sh_audio_t* sh = mpctx->sh_audio;
848 if (sh && sh->lang)
849 av_strlcpy(lang, sh->lang, 40);
850 #ifdef CONFIG_DVDREAD
851 else if (mpctx->stream->type == STREAMTYPE_DVD) {
852 int code = dvd_lang_from_aid(mpctx->stream, current_id);
853 if (code) {
854 lang[0] = code >> 8;
855 lang[1] = code;
856 lang[2] = 0;
859 #endif
861 #ifdef CONFIG_DVDNAV
862 else if (mpctx->stream->type == STREAMTYPE_DVDNAV)
863 mp_dvdnav_lang_from_aid(mpctx->stream, current_id, lang);
864 #endif
865 *(char **) arg = malloc(64);
866 snprintf(*(char **) arg, 64, "(%d) %s", current_id, lang);
868 return M_PROPERTY_OK;
870 case M_PROPERTY_STEP_UP:
871 case M_PROPERTY_SET:
872 if (action == M_PROPERTY_SET && arg)
873 tmp = *((int *) arg);
874 else
875 tmp = -1;
876 opts->audio_id = demuxer_switch_audio(mpctx->demuxer, tmp);
877 if (opts->audio_id == -2
878 || (opts->audio_id > -1
879 && mpctx->demuxer->audio->id != current_id && current_id != -2))
880 uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
881 if (opts->audio_id > -1 && mpctx->demuxer->audio->id != current_id) {
882 sh_audio_t *sh2;
883 sh2 = mpctx->demuxer->a_streams[mpctx->demuxer->audio->id];
884 if (sh2) {
885 sh2->ds = mpctx->demuxer->audio;
886 mpctx->sh_audio = sh2;
887 reinit_audio_chain(mpctx);
890 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_TRACK=%d\n", opts->audio_id);
891 return M_PROPERTY_OK;
892 default:
893 return M_PROPERTY_NOT_IMPLEMENTED;
898 /// Selected video id (RW)
899 static int mp_property_video(m_option_t *prop, int action, void *arg,
900 MPContext *mpctx)
902 struct MPOpts *opts = &mpctx->opts;
903 int current_id, tmp;
904 if (!mpctx->demuxer || !mpctx->demuxer->video)
905 return M_PROPERTY_UNAVAILABLE;
906 current_id = mpctx->demuxer->video->id;
908 switch (action) {
909 case M_PROPERTY_GET:
910 if (!arg)
911 return M_PROPERTY_ERROR;
912 *(int *) arg = current_id;
913 return M_PROPERTY_OK;
914 case M_PROPERTY_PRINT:
915 if (!arg)
916 return M_PROPERTY_ERROR;
918 if (current_id < 0)
919 *(char **) arg = strdup(mp_gtext("disabled"));
920 else {
921 char lang[40];
922 strncpy(lang, mp_gtext("unknown"), sizeof(lang));
923 *(char **) arg = malloc(64);
924 snprintf(*(char **) arg, 64, "(%d) %s", current_id, lang);
926 return M_PROPERTY_OK;
928 case M_PROPERTY_STEP_UP:
929 case M_PROPERTY_SET:
930 if (action == M_PROPERTY_SET && arg)
931 tmp = *((int *) arg);
932 else
933 tmp = -1;
934 opts->video_id = demuxer_switch_video(mpctx->demuxer, tmp);
935 if (opts->video_id == -2
936 || (opts->video_id > -1 && mpctx->demuxer->video->id != current_id
937 && current_id != -2))
938 uninit_player(mpctx, INITIALIZED_VCODEC |
939 (mpctx->opts.fixed_vo && opts->video_id != -2 ? 0 : INITIALIZED_VO));
940 if (opts->video_id > -1 && mpctx->demuxer->video->id != current_id) {
941 sh_video_t *sh2;
942 sh2 = mpctx->demuxer->v_streams[mpctx->demuxer->video->id];
943 if (sh2) {
944 sh2->ds = mpctx->demuxer->video;
945 mpctx->sh_video = sh2;
946 reinit_video_chain(mpctx);
949 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_TRACK=%d\n", opts->video_id);
950 return M_PROPERTY_OK;
952 default:
953 return M_PROPERTY_NOT_IMPLEMENTED;
957 static int mp_property_program(m_option_t *prop, int action, void *arg,
958 MPContext *mpctx)
960 demux_program_t prog;
962 switch (action) {
963 case M_PROPERTY_STEP_UP:
964 case M_PROPERTY_SET:
965 if (action == M_PROPERTY_SET && arg)
966 prog.progid = *((int *) arg);
967 else
968 prog.progid = -1;
969 if (demux_control
970 (mpctx->demuxer, DEMUXER_CTRL_IDENTIFY_PROGRAM,
971 &prog) == DEMUXER_CTRL_NOTIMPL)
972 return M_PROPERTY_ERROR;
974 if (prog.aid < 0 && prog.vid < 0) {
975 mp_msg(MSGT_CPLAYER, MSGL_ERR, "Selected program contains no audio or video streams!\n");
976 return M_PROPERTY_ERROR;
978 mp_property_do("switch_audio", M_PROPERTY_SET, &prog.aid, mpctx);
979 mp_property_do("switch_video", M_PROPERTY_SET, &prog.vid, mpctx);
980 return M_PROPERTY_OK;
982 default:
983 return M_PROPERTY_NOT_IMPLEMENTED;
987 ///@}
989 /// \defgroup VideoProperties Video properties
990 /// \ingroup Properties
991 ///@{
993 /// Fullscreen state (RW)
994 static int mp_property_fullscreen(m_option_t *prop, int action, void *arg,
995 MPContext *mpctx)
998 if (!mpctx->video_out)
999 return M_PROPERTY_UNAVAILABLE;
1001 switch (action) {
1002 case M_PROPERTY_SET:
1003 if (!arg)
1004 return M_PROPERTY_ERROR;
1005 M_PROPERTY_CLAMP(prop, *(int *) arg);
1006 if (vo_fs == !!*(int *) arg)
1007 return M_PROPERTY_OK;
1008 case M_PROPERTY_STEP_UP:
1009 case M_PROPERTY_STEP_DOWN:
1010 if (mpctx->video_out->config_ok)
1011 vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0);
1012 mpctx->opts.fullscreen = vo_fs;
1013 return M_PROPERTY_OK;
1014 default:
1015 return m_property_flag(prop, action, arg, &vo_fs);
1019 static int mp_property_deinterlace(m_option_t *prop, int action,
1020 void *arg, MPContext *mpctx)
1022 int deinterlace;
1023 vf_instance_t *vf;
1024 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1025 return M_PROPERTY_UNAVAILABLE;
1026 vf = mpctx->sh_video->vfilter;
1027 switch (action) {
1028 case M_PROPERTY_GET:
1029 if (!arg)
1030 return M_PROPERTY_ERROR;
1031 vf->control(vf, VFCTRL_GET_DEINTERLACE, arg);
1032 return M_PROPERTY_OK;
1033 case M_PROPERTY_SET:
1034 if (!arg)
1035 return M_PROPERTY_ERROR;
1036 M_PROPERTY_CLAMP(prop, *(int *) arg);
1037 vf->control(vf, VFCTRL_SET_DEINTERLACE, arg);
1038 return M_PROPERTY_OK;
1039 case M_PROPERTY_STEP_UP:
1040 case M_PROPERTY_STEP_DOWN:
1041 vf->control(vf, VFCTRL_GET_DEINTERLACE, &deinterlace);
1042 deinterlace = !deinterlace;
1043 vf->control(vf, VFCTRL_SET_DEINTERLACE, &deinterlace);
1044 return M_PROPERTY_OK;
1046 int value = 0;
1047 vf->control(vf, VFCTRL_GET_DEINTERLACE, &value);
1048 return m_property_flag_ro(prop, action, arg, value);
1051 static int mp_property_yuv_colorspace(m_option_t *prop, int action,
1052 void *arg, MPContext *mpctx)
1054 if (!mpctx->sh_video || !mpctx->sh_video->vfilter)
1055 return M_PROPERTY_UNAVAILABLE;
1057 struct vf_instance *vf = mpctx->sh_video->vfilter;
1058 int colorspace;
1059 switch (action) {
1060 case M_PROPERTY_GET:
1061 if (!arg)
1062 return M_PROPERTY_ERROR;
1063 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, arg) != true)
1064 return M_PROPERTY_UNAVAILABLE;
1065 return M_PROPERTY_OK;
1066 case M_PROPERTY_PRINT:
1067 if (!arg)
1068 return M_PROPERTY_ERROR;
1069 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &colorspace) != true)
1070 return M_PROPERTY_UNAVAILABLE;
1071 char * const names[] = {"BT.601 (SD)", "BT.709 (HD)", "SMPTE-240M"};
1072 if (colorspace < 0 || colorspace >= sizeof(names) / sizeof(names[0]))
1073 *(char **)arg = strdup("Unknown");
1074 else
1075 *(char**)arg = strdup(names[colorspace]);
1076 return M_PROPERTY_OK;
1077 case M_PROPERTY_SET:
1078 if (!arg)
1079 return M_PROPERTY_ERROR;
1080 M_PROPERTY_CLAMP(prop, *(int *) arg);
1081 vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, arg);
1082 return M_PROPERTY_OK;
1083 case M_PROPERTY_STEP_UP:;
1084 if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &colorspace) != true)
1085 return M_PROPERTY_UNAVAILABLE;
1086 colorspace += 1;
1087 vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &colorspace);
1088 return M_PROPERTY_OK;
1090 return M_PROPERTY_NOT_IMPLEMENTED;
1093 /// Panscan (RW)
1094 static int mp_property_panscan(m_option_t *prop, int action, void *arg,
1095 MPContext *mpctx)
1098 if (!mpctx->video_out
1099 || vo_control(mpctx->video_out, VOCTRL_GET_PANSCAN, NULL) != VO_TRUE)
1100 return M_PROPERTY_UNAVAILABLE;
1102 switch (action) {
1103 case M_PROPERTY_SET:
1104 if (!arg)
1105 return M_PROPERTY_ERROR;
1106 M_PROPERTY_CLAMP(prop, *(float *) arg);
1107 vo_panscan = *(float *) arg;
1108 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1109 return M_PROPERTY_OK;
1110 case M_PROPERTY_STEP_UP:
1111 case M_PROPERTY_STEP_DOWN:
1112 vo_panscan += (arg ? *(float *) arg : 0.1) *
1113 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1114 if (vo_panscan > 1)
1115 vo_panscan = 1;
1116 else if (vo_panscan < 0)
1117 vo_panscan = 0;
1118 vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
1119 return M_PROPERTY_OK;
1120 default:
1121 return m_property_float_range(prop, action, arg, &vo_panscan);
1125 /// Helper to set vo flags.
1126 /** \ingroup PropertyImplHelper
1128 static int mp_property_vo_flag(m_option_t *prop, int action, void *arg,
1129 int vo_ctrl, int *vo_var, MPContext *mpctx)
1132 if (!mpctx->video_out)
1133 return M_PROPERTY_UNAVAILABLE;
1135 switch (action) {
1136 case M_PROPERTY_SET:
1137 if (!arg)
1138 return M_PROPERTY_ERROR;
1139 M_PROPERTY_CLAMP(prop, *(int *) arg);
1140 if (*vo_var == !!*(int *) arg)
1141 return M_PROPERTY_OK;
1142 case M_PROPERTY_STEP_UP:
1143 case M_PROPERTY_STEP_DOWN:
1144 if (mpctx->video_out->config_ok)
1145 vo_control(mpctx->video_out, vo_ctrl, 0);
1146 return M_PROPERTY_OK;
1147 default:
1148 return m_property_flag(prop, action, arg, vo_var);
1152 /// Window always on top (RW)
1153 static int mp_property_ontop(m_option_t *prop, int action, void *arg,
1154 MPContext *mpctx)
1156 return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
1157 &mpctx->opts.vo_ontop, mpctx);
1160 /// Display in the root window (RW)
1161 static int mp_property_rootwin(m_option_t *prop, int action, void *arg,
1162 MPContext *mpctx)
1164 return mp_property_vo_flag(prop, action, arg, VOCTRL_ROOTWIN,
1165 &vo_rootwin, mpctx);
1168 /// Show window borders (RW)
1169 static int mp_property_border(m_option_t *prop, int action, void *arg,
1170 MPContext *mpctx)
1172 return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
1173 &vo_border, mpctx);
1176 /// Framedropping state (RW)
1177 static int mp_property_framedropping(m_option_t *prop, int action,
1178 void *arg, MPContext *mpctx)
1181 if (!mpctx->sh_video)
1182 return M_PROPERTY_UNAVAILABLE;
1184 switch (action) {
1185 case M_PROPERTY_PRINT:
1186 if (!arg)
1187 return M_PROPERTY_ERROR;
1188 *(char **) arg = strdup(frame_dropping == 1 ? mp_gtext("enabled") :
1189 (frame_dropping == 2 ? mp_gtext("hard") :
1190 mp_gtext("disabled")));
1191 return M_PROPERTY_OK;
1192 default:
1193 return m_property_choice(prop, action, arg, &frame_dropping);
1197 /// Color settings, try to use vf/vo then fall back on TV. (RW)
1198 static int mp_property_gamma(m_option_t *prop, int action, void *arg,
1199 MPContext *mpctx)
1201 int *gamma = (int *)((char *)&mpctx->opts + (int)prop->priv);
1202 int r, val;
1204 if (!mpctx->sh_video)
1205 return M_PROPERTY_UNAVAILABLE;
1207 if (gamma[0] == 1000) {
1208 gamma[0] = 0;
1209 get_video_colors(mpctx->sh_video, prop->name, gamma);
1212 switch (action) {
1213 case M_PROPERTY_SET:
1214 if (!arg)
1215 return M_PROPERTY_ERROR;
1216 M_PROPERTY_CLAMP(prop, *(int *) arg);
1217 *gamma = *(int *) arg;
1218 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1219 if (r <= 0)
1220 break;
1221 return r;
1222 case M_PROPERTY_GET:
1223 if (get_video_colors(mpctx->sh_video, prop->name, &val) > 0) {
1224 if (!arg)
1225 return M_PROPERTY_ERROR;
1226 *(int *)arg = val;
1227 return M_PROPERTY_OK;
1229 break;
1230 case M_PROPERTY_STEP_UP:
1231 case M_PROPERTY_STEP_DOWN:
1232 *gamma += (arg ? *(int *) arg : 1) *
1233 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1234 M_PROPERTY_CLAMP(prop, *gamma);
1235 r = set_video_colors(mpctx->sh_video, prop->name, *gamma);
1236 if (r <= 0)
1237 break;
1238 return r;
1239 default:
1240 return M_PROPERTY_NOT_IMPLEMENTED;
1243 #ifdef CONFIG_TV
1244 if (mpctx->demuxer->type == DEMUXER_TYPE_TV) {
1245 int l = strlen(prop->name);
1246 char tv_prop[3 + l + 1];
1247 sprintf(tv_prop, "tv_%s", prop->name);
1248 return mp_property_do(tv_prop, action, arg, mpctx);
1250 #endif
1252 return M_PROPERTY_UNAVAILABLE;
1255 /// VSync (RW)
1256 static int mp_property_vsync(m_option_t *prop, int action, void *arg,
1257 MPContext *mpctx)
1259 return m_property_flag(prop, action, arg, &vo_vsync);
1262 /// Video codec tag (RO)
1263 static int mp_property_video_format(m_option_t *prop, int action,
1264 void *arg, MPContext *mpctx)
1266 char* meta;
1267 if (!mpctx->sh_video)
1268 return M_PROPERTY_UNAVAILABLE;
1269 switch(action) {
1270 case M_PROPERTY_PRINT:
1271 if (!arg)
1272 return M_PROPERTY_ERROR;
1273 switch(mpctx->sh_video->format) {
1274 case 0x10000001:
1275 meta = strdup ("mpeg1"); break;
1276 case 0x10000002:
1277 meta = strdup ("mpeg2"); break;
1278 case 0x10000004:
1279 meta = strdup ("mpeg4"); break;
1280 case 0x10000005:
1281 meta = strdup ("h264"); break;
1282 default:
1283 if(mpctx->sh_video->format >= 0x20202020) {
1284 meta = malloc(5);
1285 sprintf (meta, "%.4s", (char *) &mpctx->sh_video->format);
1286 } else {
1287 meta = malloc(20);
1288 sprintf (meta, "0x%08X", mpctx->sh_video->format);
1291 *(char**)arg = meta;
1292 return M_PROPERTY_OK;
1294 return m_property_int_ro(prop, action, arg, mpctx->sh_video->format);
1297 /// Video codec name (RO)
1298 static int mp_property_video_codec(m_option_t *prop, int action,
1299 void *arg, MPContext *mpctx)
1301 if (!mpctx->sh_video || !mpctx->sh_video->codec)
1302 return M_PROPERTY_UNAVAILABLE;
1303 return m_property_string_ro(prop, action, arg, mpctx->sh_video->codec->name);
1307 /// Video bitrate (RO)
1308 static int mp_property_video_bitrate(m_option_t *prop, int action,
1309 void *arg, MPContext *mpctx)
1311 if (!mpctx->sh_video)
1312 return M_PROPERTY_UNAVAILABLE;
1313 return m_property_bitrate(prop, action, arg, mpctx->sh_video->i_bps);
1316 /// Video display width (RO)
1317 static int mp_property_width(m_option_t *prop, int action, void *arg,
1318 MPContext *mpctx)
1320 if (!mpctx->sh_video)
1321 return M_PROPERTY_UNAVAILABLE;
1322 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_w);
1325 /// Video display height (RO)
1326 static int mp_property_height(m_option_t *prop, int action, void *arg,
1327 MPContext *mpctx)
1329 if (!mpctx->sh_video)
1330 return M_PROPERTY_UNAVAILABLE;
1331 return m_property_int_ro(prop, action, arg, mpctx->sh_video->disp_h);
1334 /// Video fps (RO)
1335 static int mp_property_fps(m_option_t *prop, int action, void *arg,
1336 MPContext *mpctx)
1338 if (!mpctx->sh_video)
1339 return M_PROPERTY_UNAVAILABLE;
1340 return m_property_float_ro(prop, action, arg, mpctx->sh_video->fps);
1343 /// Video aspect (RO)
1344 static int mp_property_aspect(m_option_t *prop, int action, void *arg,
1345 MPContext *mpctx)
1347 if (!mpctx->sh_video)
1348 return M_PROPERTY_UNAVAILABLE;
1349 return m_property_float_ro(prop, action, arg, mpctx->sh_video->aspect);
1352 ///@}
1354 /// \defgroup SubProprties Subtitles properties
1355 /// \ingroup Properties
1356 ///@{
1358 /// Text subtitle position (RW)
1359 static int mp_property_sub_pos(m_option_t *prop, int action, void *arg,
1360 MPContext *mpctx)
1362 switch (action) {
1363 case M_PROPERTY_SET:
1364 if (!arg)
1365 return M_PROPERTY_ERROR;
1366 case M_PROPERTY_STEP_UP:
1367 case M_PROPERTY_STEP_DOWN:
1368 vo_osd_changed(OSDTYPE_SUBTITLE);
1369 default:
1370 return m_property_int_range(prop, action, arg, &sub_pos);
1374 /// Selected subtitles (RW)
1375 static int mp_property_sub(m_option_t *prop, int action, void *arg,
1376 MPContext *mpctx)
1378 struct MPOpts *opts = &mpctx->opts;
1379 demux_stream_t *const d_sub = mpctx->d_sub;
1380 const int global_sub_size = mpctx->global_sub_size;
1381 int source = -1, reset_spu = 0;
1382 char *sub_name;
1384 if (global_sub_size <= 0)
1385 return M_PROPERTY_UNAVAILABLE;
1387 switch (action) {
1388 case M_PROPERTY_GET:
1389 if (!arg)
1390 return M_PROPERTY_ERROR;
1391 *(int *) arg = mpctx->global_sub_pos;
1392 return M_PROPERTY_OK;
1393 case M_PROPERTY_PRINT:
1394 if (!arg)
1395 return M_PROPERTY_ERROR;
1396 *(char **) arg = malloc(64);
1397 (*(char **) arg)[63] = 0;
1398 sub_name = 0;
1399 if (subdata)
1400 sub_name = subdata->filename;
1401 #ifdef CONFIG_ASS
1402 if (ass_track && ass_track->name)
1403 sub_name = ass_track->name;
1404 #endif
1405 if (sub_name) {
1406 char *tmp, *tmp2;
1407 tmp = sub_name;
1408 if ((tmp2 = strrchr(tmp, '/')))
1409 tmp = tmp2 + 1;
1411 snprintf(*(char **) arg, 63, "(%d) %s%s",
1412 mpctx->set_of_sub_pos + 1,
1413 strlen(tmp) < 20 ? "" : "...",
1414 strlen(tmp) < 20 ? tmp : tmp + strlen(tmp) - 19);
1415 return M_PROPERTY_OK;
1417 #ifdef CONFIG_DVDNAV
1418 if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
1419 if (vo_spudec && opts->sub_id >= 0) {
1420 unsigned char lang[3];
1421 if (mp_dvdnav_lang_from_sid(mpctx->stream, opts->sub_id, lang)) {
1422 snprintf(*(char **) arg, 63, "(%d) %s", opts->sub_id, lang);
1423 return M_PROPERTY_OK;
1427 #endif
1429 if ((mpctx->demuxer->type == DEMUXER_TYPE_MATROSKA
1430 || mpctx->demuxer->type == DEMUXER_TYPE_LAVF
1431 || mpctx->demuxer->type == DEMUXER_TYPE_LAVF_PREFERRED
1432 || mpctx->demuxer->type == DEMUXER_TYPE_OGG)
1433 && d_sub && d_sub->sh && opts->sub_id >= 0) {
1434 const char* lang = ((sh_sub_t*)d_sub->sh)->lang;
1435 if (!lang) lang = mp_gtext("unknown");
1436 snprintf(*(char **) arg, 63, "(%d) %s", opts->sub_id, lang);
1437 return M_PROPERTY_OK;
1440 if (vo_vobsub && vobsub_id >= 0) {
1441 const char *language = mp_gtext("unknown");
1442 language = vobsub_get_id(vo_vobsub, (unsigned int) vobsub_id);
1443 snprintf(*(char **) arg, 63, "(%d) %s",
1444 vobsub_id, language ? language : mp_gtext("unknown"));
1445 return M_PROPERTY_OK;
1447 #ifdef CONFIG_DVDREAD
1448 if (vo_spudec && mpctx->stream->type == STREAMTYPE_DVD
1449 && opts->sub_id >= 0) {
1450 char lang[3];
1451 int code = dvd_lang_from_sid(mpctx->stream, opts->sub_id);
1452 lang[0] = code >> 8;
1453 lang[1] = code;
1454 lang[2] = 0;
1455 snprintf(*(char **) arg, 63, "(%d) %s", opts->sub_id, lang);
1456 return M_PROPERTY_OK;
1458 #endif
1459 if (opts->sub_id >= 0) {
1460 snprintf(*(char **) arg, 63, "(%d) %s", opts->sub_id,
1461 mp_gtext("unknown"));
1462 return M_PROPERTY_OK;
1464 snprintf(*(char **) arg, 63, mp_gtext("disabled"));
1465 return M_PROPERTY_OK;
1467 case M_PROPERTY_SET:
1468 if (!arg)
1469 return M_PROPERTY_ERROR;
1470 if (*(int *) arg < -1)
1471 *(int *) arg = -1;
1472 else if (*(int *) arg >= global_sub_size)
1473 *(int *) arg = global_sub_size - 1;
1474 mpctx->global_sub_pos = *(int *) arg;
1475 break;
1476 case M_PROPERTY_STEP_UP:
1477 mpctx->global_sub_pos += 2;
1478 mpctx->global_sub_pos =
1479 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1480 break;
1481 case M_PROPERTY_STEP_DOWN:
1482 mpctx->global_sub_pos += global_sub_size + 1;
1483 mpctx->global_sub_pos =
1484 (mpctx->global_sub_pos % (global_sub_size + 1)) - 1;
1485 break;
1486 default:
1487 return M_PROPERTY_NOT_IMPLEMENTED;
1490 if (mpctx->global_sub_pos >= 0)
1491 source = sub_source(mpctx);
1493 mp_msg(MSGT_CPLAYER, MSGL_DBG3,
1494 "subtitles: %d subs, (v@%d s@%d d@%d), @%d, source @%d\n",
1495 global_sub_size,
1496 mpctx->global_sub_indices[SUB_SOURCE_VOBSUB],
1497 mpctx->global_sub_indices[SUB_SOURCE_SUBS],
1498 mpctx->global_sub_indices[SUB_SOURCE_DEMUX],
1499 mpctx->global_sub_pos, source);
1501 mpctx->set_of_sub_pos = -1;
1502 subdata = NULL;
1504 vobsub_id = -1;
1505 opts->sub_id = -1;
1506 if (d_sub) {
1507 if (d_sub->id > -2)
1508 reset_spu = 1;
1509 d_sub->id = -2;
1511 #ifdef CONFIG_ASS
1512 ass_track = 0;
1513 #endif
1515 if (source == SUB_SOURCE_VOBSUB) {
1516 vobsub_id = vobsub_get_id_by_index(vo_vobsub, mpctx->global_sub_pos - mpctx->global_sub_indices[SUB_SOURCE_VOBSUB]);
1517 } else if (source == SUB_SOURCE_SUBS) {
1518 mpctx->set_of_sub_pos =
1519 mpctx->global_sub_pos - mpctx->global_sub_indices[SUB_SOURCE_SUBS];
1520 #ifdef CONFIG_ASS
1521 if (opts->ass_enabled && mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos])
1522 ass_track = mpctx->set_of_ass_tracks[mpctx->set_of_sub_pos];
1523 else
1524 #endif
1526 subdata = mpctx->set_of_subtitles[mpctx->set_of_sub_pos];
1527 vo_osd_changed(OSDTYPE_SUBTITLE);
1529 } else if (source == SUB_SOURCE_DEMUX) {
1530 opts->sub_id =
1531 mpctx->global_sub_pos - mpctx->global_sub_indices[SUB_SOURCE_DEMUX];
1532 if (d_sub && opts->sub_id < MAX_S_STREAMS) {
1533 int i = 0;
1534 // default: assume 1:1 mapping of sid and stream id
1535 d_sub->id = opts->sub_id;
1536 d_sub->sh = mpctx->demuxer->s_streams[d_sub->id];
1537 ds_free_packs(d_sub);
1538 for (i = 0; i < MAX_S_STREAMS; i++) {
1539 sh_sub_t *sh = mpctx->demuxer->s_streams[i];
1540 if (sh && sh->sid == opts->sub_id) {
1541 d_sub->id = i;
1542 d_sub->sh = sh;
1543 break;
1546 if (d_sub->sh && d_sub->id >= 0) {
1547 sh_sub_t *sh = d_sub->sh;
1548 if (sh->type == 'v')
1549 init_vo_spudec(mpctx);
1550 #ifdef CONFIG_ASS
1551 else if (opts->ass_enabled)
1552 ass_track = sh->ass_track;
1553 #endif
1554 } else {
1555 d_sub->id = -2;
1556 d_sub->sh = NULL;
1560 #ifdef CONFIG_DVDREAD
1561 if (vo_spudec
1562 && (mpctx->stream->type == STREAMTYPE_DVD
1563 || mpctx->stream->type == STREAMTYPE_DVDNAV)
1564 && opts->sub_id < 0 && reset_spu) {
1565 d_sub->id = -2;
1566 d_sub->sh = NULL;
1568 #endif
1570 update_subtitles(mpctx, &mpctx->opts, mpctx->sh_video, 0, 0, d_sub, 1);
1572 return M_PROPERTY_OK;
1575 /// Selected sub source (RW)
1576 static int mp_property_sub_source(m_option_t *prop, int action, void *arg,
1577 MPContext *mpctx)
1579 int source;
1580 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1581 return M_PROPERTY_UNAVAILABLE;
1583 switch (action) {
1584 case M_PROPERTY_GET:
1585 if (!arg)
1586 return M_PROPERTY_ERROR;
1587 *(int *) arg = sub_source(mpctx);
1588 return M_PROPERTY_OK;
1589 case M_PROPERTY_PRINT:
1590 if (!arg)
1591 return M_PROPERTY_ERROR;
1592 *(char **) arg = malloc(64);
1593 (*(char **) arg)[63] = 0;
1594 switch (sub_source(mpctx))
1596 case SUB_SOURCE_SUBS:
1597 snprintf(*(char **) arg, 63, mp_gtext("file"));
1598 break;
1599 case SUB_SOURCE_VOBSUB:
1600 snprintf(*(char **) arg, 63, mp_gtext("vobsub"));
1601 break;
1602 case SUB_SOURCE_DEMUX:
1603 snprintf(*(char **) arg, 63, mp_gtext("embedded"));
1604 break;
1605 default:
1606 snprintf(*(char **) arg, 63, mp_gtext("disabled"));
1608 return M_PROPERTY_OK;
1609 case M_PROPERTY_SET:
1610 if (!arg)
1611 return M_PROPERTY_ERROR;
1612 M_PROPERTY_CLAMP(prop, *(int*)arg);
1613 if (*(int *) arg < 0)
1614 mpctx->global_sub_pos = -1;
1615 else if (*(int *) arg != sub_source(mpctx)) {
1616 if (*(int *) arg != sub_source_by_pos(mpctx, mpctx->global_sub_indices[*(int *) arg]))
1617 return M_PROPERTY_UNAVAILABLE;
1618 mpctx->global_sub_pos = mpctx->global_sub_indices[*(int *) arg];
1620 break;
1621 case M_PROPERTY_STEP_UP:
1622 case M_PROPERTY_STEP_DOWN: {
1623 int step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
1624 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1625 int step = (step_all > 0) ? 1 : -1;
1626 int cur_source = sub_source(mpctx);
1627 source = cur_source;
1628 while (step_all) {
1629 source += step;
1630 if (source >= SUB_SOURCES)
1631 source = -1;
1632 else if (source < -1)
1633 source = SUB_SOURCES - 1;
1634 if (source == cur_source || source == -1 ||
1635 source == sub_source_by_pos(mpctx, mpctx->global_sub_indices[source]))
1636 step_all -= step;
1638 if (source == cur_source)
1639 return M_PROPERTY_OK;
1640 if (source == -1)
1641 mpctx->global_sub_pos = -1;
1642 else
1643 mpctx->global_sub_pos = mpctx->global_sub_indices[source];
1644 break;
1646 default:
1647 return M_PROPERTY_NOT_IMPLEMENTED;
1649 --mpctx->global_sub_pos;
1650 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1653 /// Selected subtitles from specific source (RW)
1654 static int mp_property_sub_by_type(m_option_t *prop, int action, void *arg,
1655 MPContext *mpctx)
1657 int source, is_cur_source, offset;
1658 if (!mpctx->sh_video || mpctx->global_sub_size <= 0)
1659 return M_PROPERTY_UNAVAILABLE;
1661 if (!strcmp(prop->name, "sub_file"))
1662 source = SUB_SOURCE_SUBS;
1663 else if (!strcmp(prop->name, "sub_vob"))
1664 source = SUB_SOURCE_VOBSUB;
1665 else if (!strcmp(prop->name, "sub_demux"))
1666 source = SUB_SOURCE_DEMUX;
1667 else
1668 return M_PROPERTY_ERROR;
1670 offset = mpctx->global_sub_indices[source];
1671 if (offset < 0 || source != sub_source_by_pos(mpctx, offset))
1672 return M_PROPERTY_UNAVAILABLE;
1674 is_cur_source = sub_source(mpctx) == source;
1675 switch (action) {
1676 case M_PROPERTY_GET:
1677 if (!arg)
1678 return M_PROPERTY_ERROR;
1679 if (is_cur_source) {
1680 *(int *) arg = mpctx->global_sub_pos - offset;
1681 if (source == SUB_SOURCE_VOBSUB)
1682 *(int *) arg = vobsub_get_id_by_index(vo_vobsub, *(int *) arg);
1684 else
1685 *(int *) arg = -1;
1686 return M_PROPERTY_OK;
1687 case M_PROPERTY_PRINT:
1688 if (!arg)
1689 return M_PROPERTY_ERROR;
1690 if (is_cur_source)
1691 return mp_property_sub(prop, M_PROPERTY_PRINT, arg, mpctx);
1692 *(char **) arg = malloc(64);
1693 (*(char **) arg)[63] = 0;
1694 snprintf(*(char **) arg, 63, mp_gtext("disabled"));
1695 return M_PROPERTY_OK;
1696 case M_PROPERTY_SET:
1697 if (!arg)
1698 return M_PROPERTY_ERROR;
1699 if (*(int *) arg >= 0) {
1700 int index = *(int *)arg;
1701 if (source == SUB_SOURCE_VOBSUB)
1702 index = vobsub_get_index_by_id(vo_vobsub, index);
1703 mpctx->global_sub_pos = offset + index;
1704 if (index < 0 || mpctx->global_sub_pos >= mpctx->global_sub_size
1705 || sub_source(mpctx) != source) {
1706 mpctx->global_sub_pos = -1;
1707 *(int *) arg = -1;
1710 else
1711 mpctx->global_sub_pos = -1;
1712 break;
1713 case M_PROPERTY_STEP_UP:
1714 case M_PROPERTY_STEP_DOWN: {
1715 int step_all = (arg && *(int*)arg != 0 ? *(int*)arg : 1)
1716 * (action == M_PROPERTY_STEP_UP ? 1 : -1);
1717 int step = (step_all > 0) ? 1 : -1;
1718 int max_sub_pos_for_source = -1;
1719 if (!is_cur_source)
1720 mpctx->global_sub_pos = -1;
1721 while (step_all) {
1722 if (mpctx->global_sub_pos == -1) {
1723 if (step > 0)
1724 mpctx->global_sub_pos = offset;
1725 else if (max_sub_pos_for_source == -1) {
1726 // Find max pos for specific source
1727 mpctx->global_sub_pos = mpctx->global_sub_size - 1;
1728 while (mpctx->global_sub_pos >= 0
1729 && sub_source(mpctx) != source)
1730 --mpctx->global_sub_pos;
1732 else
1733 mpctx->global_sub_pos = max_sub_pos_for_source;
1735 else {
1736 mpctx->global_sub_pos += step;
1737 if (mpctx->global_sub_pos < offset ||
1738 mpctx->global_sub_pos >= mpctx->global_sub_size ||
1739 sub_source(mpctx) != source)
1740 mpctx->global_sub_pos = -1;
1742 step_all -= step;
1744 break;
1746 default:
1747 return M_PROPERTY_NOT_IMPLEMENTED;
1749 --mpctx->global_sub_pos;
1750 return mp_property_sub(prop, M_PROPERTY_STEP_UP, NULL, mpctx);
1753 /// Subtitle delay (RW)
1754 static int mp_property_sub_delay(m_option_t *prop, int action, void *arg,
1755 MPContext *mpctx)
1757 if (!mpctx->sh_video)
1758 return M_PROPERTY_UNAVAILABLE;
1759 return m_property_delay(prop, action, arg, &sub_delay);
1762 /// Alignment of text subtitles (RW)
1763 static int mp_property_sub_alignment(m_option_t *prop, int action,
1764 void *arg, MPContext *mpctx)
1766 char *name[] = { _("top"), _("center"), _("bottom") };
1768 if (!mpctx->sh_video || mpctx->global_sub_pos < 0
1769 || sub_source(mpctx) != SUB_SOURCE_SUBS)
1770 return M_PROPERTY_UNAVAILABLE;
1772 switch (action) {
1773 case M_PROPERTY_PRINT:
1774 if (!arg)
1775 return M_PROPERTY_ERROR;
1776 M_PROPERTY_CLAMP(prop, sub_alignment);
1777 *(char **) arg = strdup(mp_gtext(name[sub_alignment]));
1778 return M_PROPERTY_OK;
1779 case M_PROPERTY_SET:
1780 if (!arg)
1781 return M_PROPERTY_ERROR;
1782 case M_PROPERTY_STEP_UP:
1783 case M_PROPERTY_STEP_DOWN:
1784 vo_osd_changed(OSDTYPE_SUBTITLE);
1785 default:
1786 return m_property_choice(prop, action, arg, &sub_alignment);
1790 /// Subtitle visibility (RW)
1791 static int mp_property_sub_visibility(m_option_t *prop, int action,
1792 void *arg, MPContext *mpctx)
1794 if (!mpctx->sh_video)
1795 return M_PROPERTY_UNAVAILABLE;
1797 switch (action) {
1798 case M_PROPERTY_SET:
1799 if (!arg)
1800 return M_PROPERTY_ERROR;
1801 case M_PROPERTY_STEP_UP:
1802 case M_PROPERTY_STEP_DOWN:
1803 vo_osd_changed(OSDTYPE_SUBTITLE);
1804 if (vo_spudec)
1805 vo_osd_changed(OSDTYPE_SPU);
1806 default:
1807 return m_property_flag(prop, action, arg, &sub_visibility);
1811 #ifdef CONFIG_ASS
1812 /// Use margins for libass subtitles (RW)
1813 static int mp_property_ass_use_margins(m_option_t *prop, int action,
1814 void *arg, MPContext *mpctx)
1816 if (!mpctx->sh_video)
1817 return M_PROPERTY_UNAVAILABLE;
1819 switch (action) {
1820 case M_PROPERTY_SET:
1821 if (!arg)
1822 return M_PROPERTY_ERROR;
1823 case M_PROPERTY_STEP_UP:
1824 case M_PROPERTY_STEP_DOWN:
1825 ass_force_reload = 1;
1826 default:
1827 return m_property_flag(prop, action, arg, &ass_use_margins);
1830 #endif
1832 /// Show only forced subtitles (RW)
1833 static int mp_property_sub_forced_only(m_option_t *prop, int action,
1834 void *arg, MPContext *mpctx)
1836 if (!vo_spudec)
1837 return M_PROPERTY_UNAVAILABLE;
1839 switch (action) {
1840 case M_PROPERTY_SET:
1841 if (!arg)
1842 return M_PROPERTY_ERROR;
1843 case M_PROPERTY_STEP_UP:
1844 case M_PROPERTY_STEP_DOWN:
1845 m_property_flag(prop, action, arg, &forced_subs_only);
1846 spudec_set_forced_subs_only(vo_spudec, forced_subs_only);
1847 return M_PROPERTY_OK;
1848 default:
1849 return m_property_flag(prop, action, arg, &forced_subs_only);
1854 #ifdef CONFIG_FREETYPE
1855 /// Subtitle scale (RW)
1856 static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
1857 MPContext *mpctx)
1859 struct MPOpts *opts = &mpctx->opts;
1861 switch (action) {
1862 case M_PROPERTY_SET:
1863 if (!arg)
1864 return M_PROPERTY_ERROR;
1865 M_PROPERTY_CLAMP(prop, *(float *) arg);
1866 #ifdef CONFIG_ASS
1867 if (opts->ass_enabled) {
1868 ass_font_scale = *(float *) arg;
1869 ass_force_reload = 1;
1871 #endif
1872 text_font_scale_factor = *(float *) arg;
1873 force_load_font = 1;
1874 return M_PROPERTY_OK;
1875 case M_PROPERTY_STEP_UP:
1876 case M_PROPERTY_STEP_DOWN:
1877 #ifdef CONFIG_ASS
1878 if (opts->ass_enabled) {
1879 ass_font_scale += (arg ? *(float *) arg : 0.1)*
1880 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
1881 M_PROPERTY_CLAMP(prop, ass_font_scale);
1882 ass_force_reload = 1;
1884 #endif
1885 text_font_scale_factor += (arg ? *(float *) arg : 0.1)*
1886 (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
1887 M_PROPERTY_CLAMP(prop, text_font_scale_factor);
1888 force_load_font = 1;
1889 return M_PROPERTY_OK;
1890 default:
1891 #ifdef CONFIG_ASS
1892 if (opts->ass_enabled)
1893 return m_property_float_ro(prop, action, arg, ass_font_scale);
1894 else
1895 #endif
1896 return m_property_float_ro(prop, action, arg, text_font_scale_factor);
1899 #endif
1901 ///@}
1903 /// \defgroup TVProperties TV properties
1904 /// \ingroup Properties
1905 ///@{
1907 #ifdef CONFIG_TV
1909 /// TV color settings (RW)
1910 static int mp_property_tv_color(m_option_t *prop, int action, void *arg,
1911 MPContext *mpctx)
1913 int r, val;
1914 tvi_handle_t *tvh = mpctx->demuxer->priv;
1915 if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
1916 return M_PROPERTY_UNAVAILABLE;
1918 switch (action) {
1919 case M_PROPERTY_SET:
1920 if (!arg)
1921 return M_PROPERTY_ERROR;
1922 M_PROPERTY_CLAMP(prop, *(int *) arg);
1923 return tv_set_color_options(tvh, (int) prop->priv, *(int *) arg);
1924 case M_PROPERTY_GET:
1925 return tv_get_color_options(tvh, (int) prop->priv, arg);
1926 case M_PROPERTY_STEP_UP:
1927 case M_PROPERTY_STEP_DOWN:
1928 if ((r = tv_get_color_options(tvh, (int) prop->priv, &val)) >= 0) {
1929 if (!r)
1930 return M_PROPERTY_ERROR;
1931 val += (arg ? *(int *) arg : 1) *
1932 (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1933 M_PROPERTY_CLAMP(prop, val);
1934 return tv_set_color_options(tvh, (int) prop->priv, val);
1936 return M_PROPERTY_ERROR;
1938 return M_PROPERTY_NOT_IMPLEMENTED;
1941 #endif
1943 static int mp_property_teletext_common(m_option_t *prop, int action, void *arg,
1944 MPContext *mpctx)
1946 int val,result;
1947 int base_ioctl=(int)prop->priv;
1949 for teletext's GET,SET,STEP ioctls this is not 0
1950 SET is GET+1
1951 STEP is GET+2
1953 if (!mpctx->demuxer || !mpctx->demuxer->teletext)
1954 return M_PROPERTY_UNAVAILABLE;
1955 if(!base_ioctl)
1956 return M_PROPERTY_ERROR;
1958 switch (action) {
1959 case M_PROPERTY_GET:
1960 if (!arg)
1961 return M_PROPERTY_ERROR;
1962 result=teletext_control(mpctx->demuxer->teletext, base_ioctl, arg);
1963 break;
1964 case M_PROPERTY_SET:
1965 if (!arg)
1966 return M_PROPERTY_ERROR;
1967 M_PROPERTY_CLAMP(prop, *(int *) arg);
1968 result=teletext_control(mpctx->demuxer->teletext, base_ioctl+1, arg);
1969 break;
1970 case M_PROPERTY_STEP_UP:
1971 case M_PROPERTY_STEP_DOWN:
1972 result=teletext_control(mpctx->demuxer->teletext, base_ioctl, &val);
1973 val += (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
1974 result=teletext_control(mpctx->demuxer->teletext, base_ioctl+1, &val);
1975 break;
1976 default:
1977 return M_PROPERTY_NOT_IMPLEMENTED;
1980 return result == VBI_CONTROL_TRUE ? M_PROPERTY_OK : M_PROPERTY_ERROR;
1983 static int mp_property_teletext_mode(m_option_t *prop, int action, void *arg,
1984 MPContext *mpctx)
1986 int result;
1987 int val;
1989 //with tvh==NULL will fail too
1990 result=mp_property_teletext_common(prop,action,arg,mpctx);
1991 if(result!=M_PROPERTY_OK)
1992 return result;
1994 if(teletext_control(mpctx->demuxer->teletext,
1995 (int)prop->priv, &val)==VBI_CONTROL_TRUE && val)
1996 mp_input_set_section(mpctx->input, "teletext");
1997 else
1998 mp_input_set_section(mpctx->input, "tv");
1999 return M_PROPERTY_OK;
2002 static int mp_property_teletext_page(m_option_t *prop, int action, void *arg,
2003 MPContext *mpctx)
2005 int result;
2006 int val;
2007 if (!mpctx->demuxer->teletext)
2008 return M_PROPERTY_UNAVAILABLE;
2009 switch(action){
2010 case M_PROPERTY_STEP_UP:
2011 case M_PROPERTY_STEP_DOWN:
2012 //This should be handled separately
2013 val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
2014 result=teletext_control(mpctx->demuxer->teletext,
2015 TV_VBI_CONTROL_STEP_PAGE, &val);
2016 break;
2017 default:
2018 result=mp_property_teletext_common(prop,action,arg,mpctx);
2020 return result;
2023 ///@}
2025 /// All properties available in MPlayer.
2026 /** \ingroup Properties
2028 static const m_option_t mp_properties[] = {
2029 // General
2030 { "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
2031 M_OPT_RANGE, 0, 3, NULL },
2032 { "loop", mp_property_loop, CONF_TYPE_INT,
2033 M_OPT_MIN, -1, 0, NULL },
2034 { "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
2035 M_OPT_RANGE, 0.01, 100.0, NULL },
2036 { "filename", mp_property_filename, CONF_TYPE_STRING,
2037 0, 0, 0, NULL },
2038 { "path", mp_property_path, CONF_TYPE_STRING,
2039 0, 0, 0, NULL },
2040 { "demuxer", mp_property_demuxer, CONF_TYPE_STRING,
2041 0, 0, 0, NULL },
2042 { "stream_pos", mp_property_stream_pos, CONF_TYPE_POSITION,
2043 M_OPT_MIN, 0, 0, NULL },
2044 { "stream_start", mp_property_stream_start, CONF_TYPE_POSITION,
2045 M_OPT_MIN, 0, 0, NULL },
2046 { "stream_end", mp_property_stream_end, CONF_TYPE_POSITION,
2047 M_OPT_MIN, 0, 0, NULL },
2048 { "stream_length", mp_property_stream_length, CONF_TYPE_POSITION,
2049 M_OPT_MIN, 0, 0, NULL },
2050 { "length", mp_property_length, CONF_TYPE_TIME,
2051 M_OPT_MIN, 0, 0, NULL },
2052 { "percent_pos", mp_property_percent_pos, CONF_TYPE_INT,
2053 M_OPT_RANGE, 0, 100, NULL },
2054 { "time_pos", mp_property_time_pos, CONF_TYPE_TIME,
2055 M_OPT_MIN, 0, 0, NULL },
2056 { "chapter", mp_property_chapter, CONF_TYPE_INT,
2057 M_OPT_MIN, 0, 0, NULL },
2058 { "chapters", mp_property_chapters, CONF_TYPE_INT,
2059 0, 0, 0, NULL },
2060 { "angle", mp_property_angle, CONF_TYPE_INT,
2061 CONF_RANGE, -2, 10, NULL },
2062 { "metadata", mp_property_metadata, CONF_TYPE_STRING_LIST,
2063 0, 0, 0, NULL },
2064 { "pause", mp_property_pause, CONF_TYPE_FLAG,
2065 M_OPT_RANGE, 0, 1, NULL },
2067 // Audio
2068 { "volume", mp_property_volume, CONF_TYPE_FLOAT,
2069 M_OPT_RANGE, 0, 100, NULL },
2070 { "mute", mp_property_mute, CONF_TYPE_FLAG,
2071 M_OPT_RANGE, 0, 1, NULL },
2072 { "audio_delay", mp_property_audio_delay, CONF_TYPE_FLOAT,
2073 M_OPT_RANGE, -100, 100, NULL },
2074 { "audio_format", mp_property_audio_format, CONF_TYPE_INT,
2075 0, 0, 0, NULL },
2076 { "audio_codec", mp_property_audio_codec, CONF_TYPE_STRING,
2077 0, 0, 0, NULL },
2078 { "audio_bitrate", mp_property_audio_bitrate, CONF_TYPE_INT,
2079 0, 0, 0, NULL },
2080 { "samplerate", mp_property_samplerate, CONF_TYPE_INT,
2081 0, 0, 0, NULL },
2082 { "channels", mp_property_channels, CONF_TYPE_INT,
2083 0, 0, 0, NULL },
2084 { "switch_audio", mp_property_audio, CONF_TYPE_INT,
2085 CONF_RANGE, -2, 65535, NULL },
2086 { "balance", mp_property_balance, CONF_TYPE_FLOAT,
2087 M_OPT_RANGE, -1, 1, NULL },
2089 // Video
2090 { "fullscreen", mp_property_fullscreen, CONF_TYPE_FLAG,
2091 M_OPT_RANGE, 0, 1, NULL },
2092 { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG,
2093 M_OPT_RANGE, 0, 1, NULL },
2094 { "yuv_colorspace", mp_property_yuv_colorspace, CONF_TYPE_INT,
2095 M_OPT_RANGE, 0, 2, NULL },
2096 { "ontop", mp_property_ontop, CONF_TYPE_FLAG,
2097 M_OPT_RANGE, 0, 1, NULL },
2098 { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG,
2099 M_OPT_RANGE, 0, 1, NULL },
2100 { "border", mp_property_border, CONF_TYPE_FLAG,
2101 M_OPT_RANGE, 0, 1, NULL },
2102 { "framedropping", mp_property_framedropping, CONF_TYPE_INT,
2103 M_OPT_RANGE, 0, 2, NULL },
2104 { "gamma", mp_property_gamma, CONF_TYPE_INT,
2105 M_OPT_RANGE, -100, 100, (void *)offsetof(struct MPOpts, vo_gamma_gamma)},
2106 { "brightness", mp_property_gamma, CONF_TYPE_INT,
2107 M_OPT_RANGE, -100, 100, (void *)offsetof(struct MPOpts, vo_gamma_brightness) },
2108 { "contrast", mp_property_gamma, CONF_TYPE_INT,
2109 M_OPT_RANGE, -100, 100, (void *)offsetof(struct MPOpts, vo_gamma_contrast) },
2110 { "saturation", mp_property_gamma, CONF_TYPE_INT,
2111 M_OPT_RANGE, -100, 100, (void *)offsetof(struct MPOpts, vo_gamma_saturation) },
2112 { "hue", mp_property_gamma, CONF_TYPE_INT,
2113 M_OPT_RANGE, -100, 100, (void *)offsetof(struct MPOpts, vo_gamma_hue) },
2114 { "panscan", mp_property_panscan, CONF_TYPE_FLOAT,
2115 M_OPT_RANGE, 0, 1, NULL },
2116 { "vsync", mp_property_vsync, CONF_TYPE_FLAG,
2117 M_OPT_RANGE, 0, 1, NULL },
2118 { "video_format", mp_property_video_format, CONF_TYPE_INT,
2119 0, 0, 0, NULL },
2120 { "video_codec", mp_property_video_codec, CONF_TYPE_STRING,
2121 0, 0, 0, NULL },
2122 { "video_bitrate", mp_property_video_bitrate, CONF_TYPE_INT,
2123 0, 0, 0, NULL },
2124 { "width", mp_property_width, CONF_TYPE_INT,
2125 0, 0, 0, NULL },
2126 { "height", mp_property_height, CONF_TYPE_INT,
2127 0, 0, 0, NULL },
2128 { "fps", mp_property_fps, CONF_TYPE_FLOAT,
2129 0, 0, 0, NULL },
2130 { "aspect", mp_property_aspect, CONF_TYPE_FLOAT,
2131 0, 0, 0, NULL },
2132 { "switch_video", mp_property_video, CONF_TYPE_INT,
2133 CONF_RANGE, -2, 65535, NULL },
2134 { "switch_program", mp_property_program, CONF_TYPE_INT,
2135 CONF_RANGE, -1, 65535, NULL },
2137 // Subs
2138 { "sub", mp_property_sub, CONF_TYPE_INT,
2139 M_OPT_MIN, -1, 0, NULL },
2140 { "sub_source", mp_property_sub_source, CONF_TYPE_INT,
2141 M_OPT_RANGE, -1, SUB_SOURCES - 1, NULL },
2142 { "sub_vob", mp_property_sub_by_type, CONF_TYPE_INT,
2143 M_OPT_MIN, -1, 0, NULL },
2144 { "sub_demux", mp_property_sub_by_type, CONF_TYPE_INT,
2145 M_OPT_MIN, -1, 0, NULL },
2146 { "sub_file", mp_property_sub_by_type, CONF_TYPE_INT,
2147 M_OPT_MIN, -1, 0, NULL },
2148 { "sub_delay", mp_property_sub_delay, CONF_TYPE_FLOAT,
2149 0, 0, 0, NULL },
2150 { "sub_pos", mp_property_sub_pos, CONF_TYPE_INT,
2151 M_OPT_RANGE, 0, 100, NULL },
2152 { "sub_alignment", mp_property_sub_alignment, CONF_TYPE_INT,
2153 M_OPT_RANGE, 0, 2, NULL },
2154 { "sub_visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
2155 M_OPT_RANGE, 0, 1, NULL },
2156 { "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
2157 M_OPT_RANGE, 0, 1, NULL },
2158 #ifdef CONFIG_FREETYPE
2159 { "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
2160 M_OPT_RANGE, 0, 100, NULL },
2161 #endif
2162 #ifdef CONFIG_ASS
2163 { "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
2164 M_OPT_RANGE, 0, 1, NULL },
2165 #endif
2167 #ifdef CONFIG_TV
2168 { "tv_brightness", mp_property_tv_color, CONF_TYPE_INT,
2169 M_OPT_RANGE, -100, 100, (void *) TV_COLOR_BRIGHTNESS },
2170 { "tv_contrast", mp_property_tv_color, CONF_TYPE_INT,
2171 M_OPT_RANGE, -100, 100, (void *) TV_COLOR_CONTRAST },
2172 { "tv_saturation", mp_property_tv_color, CONF_TYPE_INT,
2173 M_OPT_RANGE, -100, 100, (void *) TV_COLOR_SATURATION },
2174 { "tv_hue", mp_property_tv_color, CONF_TYPE_INT,
2175 M_OPT_RANGE, -100, 100, (void *) TV_COLOR_HUE },
2176 #endif
2177 { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
2178 M_OPT_RANGE, 100, 899, (void*)TV_VBI_CONTROL_GET_PAGE },
2179 { "teletext_subpage", mp_property_teletext_common, CONF_TYPE_INT,
2180 M_OPT_RANGE, 0, 64, (void*)TV_VBI_CONTROL_GET_SUBPAGE },
2181 { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_FLAG,
2182 M_OPT_RANGE, 0, 1, (void*)TV_VBI_CONTROL_GET_MODE },
2183 { "teletext_format", mp_property_teletext_common, CONF_TYPE_INT,
2184 M_OPT_RANGE, 0, 3, (void*)TV_VBI_CONTROL_GET_FORMAT },
2185 { "teletext_half_page", mp_property_teletext_common, CONF_TYPE_INT,
2186 M_OPT_RANGE, 0, 2, (void*)TV_VBI_CONTROL_GET_HALF_PAGE },
2187 { NULL, NULL, NULL, 0, 0, 0, NULL }
2191 int mp_property_do(const char *name, int action, void *val, void *ctx)
2193 return m_property_do(mp_properties, name, action, val, ctx);
2196 char* mp_property_print(const char *name, void* ctx)
2198 char* ret = NULL;
2199 if(mp_property_do(name,M_PROPERTY_PRINT,&ret,ctx) <= 0)
2200 return NULL;
2201 return ret;
2204 char *property_expand_string(MPContext *mpctx, char *str)
2206 return m_properties_expand_string(mp_properties, str, mpctx);
2209 void property_print_help(void)
2211 m_properties_print_help_list(mp_properties);
2215 /* List of default ways to show a property on OSD.
2217 * Setting osd_progbar to -1 displays seek bar, other nonzero displays
2218 * a bar showing the current position between min/max values of the
2219 * property. In this case osd_msg is only used for terminal output
2220 * if there is no video; it'll be a label shown together with percentage.
2222 * Otherwise setting osd_msg will show the string on OSD, formatted with
2223 * the text value of the property as argument.
2225 static struct property_osd_display {
2226 /// property name
2227 const char *name;
2228 /// progressbar type
2229 int osd_progbar; // -1 is special value for seek indicators
2230 /// osd msg id if it must be shared
2231 int osd_id;
2232 /// osd msg template
2233 const char *osd_msg;
2234 } property_osd_display[] = {
2235 // general
2236 { "loop", 0, -1, _("Loop: %s") },
2237 { "chapter", -1, -1, NULL },
2238 // audio
2239 { "volume", OSD_VOLUME, -1, _("Volume") },
2240 { "mute", 0, -1, _("Mute: %s") },
2241 { "audio_delay", 0, -1, _("A-V delay: %s") },
2242 { "switch_audio", 0, -1, _("Audio: %s") },
2243 { "balance", OSD_BALANCE, -1, _("Balance") },
2244 // video
2245 { "panscan", OSD_PANSCAN, -1, _("Panscan") },
2246 { "ontop", 0, -1, _("Stay on top: %s") },
2247 { "rootwin", 0, -1, _("Rootwin: %s") },
2248 { "border", 0, -1, _("Border: %s") },
2249 { "framedropping", 0, -1, _("Framedropping: %s") },
2250 { "deinterlace", 0, -1, _("Deinterlace: %s") },
2251 { "yuv_colorspace", 0, -1, _("YUV colorspace: %s") },
2252 { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") },
2253 { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2254 { "contrast", OSD_CONTRAST, -1, _("Contrast") },
2255 { "saturation", OSD_SATURATION, -1, _("Saturation") },
2256 { "hue", OSD_HUE, -1, _("Hue") },
2257 { "vsync", 0, -1, _("VSync: %s") },
2258 // subs
2259 { "sub", 0, -1, _("Subtitles: %s") },
2260 { "sub_source", 0, -1, _("Sub source: %s") },
2261 { "sub_vob", 0, -1, _("Subtitles: %s") },
2262 { "sub_demux", 0, -1, _("Subtitles: %s") },
2263 { "sub_file", 0, -1, _("Subtitles: %s") },
2264 { "sub_pos", 0, -1, _("Sub position: %s/100") },
2265 { "sub_alignment", 0, -1, _("Sub alignment: %s") },
2266 { "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
2267 { "sub_visibility", 0, -1, _("Subtitles: %s") },
2268 { "sub_forced_only", 0, -1, _("Forced sub only: %s") },
2269 #ifdef CONFIG_FREETYPE
2270 { "sub_scale", 0, -1, _("Sub Scale: %s")},
2271 #endif
2272 #ifdef CONFIG_TV
2273 { "tv_brightness", OSD_BRIGHTNESS, -1, _("Brightness") },
2274 { "tv_hue", OSD_HUE, -1, _("Hue") },
2275 { "tv_saturation", OSD_SATURATION, -1, _("Saturation") },
2276 { "tv_contrast", OSD_CONTRAST, -1, _("Contrast") },
2277 #endif
2281 static int show_property_osd(MPContext *mpctx, const char *pname)
2283 struct MPOpts *opts = &mpctx->opts;
2284 int r;
2285 m_option_t* prop;
2286 struct property_osd_display *p;
2288 // look for the command
2289 for (p = property_osd_display; p->name; p++)
2290 if (!strcmp(p->name, pname))
2291 break;
2292 if (!p->name)
2293 return -1;
2295 if (mp_property_do(pname, M_PROPERTY_GET_TYPE, &prop, mpctx) <= 0 || !prop)
2296 return -1;
2298 if (p->osd_progbar == -1)
2299 mpctx->add_osd_seek_info = true;
2300 else if (p->osd_progbar) {
2301 if (prop->type == CONF_TYPE_INT) {
2302 if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0)
2303 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2304 prop->min, prop->max, r);
2305 } else if (prop->type == CONF_TYPE_FLOAT) {
2306 float f;
2307 if (mp_property_do(pname, M_PROPERTY_GET, &f, mpctx) > 0)
2308 set_osd_bar(mpctx, p->osd_progbar, mp_gtext(p->osd_msg),
2309 prop->min, prop->max, f);
2310 } else {
2311 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2312 "Property use an unsupported type.\n");
2313 return -1;
2315 return 0;
2318 if (p->osd_msg) {
2319 char *val = mp_property_print(pname, mpctx);
2320 if (val) {
2321 int index = p - property_osd_display;
2322 set_osd_tmsg(p->osd_id >= 0 ? p->osd_id : OSD_MSG_PROPERTY + index,
2323 1, opts->osd_duration, p->osd_msg, val);
2324 free(val);
2327 return 0;
2332 * \defgroup Command2Property Command to property bridge
2334 * It is used to handle most commands that just set a property
2335 * and optionally display something on the OSD.
2336 * Two kinds of commands are handled: adjust or toggle.
2338 * Adjust commands take 1 or 2 parameters: <value> <abs>
2339 * If <abs> is non-zero the property is set to the given value
2340 * otherwise it is adjusted.
2342 * Toggle commands take 0 or 1 parameters. With no parameter
2343 * or a value less than the property minimum it just steps the
2344 * property to its next value. Otherwise it sets it to the given
2345 * value.
2350 /// List of the commands that can be handled by setting a property.
2351 static struct {
2352 /// property name
2353 const char *name;
2354 /// cmd id
2355 int cmd;
2356 /// set/adjust or toggle command
2357 int toggle;
2358 } set_prop_cmd[] = {
2359 // general
2360 { "loop", MP_CMD_LOOP, 0},
2361 { "chapter", MP_CMD_SEEK_CHAPTER, 0},
2362 { "angle", MP_CMD_SWITCH_ANGLE, 0},
2363 { "pause", MP_CMD_PAUSE, 0},
2364 // audio
2365 { "volume", MP_CMD_VOLUME, 0},
2366 { "mute", MP_CMD_MUTE, 1},
2367 { "audio_delay", MP_CMD_AUDIO_DELAY, 0},
2368 { "switch_audio", MP_CMD_SWITCH_AUDIO, 1},
2369 { "balance", MP_CMD_BALANCE, 0},
2370 // video
2371 { "fullscreen", MP_CMD_VO_FULLSCREEN, 1},
2372 { "panscan", MP_CMD_PANSCAN, 0},
2373 { "ontop", MP_CMD_VO_ONTOP, 1},
2374 { "rootwin", MP_CMD_VO_ROOTWIN, 1},
2375 { "border", MP_CMD_VO_BORDER, 1},
2376 { "framedropping", MP_CMD_FRAMEDROPPING, 1},
2377 { "gamma", MP_CMD_GAMMA, 0},
2378 { "brightness", MP_CMD_BRIGHTNESS, 0},
2379 { "contrast", MP_CMD_CONTRAST, 0},
2380 { "saturation", MP_CMD_SATURATION, 0},
2381 { "hue", MP_CMD_HUE, 0},
2382 { "vsync", MP_CMD_SWITCH_VSYNC, 1},
2383 // subs
2384 { "sub", MP_CMD_SUB_SELECT, 1},
2385 { "sub_source", MP_CMD_SUB_SOURCE, 1},
2386 { "sub_vob", MP_CMD_SUB_VOB, 1},
2387 { "sub_demux", MP_CMD_SUB_DEMUX, 1},
2388 { "sub_file", MP_CMD_SUB_FILE, 1},
2389 { "sub_pos", MP_CMD_SUB_POS, 0},
2390 { "sub_alignment", MP_CMD_SUB_ALIGNMENT, 1},
2391 { "sub_delay", MP_CMD_SUB_DELAY, 0},
2392 { "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
2393 { "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
2394 #ifdef CONFIG_FREETYPE
2395 { "sub_scale", MP_CMD_SUB_SCALE, 0},
2396 #endif
2397 #ifdef CONFIG_ASS
2398 { "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
2399 #endif
2400 #ifdef CONFIG_TV
2401 { "tv_brightness", MP_CMD_TV_SET_BRIGHTNESS, 0},
2402 { "tv_hue", MP_CMD_TV_SET_HUE, 0},
2403 { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0},
2404 { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0},
2405 #endif
2409 /// Handle commands that set a property.
2410 static int set_property_command(MPContext *mpctx, mp_cmd_t *cmd)
2412 int i, r;
2413 m_option_t* prop;
2414 const char *pname;
2416 // look for the command
2417 for (i = 0; set_prop_cmd[i].name; i++)
2418 if (set_prop_cmd[i].cmd == cmd->id)
2419 break;
2420 if (!(pname = set_prop_cmd[i].name))
2421 return 0;
2423 if (mp_property_do(pname,M_PROPERTY_GET_TYPE,&prop,mpctx) <= 0 || !prop)
2424 return 0;
2426 // toggle command
2427 if (set_prop_cmd[i].toggle) {
2428 // set to value
2429 if (cmd->nargs > 0 && cmd->args[0].v.i >= prop->min)
2430 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v.i, mpctx);
2431 else
2432 r = mp_property_do(pname, M_PROPERTY_STEP_UP, NULL, mpctx);
2433 } else if (cmd->args[1].v.i) //set
2434 r = mp_property_do(pname, M_PROPERTY_SET, &cmd->args[0].v, mpctx);
2435 else // adjust
2436 r = mp_property_do(pname, M_PROPERTY_STEP_UP, &cmd->args[0].v, mpctx);
2438 if (r <= 0)
2439 return 1;
2441 show_property_osd(mpctx, pname);
2443 return 1;
2446 #ifdef CONFIG_DVDNAV
2447 static const struct {
2448 const char *name;
2449 const mp_command_type cmd;
2450 } mp_dvdnav_bindings[] = {
2451 { "up", MP_CMD_DVDNAV_UP },
2452 { "down", MP_CMD_DVDNAV_DOWN },
2453 { "left", MP_CMD_DVDNAV_LEFT },
2454 { "right", MP_CMD_DVDNAV_RIGHT },
2455 { "menu", MP_CMD_DVDNAV_MENU },
2456 { "select", MP_CMD_DVDNAV_SELECT },
2457 { "prev", MP_CMD_DVDNAV_PREVMENU },
2458 { "mouse", MP_CMD_DVDNAV_MOUSECLICK },
2461 * keep old dvdnav sub-command options for a while in order not to
2462 * break slave-mode API too suddenly.
2464 { "1", MP_CMD_DVDNAV_UP },
2465 { "2", MP_CMD_DVDNAV_DOWN },
2466 { "3", MP_CMD_DVDNAV_LEFT },
2467 { "4", MP_CMD_DVDNAV_RIGHT },
2468 { "5", MP_CMD_DVDNAV_MENU },
2469 { "6", MP_CMD_DVDNAV_SELECT },
2470 { "7", MP_CMD_DVDNAV_PREVMENU },
2471 { "8", MP_CMD_DVDNAV_MOUSECLICK },
2472 { NULL, 0 }
2474 #endif
2476 static const char *property_error_string(int error_value)
2478 switch (error_value) {
2479 case M_PROPERTY_ERROR:
2480 return "ERROR";
2481 case M_PROPERTY_UNAVAILABLE:
2482 return "PROPERTY_UNAVAILABLE";
2483 case M_PROPERTY_NOT_IMPLEMENTED:
2484 return "NOT_IMPLEMENTED";
2485 case M_PROPERTY_UNKNOWN:
2486 return "PROPERTY_UNKNOWN";
2487 case M_PROPERTY_DISABLED:
2488 return "DISABLED";
2490 return "UNKNOWN";
2493 static void remove_subtitle_range(MPContext *mpctx, int start, int count)
2495 int idx;
2496 int end = start + count;
2497 int after = mpctx->set_of_sub_size - end;
2498 sub_data **subs = mpctx->set_of_subtitles;
2499 #ifdef CONFIG_ASS
2500 struct ass_track **ass_tracks = mpctx->set_of_ass_tracks;
2501 #endif
2502 if (count < 0 || count > mpctx->set_of_sub_size ||
2503 start < 0 || start > mpctx->set_of_sub_size - count) {
2504 mp_msg(MSGT_CPLAYER, MSGL_ERR,
2505 "Cannot remove invalid subtitle range %i +%i\n", start, count);
2506 return;
2508 for (idx = start; idx < end; idx++) {
2509 sub_data *subd = subs[idx];
2510 mp_msg(MSGT_CPLAYER, MSGL_STATUS,
2511 "SUB: Removed subtitle file (%d): %s\n", idx + 1,
2512 filename_recode(subd->filename));
2513 sub_free(subd);
2514 subs[idx] = NULL;
2515 #ifdef CONFIG_ASS
2516 if (ass_tracks[idx])
2517 ass_free_track(ass_tracks[idx]);
2518 ass_tracks[idx] = NULL;
2519 #endif
2522 mpctx->global_sub_size -= count;
2523 mpctx->set_of_sub_size -= count;
2524 if (mpctx->set_of_sub_size <= 0)
2525 mpctx->global_sub_indices[SUB_SOURCE_SUBS] = -1;
2527 memmove(subs + start, subs + end, after * sizeof(*subs));
2528 memset(subs + start + after, 0, count * sizeof(*subs));
2529 #ifdef CONFIG_ASS
2530 memmove(ass_tracks + start, ass_tracks + end, after * sizeof(*ass_tracks));
2531 memset(ass_tracks + start + after, 0, count * sizeof(*ass_tracks));
2532 #endif
2534 if (mpctx->set_of_sub_pos >= start && mpctx->set_of_sub_pos < end) {
2535 mpctx->global_sub_pos = -2;
2536 subdata = NULL;
2537 #ifdef CONFIG_ASS
2538 ass_track = NULL;
2539 #endif
2540 mp_input_queue_cmd(mpctx->input, mp_input_parse_cmd("sub_select"));
2541 } else if (mpctx->set_of_sub_pos >= end) {
2542 mpctx->set_of_sub_pos -= count;
2543 mpctx->global_sub_pos -= count;
2547 void run_command(MPContext *mpctx, mp_cmd_t *cmd)
2549 struct MPOpts *opts = &mpctx->opts;
2550 sh_audio_t * const sh_audio = mpctx->sh_audio;
2551 sh_video_t * const sh_video = mpctx->sh_video;
2552 int osd_duration = opts->osd_duration;
2553 int case_fallthrough_hack = 0;
2554 if (!set_property_command(mpctx, cmd))
2555 switch (cmd->id) {
2556 case MP_CMD_SEEK:{
2557 float v;
2558 int abs;
2559 mpctx->add_osd_seek_info = true;
2560 v = cmd->args[0].v.f;
2561 abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0;
2562 if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */
2563 mpctx->abs_seek_pos = SEEK_ABSOLUTE;
2564 if (sh_video)
2565 mpctx->osd_function =
2566 (v > sh_video->pts) ? OSD_FFW : OSD_REW;
2567 mpctx->rel_seek_secs = v;
2568 } else if (abs) { /* Absolute seek by percentage */
2569 mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR;
2570 if (sh_video)
2571 mpctx->osd_function = OSD_FFW; // Direction isn't set correctly
2572 mpctx->rel_seek_secs = v / 100.0;
2573 } else {
2574 mpctx->rel_seek_secs += v;
2575 mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW;
2578 break;
2580 case MP_CMD_SET_PROPERTY_OSD:
2581 case_fallthrough_hack = 1;
2583 case MP_CMD_SET_PROPERTY:{
2584 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_PARSE,
2585 cmd->args[1].v.s, mpctx);
2586 if (r == M_PROPERTY_UNKNOWN)
2587 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2588 "Unknown property: '%s'\n", cmd->args[0].v.s);
2589 else if (r <= 0)
2590 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2591 "Failed to set property '%s' to '%s'.\n",
2592 cmd->args[0].v.s, cmd->args[1].v.s);
2593 else if (case_fallthrough_hack)
2594 show_property_osd(mpctx, cmd->args[0].v.s);
2595 if (r <= 0)
2596 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2598 break;
2600 case MP_CMD_STEP_PROPERTY_OSD:
2601 case_fallthrough_hack = 1;
2603 case MP_CMD_STEP_PROPERTY:{
2604 void* arg = NULL;
2605 int r,i;
2606 double d;
2607 off_t o;
2608 if (cmd->args[1].v.f) {
2609 m_option_t* prop;
2610 if((r = mp_property_do(cmd->args[0].v.s,
2611 M_PROPERTY_GET_TYPE,
2612 &prop, mpctx)) <= 0)
2613 goto step_prop_err;
2614 if(prop->type == CONF_TYPE_INT ||
2615 prop->type == CONF_TYPE_FLAG)
2616 i = cmd->args[1].v.f, arg = &i;
2617 else if(prop->type == CONF_TYPE_FLOAT)
2618 arg = &cmd->args[1].v.f;
2619 else if(prop->type == CONF_TYPE_DOUBLE ||
2620 prop->type == CONF_TYPE_TIME)
2621 d = cmd->args[1].v.f, arg = &d;
2622 else if(prop->type == CONF_TYPE_POSITION)
2623 o = cmd->args[1].v.f, arg = &o;
2624 else
2625 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2626 "Ignoring step size stepping property '%s'.\n",
2627 cmd->args[0].v.s);
2629 r = mp_property_do(cmd->args[0].v.s,
2630 cmd->args[2].v.i < 0 ?
2631 M_PROPERTY_STEP_DOWN : M_PROPERTY_STEP_UP,
2632 arg, mpctx);
2633 step_prop_err:
2634 if (r == M_PROPERTY_UNKNOWN)
2635 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2636 "Unknown property: '%s'\n", cmd->args[0].v.s);
2637 else if (r <= 0)
2638 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2639 "Failed to increment property '%s' by %f.\n",
2640 cmd->args[0].v.s, cmd->args[1].v.f);
2641 else if (case_fallthrough_hack)
2642 show_property_osd(mpctx, cmd->args[0].v.s);
2643 if (r <= 0)
2644 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2646 break;
2648 case MP_CMD_GET_PROPERTY:{
2649 char *tmp;
2650 int r = mp_property_do(cmd->args[0].v.s, M_PROPERTY_TO_STRING,
2651 &tmp, mpctx);
2652 if (r <= 0) {
2653 mp_msg(MSGT_CPLAYER, MSGL_WARN,
2654 "Failed to get value of property '%s'.\n",
2655 cmd->args[0].v.s);
2656 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_ERROR=%s\n", property_error_string(r));
2657 break;
2659 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_%s=%s\n",
2660 cmd->args[0].v.s, tmp);
2661 free(tmp);
2663 break;
2665 case MP_CMD_EDL_MARK:
2666 if (edl_fd) {
2667 float v = sh_video ? sh_video->pts :
2668 playing_audio_pts(mpctx);
2669 if (mpctx->begin_skip == MP_NOPTS_VALUE) {
2670 mpctx->begin_skip = v;
2671 mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "EDL skip start, press 'i' again to end block.\n");
2672 } else {
2673 if (mpctx->begin_skip > v)
2674 mp_tmsg(MSGT_CPLAYER, MSGL_WARN, "EDL skip canceled, last start > stop\n");
2675 else {
2676 fprintf(edl_fd, "%f %f %d\n", mpctx->begin_skip, v, 0);
2677 mp_tmsg(MSGT_CPLAYER, MSGL_INFO, "EDL skip end, line written.\n");
2679 mpctx->begin_skip = MP_NOPTS_VALUE;
2682 break;
2684 case MP_CMD_SWITCH_RATIO:
2685 if (!sh_video)
2686 break;
2687 if (cmd->nargs == 0 || cmd->args[0].v.f == -1)
2688 opts->movie_aspect = (float) sh_video->disp_w / sh_video->disp_h;
2689 else
2690 opts->movie_aspect = cmd->args[0].v.f;
2691 mpcodecs_config_vo(sh_video, sh_video->disp_w, sh_video->disp_h, 0);
2692 break;
2694 case MP_CMD_SPEED_INCR:{
2695 float v = cmd->args[0].v.f;
2696 opts->playback_speed += v;
2697 build_afilter_chain(mpctx, sh_audio, &ao_data);
2698 set_osd_tmsg(OSD_MSG_SPEED, 1, osd_duration, "Speed: x %6.2f",
2699 opts->playback_speed);
2700 } break;
2702 case MP_CMD_SPEED_MULT:{
2703 float v = cmd->args[0].v.f;
2704 opts->playback_speed *= v;
2705 build_afilter_chain(mpctx, sh_audio, &ao_data);
2706 set_osd_tmsg(OSD_MSG_SPEED, 1, osd_duration, "Speed: x %6.2f",
2707 opts->playback_speed);
2708 } break;
2710 case MP_CMD_SPEED_SET:{
2711 float v = cmd->args[0].v.f;
2712 opts->playback_speed = v;
2713 build_afilter_chain(mpctx, sh_audio, &ao_data);
2714 set_osd_tmsg(OSD_MSG_SPEED, 1, osd_duration, "Speed: x %6.2f",
2715 opts->playback_speed);
2716 } break;
2718 case MP_CMD_FRAME_STEP:
2719 add_step_frame(mpctx);
2720 break;
2722 case MP_CMD_FILE_FILTER:
2723 file_filter = cmd->args[0].v.i;
2724 break;
2726 case MP_CMD_QUIT:
2727 exit_player_with_rc(mpctx, EXIT_QUIT,
2728 (cmd->nargs > 0) ? cmd->args[0].v.i : 0);
2730 case MP_CMD_PLAY_TREE_STEP:{
2731 int n = cmd->args[0].v.i == 0 ? 1 : cmd->args[0].v.i;
2732 int force = cmd->args[1].v.i;
2735 if (!force && mpctx->playtree_iter) {
2736 play_tree_iter_t *i =
2737 play_tree_iter_new_copy(mpctx->playtree_iter);
2738 if (play_tree_iter_step(i, n, 0) ==
2739 PLAY_TREE_ITER_ENTRY)
2740 mpctx->stop_play =
2741 (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2742 play_tree_iter_free(i);
2743 } else
2744 mpctx->stop_play = (n > 0) ? PT_NEXT_ENTRY : PT_PREV_ENTRY;
2745 if (mpctx->stop_play)
2746 mpctx->play_tree_step = n;
2749 break;
2751 case MP_CMD_PLAY_TREE_UP_STEP:{
2752 int n = cmd->args[0].v.i > 0 ? 1 : -1;
2753 int force = cmd->args[1].v.i;
2755 if (!force && mpctx->playtree_iter) {
2756 play_tree_iter_t *i =
2757 play_tree_iter_new_copy(mpctx->playtree_iter);
2758 if (play_tree_iter_up_step(i, n, 0) == PLAY_TREE_ITER_ENTRY)
2759 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2760 play_tree_iter_free(i);
2761 } else
2762 mpctx->stop_play = (n > 0) ? PT_UP_NEXT : PT_UP_PREV;
2764 break;
2766 case MP_CMD_PLAY_ALT_SRC_STEP:
2767 if (mpctx->playtree_iter && mpctx->playtree_iter->num_files > 1) {
2768 int v = cmd->args[0].v.i;
2769 if (v > 0
2770 && mpctx->playtree_iter->file <
2771 mpctx->playtree_iter->num_files)
2772 mpctx->stop_play = PT_NEXT_SRC;
2773 else if (v < 0 && mpctx->playtree_iter->file > 1)
2774 mpctx->stop_play = PT_PREV_SRC;
2776 break;
2778 case MP_CMD_SUB_STEP:
2779 if (sh_video) {
2780 int movement = cmd->args[0].v.i;
2781 step_sub(subdata, sh_video->pts, movement);
2782 #ifdef CONFIG_ASS
2783 if (ass_track)
2784 sub_delay +=
2785 ass_step_sub(ass_track,
2786 (sh_video->pts +
2787 sub_delay) * 1000 + .5, movement) / 1000.;
2788 #endif
2789 set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration,
2790 "Sub delay: %d ms", ROUND(sub_delay * 1000));
2792 break;
2794 case MP_CMD_SUB_LOG:
2795 log_sub(mpctx);
2796 break;
2798 case MP_CMD_OSD:{
2799 int v = cmd->args[0].v.i;
2800 int max = (term_osd
2801 && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL;
2802 if (opts->osd_level > max)
2803 opts->osd_level = max;
2804 if (v < 0)
2805 opts->osd_level = (opts->osd_level + 1) % (max + 1);
2806 else
2807 opts->osd_level = v > max ? max : v;
2808 /* Show OSD state when disabled, but not when an explicit
2809 argument is given to the OSD command, i.e. in slave mode. */
2810 if (v == -1 && opts->osd_level <= 1)
2811 set_osd_tmsg(OSD_MSG_OSD_STATUS, 0, osd_duration,
2812 "OSD: %s",
2813 opts->osd_level ? mp_gtext("enabled") :
2814 mp_gtext("disabled"));
2815 else
2816 rm_osd_msg(OSD_MSG_OSD_STATUS);
2818 break;
2820 case MP_CMD_OSD_SHOW_TEXT:
2821 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2822 (cmd->args[1].v.i <
2823 0 ? osd_duration : cmd->args[1].v.i),
2824 "%-.63s", cmd->args[0].v.s);
2825 break;
2827 case MP_CMD_OSD_SHOW_PROPERTY_TEXT:{
2828 char *txt = m_properties_expand_string(mp_properties,
2829 cmd->args[0].v.s,
2830 mpctx);
2831 /* if no argument supplied take default osd_duration, else <arg> ms. */
2832 if (txt) {
2833 set_osd_msg(OSD_MSG_TEXT, cmd->args[2].v.i,
2834 (cmd->args[1].v.i <
2835 0 ? osd_duration : cmd->args[1].v.i),
2836 "%-.63s", txt);
2837 free(txt);
2840 break;
2842 case MP_CMD_LOADFILE:{
2843 play_tree_t *e = play_tree_new();
2844 play_tree_add_file(e, cmd->args[0].v.s);
2846 if (cmd->args[1].v.i) // append
2847 play_tree_append_entry(mpctx->playtree->child, e);
2848 else {
2849 // Go back to the starting point.
2850 while (play_tree_iter_up_step
2851 (mpctx->playtree_iter, 0, 1) != PLAY_TREE_ITER_END)
2852 /* NOP */ ;
2853 play_tree_free_list(mpctx->playtree->child, 1);
2854 play_tree_set_child(mpctx->playtree, e);
2855 pt_iter_goto_head(mpctx->playtree_iter);
2856 mpctx->stop_play = PT_NEXT_SRC;
2859 break;
2861 case MP_CMD_LOADLIST:{
2862 play_tree_t *e = parse_playlist_file(mpctx->mconfig, cmd->args[0].v.s);
2863 if (!e)
2864 mp_tmsg(MSGT_CPLAYER, MSGL_ERR,
2865 "\nUnable to load playlist %s.\n", cmd->args[0].v.s);
2866 else {
2867 if (cmd->args[1].v.i) // append
2868 play_tree_append_entry(mpctx->playtree->child, e);
2869 else {
2870 // Go back to the starting point.
2871 while (play_tree_iter_up_step
2872 (mpctx->playtree_iter, 0, 1)
2873 != PLAY_TREE_ITER_END)
2874 /* NOP */ ;
2875 play_tree_free_list(mpctx->playtree->child, 1);
2876 play_tree_set_child(mpctx->playtree, e);
2877 pt_iter_goto_head(mpctx->playtree_iter);
2878 mpctx->stop_play = PT_NEXT_SRC;
2882 break;
2884 case MP_CMD_STOP:
2885 // Go back to the starting point.
2886 while (play_tree_iter_up_step
2887 (mpctx->playtree_iter, 0, 1) != PLAY_TREE_ITER_END)
2888 /* NOP */ ;
2889 mpctx->stop_play = PT_STOP;
2890 break;
2892 #ifdef CONFIG_RADIO
2893 case MP_CMD_RADIO_STEP_CHANNEL:
2894 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
2895 int v = cmd->args[0].v.i;
2896 if (v > 0)
2897 radio_step_channel(mpctx->demuxer->stream,
2898 RADIO_CHANNEL_HIGHER);
2899 else
2900 radio_step_channel(mpctx->demuxer->stream,
2901 RADIO_CHANNEL_LOWER);
2902 if (radio_get_channel_name(mpctx->demuxer->stream)) {
2903 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
2904 "Channel: %s",
2905 radio_get_channel_name(mpctx->demuxer->stream));
2908 break;
2910 case MP_CMD_RADIO_SET_CHANNEL:
2911 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO) {
2912 radio_set_channel(mpctx->demuxer->stream, cmd->args[0].v.s);
2913 if (radio_get_channel_name(mpctx->demuxer->stream)) {
2914 set_osd_tmsg(OSD_MSG_RADIO_CHANNEL, 1, osd_duration,
2915 "Channel: %s",
2916 radio_get_channel_name(mpctx->demuxer->stream));
2919 break;
2921 case MP_CMD_RADIO_SET_FREQ:
2922 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
2923 radio_set_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
2924 break;
2926 case MP_CMD_RADIO_STEP_FREQ:
2927 if (mpctx->demuxer->stream->type == STREAMTYPE_RADIO)
2928 radio_step_freq(mpctx->demuxer->stream, cmd->args[0].v.f);
2929 break;
2930 #endif
2932 #ifdef CONFIG_TV
2933 case MP_CMD_TV_START_SCAN:
2934 if (mpctx->file_format == DEMUXER_TYPE_TV)
2935 tv_start_scan((tvi_handle_t *) (mpctx->demuxer->priv),1);
2936 break;
2937 case MP_CMD_TV_SET_FREQ:
2938 if (mpctx->file_format == DEMUXER_TYPE_TV)
2939 tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
2940 cmd->args[0].v.f * 16.0);
2941 #ifdef CONFIG_PVR
2942 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
2943 pvr_set_freq (mpctx->stream, ROUND (cmd->args[0].v.f));
2944 set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
2945 pvr_get_current_channelname (mpctx->stream),
2946 pvr_get_current_stationname (mpctx->stream));
2948 #endif /* CONFIG_PVR */
2949 break;
2951 case MP_CMD_TV_STEP_FREQ:
2952 if (mpctx->file_format == DEMUXER_TYPE_TV)
2953 tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
2954 cmd->args[0].v.f * 16.0);
2955 #ifdef CONFIG_PVR
2956 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
2957 pvr_force_freq_step (mpctx->stream, ROUND (cmd->args[0].v.f));
2958 set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
2959 pvr_get_current_channelname (mpctx->stream),
2960 pvr_get_current_frequency (mpctx->stream));
2962 #endif /* CONFIG_PVR */
2963 break;
2965 case MP_CMD_TV_SET_NORM:
2966 if (mpctx->file_format == DEMUXER_TYPE_TV)
2967 tv_set_norm((tvi_handle_t *) (mpctx->demuxer->priv),
2968 cmd->args[0].v.s);
2969 break;
2971 case MP_CMD_TV_STEP_CHANNEL:{
2972 if (mpctx->file_format == DEMUXER_TYPE_TV) {
2973 int v = cmd->args[0].v.i;
2974 if (v > 0) {
2975 tv_step_channel((tvi_handle_t *) (mpctx->
2976 demuxer->priv),
2977 TV_CHANNEL_HIGHER);
2978 } else {
2979 tv_step_channel((tvi_handle_t *) (mpctx->
2980 demuxer->priv),
2981 TV_CHANNEL_LOWER);
2983 if (tv_channel_list) {
2984 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
2985 "Channel: %s", tv_channel_current->name);
2986 //vo_osd_changed(OSDTYPE_SUBTITLE);
2989 #ifdef CONFIG_PVR
2990 else if (mpctx->stream &&
2991 mpctx->stream->type == STREAMTYPE_PVR) {
2992 pvr_set_channel_step (mpctx->stream, cmd->args[0].v.i);
2993 set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
2994 pvr_get_current_channelname (mpctx->stream),
2995 pvr_get_current_stationname (mpctx->stream));
2997 #endif /* CONFIG_PVR */
2999 #ifdef CONFIG_DVBIN
3000 if (mpctx->stream->type == STREAMTYPE_DVB) {
3001 int dir;
3002 int v = cmd->args[0].v.i;
3004 mpctx->last_dvb_step = v;
3005 if (v > 0)
3006 dir = DVB_CHANNEL_HIGHER;
3007 else
3008 dir = DVB_CHANNEL_LOWER;
3011 if (dvb_step_channel(mpctx->stream, dir)) {
3012 mpctx->stop_play = PT_NEXT_ENTRY;
3013 mpctx->dvbin_reopen = 1;
3016 #endif /* CONFIG_DVBIN */
3017 break;
3019 case MP_CMD_TV_SET_CHANNEL:
3020 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3021 tv_set_channel((tvi_handle_t *) (mpctx->demuxer->priv),
3022 cmd->args[0].v.s);
3023 if (tv_channel_list) {
3024 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3025 "Channel: %s", tv_channel_current->name);
3026 //vo_osd_changed(OSDTYPE_SUBTITLE);
3029 #ifdef CONFIG_PVR
3030 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3031 pvr_set_channel (mpctx->stream, cmd->args[0].v.s);
3032 set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3033 pvr_get_current_channelname (mpctx->stream),
3034 pvr_get_current_stationname (mpctx->stream));
3036 #endif /* CONFIG_PVR */
3037 break;
3039 #ifdef CONFIG_DVBIN
3040 case MP_CMD_DVB_SET_CHANNEL:
3041 if (mpctx->stream->type == STREAMTYPE_DVB) {
3042 mpctx->last_dvb_step = 1;
3044 if (dvb_set_channel(mpctx->stream, cmd->args[1].v.i,
3045 cmd->args[0].v.i)) {
3046 mpctx->stop_play = PT_NEXT_ENTRY;
3047 mpctx->dvbin_reopen = 1;
3050 break;
3051 #endif /* CONFIG_DVBIN */
3053 case MP_CMD_TV_LAST_CHANNEL:
3054 if (mpctx->file_format == DEMUXER_TYPE_TV) {
3055 tv_last_channel((tvi_handle_t *) (mpctx->demuxer->priv));
3056 if (tv_channel_list) {
3057 set_osd_tmsg(OSD_MSG_TV_CHANNEL, 1, osd_duration,
3058 "Channel: %s", tv_channel_current->name);
3059 //vo_osd_changed(OSDTYPE_SUBTITLE);
3062 #ifdef CONFIG_PVR
3063 else if (mpctx->stream && mpctx->stream->type == STREAMTYPE_PVR) {
3064 pvr_set_lastchannel (mpctx->stream);
3065 set_osd_msg (OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
3066 pvr_get_current_channelname (mpctx->stream),
3067 pvr_get_current_stationname (mpctx->stream));
3069 #endif /* CONFIG_PVR */
3070 break;
3072 case MP_CMD_TV_STEP_NORM:
3073 if (mpctx->file_format == DEMUXER_TYPE_TV)
3074 tv_step_norm((tvi_handle_t *) (mpctx->demuxer->priv));
3075 break;
3077 case MP_CMD_TV_STEP_CHANNEL_LIST:
3078 if (mpctx->file_format == DEMUXER_TYPE_TV)
3079 tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
3080 break;
3081 #endif /* CONFIG_TV */
3082 case MP_CMD_TV_TELETEXT_ADD_DEC:
3084 if (mpctx->demuxer->teletext)
3085 teletext_control(mpctx->demuxer->teletext,TV_VBI_CONTROL_ADD_DEC,
3086 &(cmd->args[0].v.s));
3087 break;
3089 case MP_CMD_TV_TELETEXT_GO_LINK:
3091 if (mpctx->demuxer->teletext)
3092 teletext_control(mpctx->demuxer->teletext,TV_VBI_CONTROL_GO_LINK,
3093 &(cmd->args[0].v.i));
3094 break;
3097 case MP_CMD_SUB_LOAD:
3098 if (sh_video) {
3099 int n = mpctx->set_of_sub_size;
3100 add_subtitles(mpctx, cmd->args[0].v.s, sh_video->fps, 0);
3101 if (n != mpctx->set_of_sub_size) {
3102 if (mpctx->global_sub_indices[SUB_SOURCE_SUBS] < 0)
3103 mpctx->global_sub_indices[SUB_SOURCE_SUBS] =
3104 mpctx->global_sub_size;
3105 ++mpctx->global_sub_size;
3108 break;
3110 case MP_CMD_SUB_REMOVE:
3111 if (sh_video) {
3112 int v = cmd->args[0].v.i;
3113 if (v < 0) {
3114 remove_subtitle_range(mpctx, 0, mpctx->set_of_sub_size);
3115 } else if (v < mpctx->set_of_sub_size) {
3116 remove_subtitle_range(mpctx, v, 1);
3119 break;
3121 case MP_CMD_GET_SUB_VISIBILITY:
3122 if (sh_video) {
3123 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3124 "ANS_SUB_VISIBILITY=%d\n", sub_visibility);
3126 break;
3128 case MP_CMD_SCREENSHOT:
3129 if (mpctx->video_out && mpctx->video_out->config_ok) {
3130 mp_msg(MSGT_CPLAYER, MSGL_INFO, "sending VFCTRL_SCREENSHOT!\n");
3131 if (CONTROL_OK !=
3132 ((vf_instance_t *) sh_video->vfilter)->
3133 control(sh_video->vfilter, VFCTRL_SCREENSHOT,
3134 &cmd->args[0].v.i))
3135 mp_msg(MSGT_CPLAYER, MSGL_INFO, "failed (forgot -vf screenshot?)\n");
3137 break;
3139 case MP_CMD_AF_EQ_SET:{
3140 af_instance_t* m1=af_get(sh_audio->afilter, "equalizer");
3141 if (m1) m1->control( m1, AF_CONTROL_COMMAND_LINE, cmd->args[0].v.s);
3142 else mp_msg(MSGT_CPLAYER, MSGL_INFO, "failed (forgot -af equalizer=0:0 ?)\n");
3144 break;
3146 case MP_CMD_VF_CHANGE_RECTANGLE:
3147 if (!sh_video)
3148 break;
3149 set_rectangle(sh_video, cmd->args[0].v.i, cmd->args[1].v.i);
3150 break;
3152 case MP_CMD_GET_TIME_LENGTH:{
3153 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_LENGTH=%.2f\n",
3154 demuxer_get_time_length(mpctx->demuxer));
3156 break;
3158 case MP_CMD_GET_FILENAME:{
3159 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_FILENAME='%s'\n",
3160 get_metadata(mpctx, META_NAME));
3162 break;
3164 case MP_CMD_GET_VIDEO_CODEC:{
3165 char *inf = get_metadata(mpctx, META_VIDEO_CODEC);
3166 if (!inf)
3167 inf = strdup("");
3168 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_CODEC='%s'\n", inf);
3169 free(inf);
3171 break;
3173 case MP_CMD_GET_VIDEO_BITRATE:{
3174 char *inf = get_metadata(mpctx, META_VIDEO_BITRATE);
3175 if (!inf)
3176 inf = strdup("");
3177 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VIDEO_BITRATE='%s'\n", inf);
3178 free(inf);
3180 break;
3182 case MP_CMD_GET_VIDEO_RESOLUTION:{
3183 char *inf = get_metadata(mpctx, META_VIDEO_RESOLUTION);
3184 if (!inf)
3185 inf = strdup("");
3186 mp_msg(MSGT_GLOBAL, MSGL_INFO,
3187 "ANS_VIDEO_RESOLUTION='%s'\n", inf);
3188 free(inf);
3190 break;
3192 case MP_CMD_GET_AUDIO_CODEC:{
3193 char *inf = get_metadata(mpctx, META_AUDIO_CODEC);
3194 if (!inf)
3195 inf = strdup("");
3196 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_CODEC='%s'\n", inf);
3197 free(inf);
3199 break;
3201 case MP_CMD_GET_AUDIO_BITRATE:{
3202 char *inf = get_metadata(mpctx, META_AUDIO_BITRATE);
3203 if (!inf)
3204 inf = strdup("");
3205 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_BITRATE='%s'\n", inf);
3206 free(inf);
3208 break;
3210 case MP_CMD_GET_AUDIO_SAMPLES:{
3211 char *inf = get_metadata(mpctx, META_AUDIO_SAMPLES);
3212 if (!inf)
3213 inf = strdup("");
3214 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_AUDIO_SAMPLES='%s'\n", inf);
3215 free(inf);
3217 break;
3219 case MP_CMD_GET_META_TITLE:{
3220 char *inf = get_metadata(mpctx, META_INFO_TITLE);
3221 if (!inf)
3222 inf = strdup("");
3223 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TITLE='%s'\n", inf);
3224 free(inf);
3226 break;
3228 case MP_CMD_GET_META_ARTIST:{
3229 char *inf = get_metadata(mpctx, META_INFO_ARTIST);
3230 if (!inf)
3231 inf = strdup("");
3232 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ARTIST='%s'\n", inf);
3233 free(inf);
3235 break;
3237 case MP_CMD_GET_META_ALBUM:{
3238 char *inf = get_metadata(mpctx, META_INFO_ALBUM);
3239 if (!inf)
3240 inf = strdup("");
3241 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_ALBUM='%s'\n", inf);
3242 free(inf);
3244 break;
3246 case MP_CMD_GET_META_YEAR:{
3247 char *inf = get_metadata(mpctx, META_INFO_YEAR);
3248 if (!inf)
3249 inf = strdup("");
3250 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_YEAR='%s'\n", inf);
3251 free(inf);
3253 break;
3255 case MP_CMD_GET_META_COMMENT:{
3256 char *inf = get_metadata(mpctx, META_INFO_COMMENT);
3257 if (!inf)
3258 inf = strdup("");
3259 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_COMMENT='%s'\n", inf);
3260 free(inf);
3262 break;
3264 case MP_CMD_GET_META_TRACK:{
3265 char *inf = get_metadata(mpctx, META_INFO_TRACK);
3266 if (!inf)
3267 inf = strdup("");
3268 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_TRACK='%s'\n", inf);
3269 free(inf);
3271 break;
3273 case MP_CMD_GET_META_GENRE:{
3274 char *inf = get_metadata(mpctx, META_INFO_GENRE);
3275 if (!inf)
3276 inf = strdup("");
3277 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_META_GENRE='%s'\n", inf);
3278 free(inf);
3280 break;
3282 case MP_CMD_GET_VO_FULLSCREEN:
3283 if (mpctx->video_out && mpctx->video_out->config_ok)
3284 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_VO_FULLSCREEN=%d\n", vo_fs);
3285 break;
3287 case MP_CMD_GET_PERCENT_POS:
3288 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_PERCENT_POSITION=%d\n",
3289 demuxer_get_percent_pos(mpctx->demuxer));
3290 break;
3292 case MP_CMD_GET_TIME_POS:{
3293 float pos = 0;
3294 if (sh_video)
3295 pos = sh_video->pts;
3296 else if (sh_audio && mpctx->audio_out)
3297 pos = playing_audio_pts(mpctx);
3298 mp_msg(MSGT_GLOBAL, MSGL_INFO, "ANS_TIME_POSITION=%.1f\n", pos);
3300 break;
3302 case MP_CMD_RUN:
3303 #ifndef __MINGW32__
3304 if (!fork()) {
3305 execl("/bin/sh", "sh", "-c", cmd->args[0].v.s, NULL);
3306 exit(0);
3308 #endif
3309 break;
3311 case MP_CMD_KEYDOWN_EVENTS:
3312 mplayer_put_key(mpctx->key_fifo, cmd->args[0].v.i);
3313 break;
3315 case MP_CMD_SET_MOUSE_POS:{
3316 int pointer_x, pointer_y;
3317 double dx, dy;
3318 pointer_x = cmd->args[0].v.i;
3319 pointer_y = cmd->args[1].v.i;
3320 rescale_input_coordinates(mpctx, pointer_x, pointer_y, &dx, &dy);
3321 #ifdef CONFIG_DVDNAV
3322 if (mpctx->stream->type == STREAMTYPE_DVDNAV
3323 && dx > 0.0 && dy > 0.0) {
3324 int button = -1;
3325 pointer_x = (int) (dx * (double) sh_video->disp_w);
3326 pointer_y = (int) (dy * (double) sh_video->disp_h);
3327 mp_dvdnav_update_mouse_pos(mpctx->stream,
3328 pointer_x, pointer_y, &button);
3329 if (opts->osd_level > 1 && button > 0)
3330 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3331 "Selected button number %d", button);
3333 #endif
3334 #ifdef CONFIG_MENU
3335 if (use_menu && dx >= 0.0 && dy >= 0.0)
3336 menu_update_mouse_pos(dx, dy);
3337 #endif
3339 break;
3341 #ifdef CONFIG_DVDNAV
3342 case MP_CMD_DVDNAV:{
3343 int button = -1;
3344 int i;
3345 mp_command_type command = 0;
3346 if (mpctx->stream->type != STREAMTYPE_DVDNAV)
3347 break;
3349 for (i = 0; mp_dvdnav_bindings[i].name; i++)
3350 if (cmd->args[0].v.s &&
3351 !strcasecmp (cmd->args[0].v.s,
3352 mp_dvdnav_bindings[i].name))
3353 command = mp_dvdnav_bindings[i].cmd;
3355 mp_dvdnav_handle_input(mpctx->stream,command,&button);
3356 if (opts->osd_level > 1 && button > 0)
3357 set_osd_msg(OSD_MSG_TEXT, 1, osd_duration,
3358 "Selected button number %d", button);
3360 break;
3362 case MP_CMD_SWITCH_TITLE:
3363 if (mpctx->stream->type == STREAMTYPE_DVDNAV)
3364 mp_dvdnav_switch_title(mpctx->stream, cmd->args[0].v.i);
3365 break;
3367 #endif
3369 default:
3370 mp_msg(MSGT_CPLAYER, MSGL_V,
3371 "Received unknown cmd %s\n", cmd->name);
3374 switch (cmd->pausing) {
3375 case 1: // "pausing"
3376 pause_player(mpctx);
3377 break;
3378 case 3: // "pausing_toggle"
3379 if (mpctx->paused)
3380 unpause_player(mpctx);
3381 else
3382 pause_player(mpctx);
3383 break;