示例#1
0
文件: configfiles.c 项目: FyhSky/bomi
void mp_write_watch_later_conf(struct MPContext *mpctx)
{
    char *filename = mpctx->filename;
    char *conffile = NULL;
    if (!filename)
        goto exit;

    struct demuxer *demux = mpctx->demuxer;
    if (demux && (!demux->seekable || demux->partially_seekable)) {
        MP_INFO(mpctx, "Not seekable - not saving state.\n");
        goto exit;
    }

    double pos = get_current_time(mpctx);
    if (pos == MP_NOPTS_VALUE)
        goto exit;

    mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);

    conffile = mp_get_playback_resume_config_filename(mpctx->global, filename);
    if (!conffile)
        goto exit;

    MP_INFO(mpctx, "Saving state.\n");

    FILE *file = fopen(conffile, "wb");
    if (!file)
        goto exit;
    if (mpctx->opts->write_filename_in_watch_later_config) {
        char write_name[1024] = {0};
        for (int n = 0; filename[n] && n < sizeof(write_name) - 1; n++)
            write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
        fprintf(file, "# %s\n", write_name);
    }
    fprintf(file, "start=%f\n", pos);
    for (int i = 0; backup_properties[i]; i++) {
        const char *pname = backup_properties[i];
        char *val = NULL;
        int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &val, mpctx);
        if (r == M_PROPERTY_OK) {
            if (strncmp(pname, "options/", 8) == 0)
                pname += 8;
            // Only store it if it's different from the initial value.
            char *prev = mpctx->resume_defaults[i];
            if (!prev || strcmp(prev, val) != 0) {
                if (needs_config_quoting(val)) {
                    // e.g. '%6%STRING'
                    fprintf(file, "%s=%%%d%%%s\n", pname, (int)strlen(val), val);
                } else {
                    fprintf(file, "%s=%s\n", pname, val);
                }
            }
        }
        talloc_free(val);
    }
    fclose(file);

exit:
    talloc_free(conffile);
}
示例#2
0
文件: video.c 项目: prodigeni/mpv
// Called after video reinit. This can be generally used to try to insert more
// filters using the filter chain edit functionality in command.c.
static void init_filter_params(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;

    // Note that the filter chain is already initialized. This code might
    // recreate the chain a second time, which is not very elegant, but allows
    // us to test whether enabling deinterlacing works with the current video
    // format and other filters.
    if (opts->deinterlace >= 0)
        mp_property_do("deinterlace", M_PROPERTY_SET, &opts->deinterlace, mpctx);
}
示例#3
0
// Used to retrieve default settings, which should not be stored in the
// resume config. Uses backup_properties[] meaning/order of values.
// This explicitly includes values set by config files and command line.
void mp_get_resume_defaults(struct MPContext *mpctx)
{
    char **list =
        talloc_zero_array(mpctx, char*, MP_ARRAY_SIZE(backup_properties));
    for (int i = 0; backup_properties[i]; i++) {
        const char *pname = backup_properties[i];
        char *val = NULL;
        int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &val, mpctx);
        if (r == M_PROPERTY_OK)
            list[i] = talloc_steal(list, val);
    }
    mpctx->resume_defaults = list;
}
示例#4
0
void mp_write_watch_later_conf(struct MPContext *mpctx)
{
    void *tmp = talloc_new(NULL);
    char *filename = mpctx->filename;
    if (!filename)
        goto exit;

    double pos = get_current_time(mpctx);
    if (pos == MP_NOPTS_VALUE)
        goto exit;

    mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);

    char *conffile = mp_get_playback_resume_config_filename(mpctx->global,
                     mpctx->filename);
    talloc_steal(tmp, conffile);
    if (!conffile)
        goto exit;

    MP_INFO(mpctx, "Saving state.\n");

    FILE *file = fopen(conffile, "wb");
    if (!file)
        goto exit;
    fprintf(file, "start=%f\n", pos);
    for (int i = 0; backup_properties[i]; i++) {
        const char *pname = backup_properties[i];
        char *val = NULL;
        int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &val, mpctx);
        if (r == M_PROPERTY_OK) {
            if (strncmp(pname, "options/", 8) == 0)
                pname += 8;
            // Only store it if it's different from the initial value.
            char *prev = mpctx->resume_defaults[i];
            if (!prev || strcmp(prev, val) != 0) {
                if (needs_config_quoting(val)) {
                    // e.g. '%6%STRING'
                    fprintf(file, "%s=%%%d%%%s\n", pname, (int)strlen(val), val);
                } else {
                    fprintf(file, "%s=%s\n", pname, val);
                }
            }
        }
        talloc_free(val);
    }
    fclose(file);

exit:
    talloc_free(tmp);
}
示例#5
0
文件: video.c 项目: MadFishTheOne/mpv
static void init_filter_params(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;
    struct sh_video *sh_video = mpctx->sh_video;

    // Note that the video decoder already initializes the filter chain. This
    // might recreate the chain a second time, which is not very elegant, but
    // allows us to test whether enabling deinterlacing works with the current
    // video format and other filters.
    if (sh_video->vf_initialized != 1)
        return;

    if (sh_video->vf_reconfig_count <= mpctx->last_vf_reconfig_count) {
        if (opts->deinterlace >= 0) {
            mp_property_do("deinterlace", M_PROPERTY_SET, &opts->deinterlace,
                           mpctx);
        }
    }
    // Setting filter params has to be "stable" (no change if params already
    // set) - checking the reconfig count is just an optimization.
    mpctx->last_vf_reconfig_count = sh_video->vf_reconfig_count;
}
void mplEventHandling( int msg,float param )
{
 int iparam = (int)param;
 mixer_t *mixer = mpctx_get_mixer(guiIntfStruct.mpcontext);

 switch( msg )
  {
// --- user events
   case evExit:
        exit_player( "Exit" );
        break;

   case evPlayNetwork:
        gfree( (void **)&guiIntfStruct.Subtitlename );
	gfree( (void **)&guiIntfStruct.AudioFile );
	guiIntfStruct.StreamType=STREAMTYPE_STREAM;
        goto play;
   case evSetURL:
        gtkShow( evPlayNetwork,NULL );
	break;

   case evSetAudio:
        if ( !guiIntfStruct.demuxer || audio_id == iparam ) break;
	audio_id=iparam;
	goto play;

   case evSetVideo:
        if ( !guiIntfStruct.demuxer || video_id == iparam ) break;
	video_id=iparam;
	goto play;

   case evSetSubtitle:
        mp_property_do("sub",M_PROPERTY_SET,&iparam,guiIntfStruct.mpcontext);
	break;

#ifdef CONFIG_VCD
   case evSetVCDTrack:
        guiIntfStruct.Track=iparam;
   case evPlayVCD:
 	gtkSet( gtkClearStruct,0,(void *)guiALL );
	guiIntfStruct.StreamType=STREAMTYPE_VCD;
	goto play;
#endif
#ifdef CONFIG_DVDREAD
   case evPlayDVD:
        guiIntfStruct.DVD.current_title=1;
        guiIntfStruct.DVD.current_chapter=1;
        guiIntfStruct.DVD.current_angle=1;
play_dvd_2:
 	gtkSet( gtkClearStruct,0,(void *)(guiALL - guiDVD) );
        guiIntfStruct.StreamType=STREAMTYPE_DVD;
	goto play;
#endif
   case evPlay:
   case evPlaySwitchToPause:
play:

        if ( ( msg == evPlaySwitchToPause )&&( guiIntfStruct.Playing == 2 ) ) goto NoPause;

	if ( gtkSet( gtkGetCurrPlItem,0,NULL ) &&( guiIntfStruct.StreamType == STREAMTYPE_FILE ) )
	 {
	  plItem * next = gtkSet( gtkGetCurrPlItem,0,NULL );
	  plLastPlayed=next;
	  mplSetFileName( next->path,next->name,STREAMTYPE_FILE );
	 }

        switch ( guiIntfStruct.StreamType )
         {
	  case STREAMTYPE_STREAM:
	  case STREAMTYPE_FILE:
	       gtkSet( gtkClearStruct,0,(void *)(guiALL - guiFilenames) );
	       break;
#ifdef CONFIG_VCD
          case STREAMTYPE_VCD:
	       gtkSet( gtkClearStruct,0,(void *)(guiALL - guiVCD - guiFilenames) );
	       if ( !cdrom_device ) cdrom_device=gstrdup( DEFAULT_CDROM_DEVICE );
	       mplSetFileName( NULL,cdrom_device,STREAMTYPE_VCD );
	       if ( guiIntfStruct.Playing != 2 )
	        {
		 if ( !guiIntfStruct.Track )
		  {
		   if ( guiIntfStruct.VCDTracks > 1 ) guiIntfStruct.Track=2;
		    else guiIntfStruct.Track=1;
		  }
                 guiIntfStruct.DiskChanged=1;
		}
	       break;
#endif
#ifdef CONFIG_DVDREAD
          case STREAMTYPE_DVD:
	       gtkSet( gtkClearStruct,0,(void *)(guiALL - guiDVD - guiFilenames) );
	       if ( !dvd_device ) dvd_device=gstrdup( DEFAULT_DVD_DEVICE );
	       mplSetFileName( NULL,dvd_device,STREAMTYPE_DVD );
	       if ( guiIntfStruct.Playing != 2 )
	        {
		 guiIntfStruct.Title=guiIntfStruct.DVD.current_title;
		 guiIntfStruct.Chapter=guiIntfStruct.DVD.current_chapter;
		 guiIntfStruct.Angle=guiIntfStruct.DVD.current_angle;
                 guiIntfStruct.DiskChanged=1;
		}
               break;
#endif
         }
	guiIntfStruct.NewPlay=1;
        mplPlay();
        break;
#ifdef CONFIG_DVDREAD
   case evSetDVDSubtitle:
        dvdsub_id=iparam;
        goto play_dvd_2;
        break;
   case evSetDVDAudio:
        audio_id=iparam;
        goto play_dvd_2;
        break;
   case evSetDVDChapter:
        guiIntfStruct.DVD.current_chapter=iparam;
        goto play_dvd_2;
        break;
   case evSetDVDTitle:
        guiIntfStruct.DVD.current_title=iparam;
	guiIntfStruct.DVD.current_chapter=1;
	guiIntfStruct.DVD.current_angle=1;
        goto play_dvd_2;
        break;
#endif

   case evPause:
   case evPauseSwitchToPlay:
NoPause:
        mplPause();
        break;

   case evStop:
	guiIntfStruct.Playing=guiSetStop;
	mplState();
	guiIntfStruct.NoWindow=False;
	break;

   case evLoadPlay:
        mplMainAutoPlay=1;
//	guiIntfStruct.StreamType=STREAMTYPE_FILE;
   case evLoad:
	gtkSet( gtkDelPl,0,NULL );
        gtkShow( evLoad,NULL );
        break;
   case evLoadSubtitle:  gtkShow( evLoadSubtitle,NULL );  break;
   case evDropSubtitle:
	gfree( (void **)&guiIntfStruct.Subtitlename );
	guiLoadSubtitle( NULL );
	break;
   case evLoadAudioFile: gtkShow( evLoadAudioFile,NULL ); break;
   case evPrev: mplPrev(); break;
   case evNext: mplNext(); break;

   case evPlayList:    gtkShow( evPlayList,NULL );        break;
   case evSkinBrowser: gtkShow( evSkinBrowser,skinName ); break;
   case evAbout:       gtkShow( evAbout,NULL );           break;
   case evPreferences: gtkShow( evPreferences,NULL );     break;
   case evEqualizer:   gtkShow( evEqualizer,NULL );       break;

   case evForward10min:	    mplRelSeek( 600 ); break;
   case evBackward10min:    mplRelSeek( -600 );break;
   case evForward1min:      mplRelSeek( 60 );  break;
   case evBackward1min:     mplRelSeek( -60 ); break;
   case evForward10sec:     mplRelSeek( 10 );  break;
   case evBackward10sec:    mplRelSeek( -10 ); break;
   case evSetMoviePosition: mplAbsSeek( param ); break;

   case evIncVolume:  vo_x11_putkey( wsGrayMul ); break;
   case evDecVolume:  vo_x11_putkey( wsGrayDiv ); break;
   case evMute:       mixer_mute( mixer ); break;

   case evSetVolume:
        guiIntfStruct.Volume=param;
	goto set_volume;
   case evSetBalance:
        guiIntfStruct.Balance=param;
set_volume:
        {
	 float l = guiIntfStruct.Volume * ( ( 100.0 - guiIntfStruct.Balance ) / 50.0 );
	 float r = guiIntfStruct.Volume * ( ( guiIntfStruct.Balance ) / 50.0 );
	 if ( l > guiIntfStruct.Volume ) l=guiIntfStruct.Volume;
	 if ( r > guiIntfStruct.Volume ) r=guiIntfStruct.Volume;
//	 printf( "!!! v: %.2f b: %.2f -> %.2f x %.2f\n",guiIntfStruct.Volume,guiIntfStruct.Balance,l,r );
         mixer_setvolume( mixer,l,r );
	}
	if ( osd_level )
	 {
	  osd_visible=(GetTimerMS() + 1000) | 1;
	  vo_osd_progbar_type=OSD_VOLUME;
	  vo_osd_progbar_value=( ( guiIntfStruct.Volume ) * 256.0 ) / 100.0;
	  vo_osd_changed( OSDTYPE_PROGBAR );
	 }
        break;


   case evIconify:
        switch ( iparam )
         {
          case 0: wsIconify( appMPlayer.mainWindow ); break;
          case 1: wsIconify( appMPlayer.subWindow ); break;
         }
        break;
   case evHalfSize:
        btnSet( evFullScreen,btnReleased );
        if ( guiIntfStruct.Playing )
         {
          if ( appMPlayer.subWindow.isFullScreen )
           {
            mplFullScreen();
           }
          wsResizeWindow( &appMPlayer.subWindow, guiIntfStruct.MovieWidth / 2, guiIntfStruct.MovieHeight / 2 );
          wsMoveWindow( &appMPlayer.subWindow, 0,
                        ( wsMaxX - guiIntfStruct.MovieWidth/2  )/2 + wsOrgX,
                        ( wsMaxY - guiIntfStruct.MovieHeight/2 )/2 + wsOrgY  );
         }
        break;
   case evDoubleSize:
    	btnSet( evFullScreen,btnReleased );
        if ( guiIntfStruct.Playing )
         {
          if ( appMPlayer.subWindow.isFullScreen )
           {
            mplFullScreen();
           }
          wsResizeWindow( &appMPlayer.subWindow, guiIntfStruct.MovieWidth * 2, guiIntfStruct.MovieHeight * 2 );
          wsMoveWindow( &appMPlayer.subWindow, 0,
                        ( wsMaxX - guiIntfStruct.MovieWidth*2  )/2 + wsOrgX,
                        ( wsMaxY - guiIntfStruct.MovieHeight*2 )/2 + wsOrgY  );
         }
        break;
   case evNormalSize:
	btnSet( evFullScreen,btnReleased );
        if ( guiIntfStruct.Playing )
         {
          if ( appMPlayer.subWindow.isFullScreen )
           {
            mplFullScreen();
           }
          wsResizeWindow( &appMPlayer.subWindow, guiIntfStruct.MovieWidth, guiIntfStruct.MovieHeight );
          wsMoveWindow( &appMPlayer.subWindow, 0,
                        ( wsMaxX - guiIntfStruct.MovieWidth  )/2 + wsOrgX,
                        ( wsMaxY - guiIntfStruct.MovieHeight )/2 + wsOrgY  );
	  break;
         } else if ( !appMPlayer.subWindow.isFullScreen ) break;
   case evFullScreen:
        if ( !guiIntfStruct.Playing && !gtkShowVideoWindow ) break;
        mplFullScreen();
	if ( appMPlayer.subWindow.isFullScreen ) btnSet( evFullScreen,btnPressed );
	 else btnSet( evFullScreen,btnReleased );
        break;

   case evSetAspect:
	switch ( iparam )
	 {
	  case 2:  movie_aspect=16.0f / 9.0f; break;
	  case 3:  movie_aspect=4.0f / 3.0f;  break;
	  case 4:  movie_aspect=2.35;         break;
	  case 1:
	  default: movie_aspect=-1;
	 }
	wsClearWindow( appMPlayer.subWindow );
#ifdef CONFIG_DVDREAD
	if ( guiIntfStruct.StreamType == STREAMTYPE_DVD || guiIntfStruct.StreamType == STREAMTYPE_VCD ) goto play_dvd_2;
	 else
#endif
	 guiIntfStruct.NewPlay=1;
	break;

// --- timer events
   case evRedraw:
        {
          unsigned now = GetTimerMS();
          extern int mplPBFade;
          if ((now > last_redraw_time) &&
              (now < last_redraw_time + GUI_REDRAW_WAIT) &&
              !mplPBFade)
            break;
          last_redraw_time = now;
        }
        mplMainRender=1;
        wsPostRedisplay( &appMPlayer.mainWindow );
	wsPostRedisplay( &appMPlayer.barWindow );
        break;
// --- system events
#ifdef MP_DEBUG
   case evNone:
        mp_msg( MSGT_GPLAYER,MSGL_STATUS,"[mw] event none received.\n" );
        break;
   default:
        mp_msg( MSGT_GPLAYER,MSGL_STATUS,"[mw] unknown event received ( %d,%.2f ).\n",msg,param );
        break;
#endif
  }
}
示例#7
0
文件: video.c 项目: xnoreq/mpv
int reinit_video_chain(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;
    assert(!mpctx->d_video);
    struct track *track = mpctx->current_track[0][STREAM_VIDEO];
    struct sh_stream *sh = track ? track->stream : NULL;
    if (!sh)
        goto no_video;

    MP_VERBOSE(mpctx, "[V] fourcc:0x%X  size:%dx%d  fps:%5.3f\n",
               sh->format,
               sh->video->disp_w, sh->video->disp_h,
               sh->video->fps);

    //================== Init VIDEO (codec & libvo) ==========================
    if (!opts->fixed_vo || !mpctx->video_out) {
        struct vo_extra ex = {
            .input_ctx = mpctx->input,
            .osd = mpctx->osd,
            .encode_lavc_ctx = mpctx->encode_lavc_ctx,
            .opengl_cb_context = mpctx->gl_cb_ctx,
        };
        mpctx->video_out = init_best_video_out(mpctx->global, &ex);
        if (!mpctx->video_out) {
            MP_FATAL(mpctx, "Error opening/initializing "
                    "the selected video_out (-vo) device.\n");
            mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED;
            goto err_out;
        }
        mpctx->mouse_cursor_visible = true;
    }

    update_window_title(mpctx, true);

    struct dec_video *d_video = talloc_zero(NULL, struct dec_video);
    mpctx->d_video = d_video;
    d_video->global = mpctx->global;
    d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
    d_video->opts = mpctx->opts;
    d_video->header = sh;
    d_video->fps = sh->video->fps;
    d_video->vo = mpctx->video_out;

    if (opts->force_fps) {
        d_video->fps = opts->force_fps;
        MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
        MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
    }
    update_fps(mpctx);

    vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);

    recreate_video_filters(mpctx);

    if (!video_init_best_codec(d_video, opts->video_decoders))
        goto err_out;

    bool saver_state = opts->pause || !opts->stop_screensaver;
    vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
                                             : VOCTRL_KILL_SCREENSAVER, NULL);

    vo_set_paused(mpctx->video_out, mpctx->paused);

    mpctx->sync_audio_to_video = !sh->attached_picture;
    mpctx->vo_pts_history_seek_ts++;

    // If we switch on video again, ensure audio position matches up.
    if (mpctx->d_audio)
        mpctx->audio_status = STATUS_SYNCING;

    reset_video_state(mpctx);
    reset_subtitle_state(mpctx);

    return 1;

err_out:
no_video:
    uninit_video_chain(mpctx);
    if (track)
        error_on_track(mpctx, track);
    handle_force_window(mpctx, true);
    return 0;
}

// Try to refresh the video by doing a precise seek to the currently displayed
// frame. This can go wrong in all sorts of ways, so use sparingly.
void mp_force_video_refresh(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;
    struct dec_video *d_video = mpctx->d_video;

    if (!d_video || !d_video->decoder_output.imgfmt)
        return;

    // If not paused, the next frame should come soon enough.
    if (opts->pause && mpctx->last_vo_pts != MP_NOPTS_VALUE)
        queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts, 2, true);
}

static int check_framedrop(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;
    // check for frame-drop:
    if (mpctx->video_status == STATUS_PLAYING && !mpctx->paused &&
        mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao))
    {
        float fps = mpctx->d_video->fps;
        double frame_time = fps > 0 ? 1.0 / fps : 0;
        // we should avoid dropping too many frames in sequence unless we
        // are too late. and we allow 100ms A-V delay here:
        if (mpctx->last_av_difference - 0.100 > mpctx->dropped_frames * frame_time)
            return !!(opts->frame_dropping & 2);
    }
    return 0;
}

// Read a packet, store decoded image into d_video->waiting_decoded_mpi
// returns VD_* code
static int decode_image(struct MPContext *mpctx)
{
    struct dec_video *d_video = mpctx->d_video;

    if (d_video->header->attached_picture) {
        d_video->waiting_decoded_mpi =
                    video_decode(d_video, d_video->header->attached_picture, 0);
        return VD_EOF;
    }

    struct demux_packet *pkt;
    if (demux_read_packet_async(d_video->header, &pkt) == 0)
        return VD_WAIT;
    if (pkt && pkt->pts != MP_NOPTS_VALUE)
        pkt->pts += mpctx->video_offset;
    if ((pkt && pkt->pts >= mpctx->hrseek_pts - .005) ||
        d_video->has_broken_packet_pts ||
        !mpctx->opts->hr_seek_framedrop)
    {
        mpctx->hrseek_framedrop = false;
    }
    bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
    int framedrop_type = hrseek && mpctx->hrseek_framedrop ?
                         2 : check_framedrop(mpctx);
    d_video->waiting_decoded_mpi =
        video_decode(d_video, pkt, framedrop_type);
    bool had_packet = !!pkt;
    talloc_free(pkt);

    if (had_packet && !d_video->waiting_decoded_mpi &&
        mpctx->video_status == STATUS_PLAYING)
    {
        mpctx->dropped_frames_total++;
        mpctx->dropped_frames++;
    }

    return had_packet ? VD_PROGRESS : VD_EOF;
}


// Called after video reinit. This can be generally used to try to insert more
// filters using the filter chain edit functionality in command.c.
static void init_filter_params(struct MPContext *mpctx)
{
    struct MPOpts *opts = mpctx->opts;

    // Note that the filter chain is already initialized. This code might
    // recreate the chain a second time, which is not very elegant, but allows
    // us to test whether enabling deinterlacing works with the current video
    // format and other filters.
    if (opts->deinterlace >= 0)
        mp_property_do("deinterlace", M_PROPERTY_SET, &opts->deinterlace, mpctx);
}

// Feed newly decoded frames to the filter, take care of format changes.
// If eof=true, drain the filter chain, and return VD_EOF if empty.
static int video_filter(struct MPContext *mpctx, bool eof)
{
    struct dec_video *d_video = mpctx->d_video;
    struct vf_chain *vf = d_video->vfilter;

    if (vf->initialized < 0)
        return VD_ERROR;

    // There is already a filtered frame available.
    // If vf_needs_input() returns > 0, the filter wants input anyway.
    if (vf_output_frame(vf, eof) > 0 && vf_needs_input(vf) < 1)
        return VD_PROGRESS;

    // Decoder output is different from filter input?
    bool need_vf_reconfig = !vf->input_params.imgfmt || vf->initialized < 1 ||
        !mp_image_params_equal(&d_video->decoder_output, &vf->input_params);

    // (If imgfmt==0, nothing was decoded yet, and the format is unknown.)
    if (need_vf_reconfig && d_video->decoder_output.imgfmt) {
        // Drain the filter chain.
        if (vf_output_frame(vf, true) > 0)
            return VD_PROGRESS;

        // The filter chain is drained; execute the filter format change.
        filter_reconfig(mpctx, false);
        if (vf->initialized == 0)
            return VD_PROGRESS; // hw decoding fallback; try again
        if (vf->initialized < 1)
            return VD_ERROR;
        init_filter_params(mpctx);
        return VD_RECONFIG;
    }

    // If something was decoded, and the filter chain is ready, filter it.
    if (!need_vf_reconfig && d_video->waiting_decoded_mpi) {
        vf_filter_frame(vf, d_video->waiting_decoded_mpi);
        d_video->waiting_decoded_mpi = NULL;
        return VD_PROGRESS;
    }

    return eof ? VD_EOF : VD_PROGRESS;
}

// Make sure at least 1 filtered image is available, decode new video if needed.
// returns VD_* code
// A return value of VD_PROGRESS doesn't necessarily output a frame, but makes
// the promise that calling this function again will eventually do something.
static int video_decode_and_filter(struct MPContext *mpctx)
{
    struct dec_video *d_video = mpctx->d_video;

    int r = video_filter(mpctx, false);
    if (r < 0)
        return r;

    if (!d_video->waiting_decoded_mpi) {
        // Decode a new image, or at least feed the decoder a packet.
        r = decode_image(mpctx);
        if (r == VD_WAIT)
            return r;
        if (d_video->waiting_decoded_mpi)
            d_video->decoder_output = d_video->waiting_decoded_mpi->params;
    }

    bool eof = !d_video->waiting_decoded_mpi && (r == VD_EOF || r < 0);
    r = video_filter(mpctx, eof);
    if (r == VD_RECONFIG) // retry feeding decoded image
        r = video_filter(mpctx, eof);
    return r;
}

static int video_feed_async_filter(struct MPContext *mpctx)
{
    struct dec_video *d_video = mpctx->d_video;
    struct vf_chain *vf = d_video->vfilter;

    if (vf->initialized < 0)
        return VD_ERROR;

    if (vf_needs_input(vf) < 1)
        return 0;
    mpctx->sleeptime = 0; // retry until done
    return video_decode_and_filter(mpctx);
}

/* Modify video timing to match the audio timeline. There are two main
 * reasons this is needed. First, video and audio can start from different
 * positions at beginning of file or after a seek (MPlayer starts both
 * immediately even if they have different pts). Second, the file can have
 * audio timestamps that are inconsistent with the duration of the audio
 * packets, for example two consecutive timestamp values differing by
 * one second but only a packet with enough samples for half a second
 * of playback between them.
 */
static void adjust_sync(struct MPContext *mpctx, double v_pts, double frame_time)
{
    struct MPOpts *opts = mpctx->opts;

    if (mpctx->audio_status != STATUS_PLAYING)
        return;

    double a_pts = written_audio_pts(mpctx) + mpctx->audio_delay - mpctx->delay;
    double av_delay = a_pts - v_pts;

    double change = av_delay * 0.1;
    double max_change = opts->default_max_pts_correction >= 0 ?
                        opts->default_max_pts_correction : frame_time * 0.1;
    if (change < -max_change)
        change = -max_change;
    else if (change > max_change)
        change = max_change;
    mpctx->delay += change;
    mpctx->total_avsync_change += change;
}

// Enough video filtered already to push one frame to the VO?
static bool have_new_frame(struct MPContext *mpctx)
{
    bool need_2nd = !!(mpctx->opts->frame_dropping & 1) // we need the duration
        && mpctx->video_pts != MP_NOPTS_VALUE; // ...except for the 1st frame

    return mpctx->next_frame[0] && (!need_2nd || mpctx->next_frame[1]);
}
void uiEventHandling( int msg,float param )
{
 int iparam = (int)param;
 mixer_t *mixer = mpctx_get_mixer(guiInfo.mpcontext);

 switch( msg )
  {
/* user events */
   case evExit:
        mplayer( MPLAYER_EXIT_GUI, EXIT_QUIT, 0 );
        break;

   case evLoadURL:
        gtkShow( evLoadURL,NULL );
	break;

   case ivSetAudio:
        if ( !mpctx_get_demuxer(guiInfo.mpcontext) || audio_id == iparam ) break;
	mp_property_do("switch_audio",M_PROPERTY_SET,&iparam,guiInfo.mpcontext);
	break;

   case ivSetVideo:
        if ( !mpctx_get_demuxer(guiInfo.mpcontext) || video_id == iparam ) break;
	mp_property_do("switch_video",M_PROPERTY_SET,&iparam,guiInfo.mpcontext);
	break;

   case ivSetSubtitle:
        mp_property_do("sub",M_PROPERTY_SET,&iparam,guiInfo.mpcontext);
	break;

#ifdef CONFIG_CDDA
   case ivSetCDTrack:
        guiInfo.Track=iparam;
   case evPlayCD:
 	guiInfoMediumClear ( CLEAR_ALL );
	guiInfo.StreamType=STREAMTYPE_CDDA;
	goto play;
#endif
#ifdef CONFIG_VCD
   case ivSetVCDTrack:
        guiInfo.Track=iparam;
   case evPlayVCD:
 	guiInfoMediumClear ( CLEAR_ALL );
	guiInfo.StreamType=STREAMTYPE_VCD;
	goto play;
#endif
#ifdef CONFIG_DVDREAD
   case ivSetDVDSubtitle:
        dvdsub_id=iparam;
        uiEventHandling( ivPlayDVD, 0 );
        break;
   case ivSetDVDAudio:
        audio_id=iparam;
        uiEventHandling( ivPlayDVD, 0 );
        break;
   case ivSetDVDChapter:
        guiInfo.Chapter=iparam;
        uiEventHandling( ivPlayDVD, 0 );
        break;
   case ivSetDVDTitle:
        guiInfo.Track=iparam;
        guiInfo.Chapter=1;
        guiInfo.Angle=1;
        uiEventHandling( ivPlayDVD, 0 );
        break;
   case evPlayDVD:
        guiInfo.Track=1;
        guiInfo.Chapter=1;
        guiInfo.Angle=1;
   case ivPlayDVD:
 	guiInfoMediumClear( CLEAR_ALL - CLEAR_DVD );
        guiInfo.StreamType=STREAMTYPE_DVD;
	goto play;
#endif
   case evPlay:
   case evPlaySwitchToPause:
play:

        if ( ( msg == evPlaySwitchToPause )&&( guiInfo.Playing == GUI_PAUSE ) ) goto NoPause;

	if ( listMgr( PLAYLIST_ITEM_GET_CURR,0 ) &&( guiInfo.StreamType == STREAMTYPE_FILE ) )
	 {
	  plItem * next = listMgr( PLAYLIST_ITEM_GET_CURR,0 );
	  uiSetFileName( next->path,next->name,SAME_STREAMTYPE );
	 }

        switch ( guiInfo.StreamType )
         {
	  case STREAMTYPE_FILE:
	  case STREAMTYPE_STREAM:
	  case STREAMTYPE_PLAYLIST:
	       guiInfoMediumClear( CLEAR_ALL - CLEAR_FILE );
	       if ( !guiInfo.Track )
	         guiInfo.Track=1;
	       guiInfo.NewPlay=GUI_FILE_NEW;
	       break;

          case STREAMTYPE_CDDA:
	       guiInfoMediumClear( CLEAR_ALL - CLEAR_VCD - CLEAR_FILE );
	       if ( guiInfo.Playing != GUI_PAUSE )
	        {
		 if ( !guiInfo.Track )
                   guiInfo.Track=1;
                 guiInfo.NewPlay=GUI_FILE_SAME;
		}
	       break;

          case STREAMTYPE_VCD:
	       guiInfoMediumClear( CLEAR_ALL - CLEAR_VCD - CLEAR_FILE );
	       if ( guiInfo.Playing != GUI_PAUSE )
	        {
		 if ( !guiInfo.Track )
                   guiInfo.Track=2;
                 guiInfo.NewPlay=GUI_FILE_SAME;
		}
	       break;

          case STREAMTYPE_DVD:
	       guiInfoMediumClear( CLEAR_ALL - CLEAR_DVD - CLEAR_FILE );
	       if ( guiInfo.Playing != GUI_PAUSE )
	        {
                 guiInfo.NewPlay=GUI_FILE_SAME;
		}
               break;
         }
        uiPlay();
        break;

   case evPause:
   case evPauseSwitchToPlay:
NoPause:
        uiPause();
        break;

   case evStop:
	guiInfo.Playing=GUI_STOP;
	uiState();
	break;

   case evLoadPlay:
        uiMainAutoPlay=1;
//	guiInfo.StreamType=STREAMTYPE_FILE;
   case evLoad:
	listMgr( PLAYLIST_DELETE,0 );
        gtkShow( evLoad,NULL );
        break;
   case evLoadSubtitle:  gtkShow( evLoadSubtitle,NULL );  break;
   case evDropSubtitle:
	nfree( guiInfo.SubtitleFilename );
	mplayerLoadSubtitle( NULL );
	break;
   case evLoadAudioFile: gtkShow( evLoadAudioFile,NULL ); break;
   case evPrev: uiPrev(); break;
   case evNext: uiNext(); break;

   case evPlaylist:    gtkShow( evPlaylist,NULL );        break;
   case evSkinBrowser: gtkShow( evSkinBrowser,skinName ); break;
   case evAbout:       gtkShow( evAbout,NULL );           break;
   case evPreferences: gtkShow( evPreferences,NULL );     break;
   case evEqualizer:   gtkShow( evEqualizer,NULL );       break;

   case evForward10min:	    uiRelSeek( 600 ); break;
   case evBackward10min:    uiRelSeek( -600 );break;
   case evForward1min:      uiRelSeek( 60 );  break;
   case evBackward1min:     uiRelSeek( -60 ); break;
   case evForward10sec:     uiRelSeek( 10 );  break;
   case evBackward10sec:    uiRelSeek( -10 ); break;
   case evSetMoviePosition: uiAbsSeek( param ); break;

   case evIncVolume:  vo_x11_putkey( wsGrayMul ); break;
   case evDecVolume:  vo_x11_putkey( wsGrayDiv ); break;
   case evMute:       mixer_mute( mixer ); break;

   case evSetVolume:
        guiInfo.Volume=param;
	goto set_volume;
   case evSetBalance:
        guiInfo.Balance=param;
set_volume:
        {
	 float l = guiInfo.Volume * ( ( 100.0 - guiInfo.Balance ) / 50.0 );
	 float r = guiInfo.Volume * ( ( guiInfo.Balance ) / 50.0 );
	 if ( l > guiInfo.Volume ) l=guiInfo.Volume;
	 if ( r > guiInfo.Volume ) r=guiInfo.Volume;
//	 printf( "!!! v: %.2f b: %.2f -> %.2f x %.2f\n",guiInfo.Volume,guiInfo.Balance,l,r );
         mixer_setvolume( mixer,l,r );
	}
	if ( osd_level )
	 {
	  osd_visible=(GetTimerMS() + 1000) | 1;
	  vo_osd_progbar_type=OSD_VOLUME;
	  vo_osd_progbar_value=( ( guiInfo.Volume ) * 256.0 ) / 100.0;
	  vo_osd_changed( OSDTYPE_PROGBAR );
	 }
        break;


   case evMenu:
        /*if (guiApp.menuIsPresent)   NOTE TO MYSELF: Uncomment only after mouse
         {                                            pointer and cursor keys work
          gtkShow( ivHidePopUpMenu,NULL );            with this menu from skin as
          uiShowMenu( 0,0 );                          they do with normal menus.
         }
        else*/ gtkShow( ivShowPopUpMenu,NULL );
        break;

   case evIconify:
        switch ( iparam )
         {
          case 0: wsIconify( guiApp.mainWindow ); break;
          case 1: wsIconify( guiApp.videoWindow ); break;
         }
        break;
   case evHalfSize:
        if ( guiInfo.VideoWindow && guiInfo.Playing )
         {
          if ( guiApp.videoWindow.isFullScreen )
           {
            uiFullScreen();
           }
          wsResizeWindow( &guiApp.videoWindow, guiInfo.VideoWidth / 2, guiInfo.VideoHeight / 2 );
          wsMoveWindow( &guiApp.videoWindow, False, guiApp.video.x, guiApp.video.y );
          btnSet( evFullScreen,btnReleased );
         }
        break;
   case evDoubleSize:
        if ( guiInfo.VideoWindow && guiInfo.Playing )
         {
          if ( guiApp.videoWindow.isFullScreen )
           {
            uiFullScreen();
           }
          wsResizeWindow( &guiApp.videoWindow, guiInfo.VideoWidth * 2, guiInfo.VideoHeight * 2 );
          wsMoveWindowWithin( &guiApp.videoWindow, False, guiApp.video.x, guiApp.video.y );
          btnSet( evFullScreen,btnReleased );
         }
        break;
   case evNormalSize:
        if ( guiInfo.VideoWindow && guiInfo.Playing )
         {
          if ( guiApp.videoWindow.isFullScreen )
           {
            uiFullScreen();
           }
          wsResizeWindow( &guiApp.videoWindow, guiInfo.VideoWidth, guiInfo.VideoHeight );
          wsMoveWindow( &guiApp.videoWindow, False, guiApp.video.x, guiApp.video.y );
          btnSet( evFullScreen,btnReleased );
	  break;
         } else if ( !guiApp.videoWindow.isFullScreen ) break;
   case evFullScreen:
        if ( guiInfo.VideoWindow && guiInfo.Playing )
         {
          uiFullScreen();
          if ( !guiApp.videoWindow.isFullScreen )
           {
            wsResizeWindow( &guiApp.videoWindow, guiInfo.VideoWidth, guiInfo.VideoHeight );
            wsMoveWindow( &guiApp.videoWindow, False, guiApp.video.x, guiApp.video.y );
           }
         }
	if ( guiApp.videoWindow.isFullScreen ) btnSet( evFullScreen,btnPressed );
	 else btnSet( evFullScreen,btnReleased );
        break;

   case evSetAspect:
	switch ( iparam )
	 {
	  case 2:  movie_aspect=16.0f / 9.0f; break;
	  case 3:  movie_aspect=4.0f / 3.0f;  break;
	  case 4:  movie_aspect=2.35;         break;
	  case 1:
	  default: movie_aspect=-1;
	 }
	wsClearWindow( guiApp.videoWindow );
	if ( guiInfo.StreamType == STREAMTYPE_VCD ) uiEventHandling( evPlayVCD, 0 );
	 else if ( guiInfo.StreamType == STREAMTYPE_DVD ) uiEventHandling( ivPlayDVD, 0 );
	 else
	 guiInfo.NewPlay=GUI_FILE_NEW;
	break;

/* timer events */
   case ivRedraw:
        {
          unsigned now = GetTimerMS();
          if ((now > last_redraw_time) &&
              (now < last_redraw_time + GUI_REDRAW_WAIT) &&
              !uiPlaybarFade && (iparam == 0))
            break;
          last_redraw_time = now;
        }
        uiMainRender=1;
        wsPostRedisplay( &guiApp.mainWindow );
	wsPostRedisplay( &guiApp.playbarWindow );
        break;
/* system events */
   case evNone:
        mp_msg( MSGT_GPLAYER,MSGL_DBG2,"[main] uiEventHandling: evNone\n" );
        break;
   default:
        mp_msg( MSGT_GPLAYER,MSGL_DBG2,"[main] uiEventHandling: unknown event %d, param %.2f\n", msg, param );
        break;
  }
}
示例#9
0
void mp_write_watch_later_conf(struct MPContext *mpctx)
{
    struct playlist_entry *cur = mpctx->playing;
    char *conffile = NULL;
    if (!cur)
        goto exit;

    struct demuxer *demux = mpctx->demuxer;
    if (demux && (!demux->seekable || demux->partially_seekable)) {
        MP_INFO(mpctx, "Not seekable - not saving state.\n");
        goto exit;
    }

    conffile = mp_get_playback_resume_config_filename(mpctx, cur->filename);
    if (!conffile)
        goto exit;

    mp_mk_config_dir(mpctx->global, mpctx->cached_watch_later_configdir);

    MP_INFO(mpctx, "Saving state.\n");

    FILE *file = fopen(conffile, "wb");
    if (!file)
        goto exit;

    write_filename(mpctx, file, cur->filename);

    double pos = get_current_time(mpctx);
    if (pos != MP_NOPTS_VALUE)
        fprintf(file, "start=%f\n", pos);
    for (int i = 0; backup_properties[i]; i++) {
        const char *pname = backup_properties[i];
        char *val = NULL;
        int r = mp_property_do(pname, M_PROPERTY_GET_STRING, &val, mpctx);
        if (r == M_PROPERTY_OK) {
            if (strncmp(pname, "options/", 8) == 0)
                pname += 8;
            // Only store it if it's different from the initial value.
            char *prev = mpctx->resume_defaults[i];
            if (!prev || strcmp(prev, val) != 0) {
                if (needs_config_quoting(val)) {
                    // e.g. '%6%STRING'
                    fprintf(file, "%s=%%%d%%%s\n", pname, (int)strlen(val), val);
                } else {
                    fprintf(file, "%s=%s\n", pname, val);
                }
            }
        }
        talloc_free(val);
    }
    fclose(file);

    // This allows us to recursively resume directories etc., whose entries are
    // expanded the first time it's "played". For example, if "/a/b/c.mkv" is
    // the current entry, then we want to resume this file if the user does
    // "mpv /a". This would expand to the directory entries in "/a", and if
    // "/a/a.mkv" is not the first entry, this would be played.
    // Here, we write resume entries for "/a" and "/a/b".
    // (Unfortunately, this will leave stray resume files on resume, because
    // obviously it resumes only from one of those paths.)
    for (int n = 0; n < cur->num_redirects; n++)
        write_redirect(mpctx, cur->redirects[n]);
    // And at last, for local directories, we write an entry for each path
    // prefix, so the user can resume from an arbitrary directory. This starts
    // with the first redirect (all other redirects are further prefixes).
    if (cur->num_redirects) {
        char *path = cur->redirects[0];
        char tmp[4096];
        if (!mp_is_url(bstr0(path)) && strlen(path) < sizeof(tmp)) {
            snprintf(tmp, sizeof(tmp), "%s", path);
            for (;;) {
                bstr dir = mp_dirname(tmp);
                if (dir.len == strlen(tmp) || !dir.len || bstr_equals0(dir, "."))
                    break;

                tmp[dir.len] = '\0';
                if (strlen(tmp) >= 2) // keep "/"
                    mp_path_strip_trailing_separator(tmp);
                write_redirect(mpctx, tmp);
            }
        }
    }

exit:
    talloc_free(conffile);
}
示例#10
0
文件: actions.c 项目: 0p1pp1/mplayer
void uiEvent(int ev, float param)
{
    int iparam     = (int)param;
    mixer_t *mixer = mpctx_get_mixer(guiInfo.mpcontext);
    float aspect;
    char cmd[40];

    switch (ev) {
/* user events */
    case evExit:
        mplayer(MPLAYER_EXIT_GUI, EXIT_QUIT, 0);
        break;

    case evLoadURL:
        gtkShow(evLoadURL, NULL);
        break;

    case ivSetAudio:

        if (!mpctx_get_demuxer(guiInfo.mpcontext) || audio_id == iparam)
            break;

        mp_property_do("switch_audio", M_PROPERTY_SET, &iparam, guiInfo.mpcontext);
        break;

    case ivSetVideo:

        if (!mpctx_get_demuxer(guiInfo.mpcontext) || video_id == iparam)
            break;

        mp_property_do("switch_video", M_PROPERTY_SET, &iparam, guiInfo.mpcontext);
        break;

    case ivSetSubtitle:
        mp_property_do("sub", M_PROPERTY_SET, &iparam, guiInfo.mpcontext);
        break;

#ifdef CONFIG_CDDA
    case ivSetCDTrack:
        guiInfo.Track = iparam;

    case evPlayCD:
        guiInfo.StreamType = STREAMTYPE_CDDA;
        goto play;
#endif
#ifdef CONFIG_VCD
    case ivSetVCDTrack:
        guiInfo.Track = iparam;

    case evPlayVCD:
        guiInfo.StreamType = STREAMTYPE_VCD;
        goto play;
#endif
#ifdef CONFIG_DVDREAD
    case ivSetDVDSubtitle:
        dvdsub_id = iparam;
        uiEvent(ivPlayDVD, 0);
        break;

    case ivSetDVDAudio:
        audio_id = iparam;
        uiEvent(ivPlayDVD, 0);
        break;

    case ivSetDVDChapter:
        guiInfo.Chapter = iparam;
        uiEvent(ivPlayDVD, 0);
        break;

    case ivSetDVDTitle:
        guiInfo.Track   = iparam;
        guiInfo.Chapter = 1;
        guiInfo.Angle   = 1;
        uiEvent(ivPlayDVD, 0);
        break;

    case evPlayDVD:
        guiInfo.Chapter = 1;
        guiInfo.Angle   = 1;

    case ivPlayDVD:
        guiInfo.StreamType = STREAMTYPE_DVD;
        goto play;
#endif
#ifdef CONFIG_TV
    case evPlayTV:
        guiInfo.StreamType = guiTV[gui_tv_digital].StreamType;
        goto play;
#endif
    case evPlay:
    case evPlaySwitchToPause:
play:

        if (guiInfo.Playing != GUI_PAUSE) {
            MediumPrepare(guiInfo.StreamType);

            switch (guiInfo.StreamType) {
            case STREAMTYPE_FILE:
            case STREAMTYPE_STREAM:
            case STREAMTYPE_PLAYLIST:

                if (!guiInfo.Track)
                    guiInfo.Track = 1;

                guiInfo.MediumChanged = GUI_MEDIUM_NEW;
                guiInfo.PlaylistNext  = !guiInfo.Playing;

                break;

            case STREAMTYPE_CDDA:
            case STREAMTYPE_VCD:
            case STREAMTYPE_DVD:
            case STREAMTYPE_TV:
            case STREAMTYPE_DVB:

                if (!guiInfo.Track)
                    guiInfo.Track = (guiInfo.StreamType == STREAMTYPE_VCD ? 2 : 1);

            case STREAMTYPE_BINCUE:   // track 0 is OK and will auto-select first media data track
                guiInfo.MediumChanged = GUI_MEDIUM_SAME;

                break;
            }
        }

        uiPlay();
        break;

    case evPause:
    case evPauseSwitchToPlay:
        uiPause();
        break;

    case evStop:
        guiInfo.Playing = GUI_STOP;
        uiState();
        break;

    case evLoadPlay:
        uiLoadPlay = True;

//      guiInfo.StreamType=STREAMTYPE_FILE;
    case evLoad:
        gtkShow(evLoad, NULL);
        break;

    case evLoadSubtitle:
        gtkShow(evLoadSubtitle, NULL);
        break;

    case evDropSubtitle:
        nfree(guiInfo.SubtitleFilename);
        mplayerLoadSubtitle(NULL);
        break;

    case evLoadAudioFile:
        gtkShow(evLoadAudioFile, NULL);
        break;

    case evPlayImage:
        gtkShow(evPlayImage, NULL);
        break;

    case evPrev:
        uiPrev();
        break;

    case evNext:
        uiNext();
        break;

    case evPlaylist:
        gtkShow(evPlaylist, NULL);
        break;

    case evSkinBrowser:
        gtkShow(evSkinBrowser, skinName);
        break;

    case evAbout:
        gtkShow(evAbout, NULL);
        break;

    case evPreferences:
        gtkShow(evPreferences, NULL);
        break;

    case evEqualizer:
        gtkShow(evEqualizer, NULL);
        break;

    case evForward10min:
        uiRelSeek(600);
        break;

    case evBackward10min:
        uiRelSeek(-600);
        break;

    case evForward1min:
        uiRelSeek(60);
        break;

    case evBackward1min:
        uiRelSeek(-60);
        break;

    case evForward10sec:
        uiRelSeek(10);
        break;

    case evBackward10sec:
        uiRelSeek(-10);
        break;

    case evSetMoviePosition:
        guiInfo.Position = param;
        uiPctSeek(guiInfo.Position);
        break;

    case evIncVolume:
        mplayer_put_key(KEY_VOLUME_UP);
        break;

    case evDecVolume:
        mplayer_put_key(KEY_VOLUME_DOWN);
        break;

    case evMute:
        mixer_mute(mixer);
        break;

    case evSetVolume:
    case ivSetVolume:
        guiInfo.Volume = param;
        {
            float l = guiInfo.Volume * (100.0 - guiInfo.Balance) / 50.0;
            float r = guiInfo.Volume * guiInfo.Balance / 50.0;
            mixer_setvolume(mixer, FFMIN(l, guiInfo.Volume), FFMIN(r, guiInfo.Volume));
        }

        if (ev == ivSetVolume)
            break;

        if (osd_level) {
            osd_visible = (GetTimerMS() + 1000) | 1;
            vo_osd_progbar_type  = OSD_VOLUME;
            vo_osd_progbar_value = guiInfo.Volume * 256.0 / 100.0;
            vo_osd_changed(OSDTYPE_PROGBAR);
        }

        break;

    case evSetBalance:
    case ivSetBalance:
        guiInfo.Balance = param;
        mixer_setbalance(mixer, (guiInfo.Balance - 50.0) / 50.0);     // transform 0..100 to -1..1
        uiEvent(ivSetVolume, guiInfo.Volume);

        if (ev == ivSetBalance)
            break;

        if (osd_level) {
            osd_visible = (GetTimerMS() + 1000) | 1;
            vo_osd_progbar_type  = OSD_BALANCE;
            vo_osd_progbar_value = guiInfo.Balance * 256.0 / 100.0;
            vo_osd_changed(OSDTYPE_PROGBAR);
        }

        break;

    case evMenu:
        /*if (guiApp.menuIsPresent)   NOTE TO MYSELF: Uncomment only after mouse
         * {                                            pointer and cursor keys work
         * gtkShow( ivHidePopUpMenu,NULL );            with this menu from skin as
         * uiMenuShow( 0,0 );                          they do with normal menus.
         * }
         * else*/gtkShow(ivShowPopUpMenu, NULL);
        break;

    case evIconify:

        switch (iparam) {
        case 0:
            wsWindowIconify(&guiApp.mainWindow);
            break;

        case 1:
            wsWindowIconify(&guiApp.videoWindow);
            break;
        }

        break;

    case evHalfSize:

        if (guiInfo.VideoWindow && guiInfo.Playing) {
            if (guiApp.videoWindow.isFullScreen) {
                uiFullScreen();
            }

            wsWindowResize(&guiApp.videoWindow, guiInfo.VideoWidth / 2, guiInfo.VideoHeight / 2);
            btnSet(evFullScreen, btnReleased);
        }

        break;

    case evDoubleSize:

        if (guiInfo.VideoWindow && guiInfo.Playing) {
            if (guiApp.videoWindow.isFullScreen) {
                uiFullScreen();
            }

            wsWindowResize(&guiApp.videoWindow, guiInfo.VideoWidth * 2, guiInfo.VideoHeight * 2);
            wsWindowMoveWithin(&guiApp.videoWindow, False, guiApp.video.x, guiApp.video.y);
            btnSet(evFullScreen, btnReleased);
        }

        break;

    case evNormalSize:

        if (guiInfo.VideoWindow && guiInfo.Playing) {
            if (guiApp.videoWindow.isFullScreen) {
                uiFullScreen();
            }

            wsWindowResize(&guiApp.videoWindow, guiInfo.VideoWidth, guiInfo.VideoHeight);
            btnSet(evFullScreen, btnReleased);
            break;
        } else if (!guiApp.videoWindow.isFullScreen)
            break;

    case evFullScreen:

        if (guiInfo.VideoWindow && (guiInfo.Playing || !iparam)) {
            uiFullScreen();

            if (!guiApp.videoWindow.isFullScreen)
                wsWindowResize(&guiApp.videoWindow, iparam ? guiInfo.VideoWidth : guiApp.video.width, iparam ? guiInfo.VideoHeight : guiApp.video.height);
        }

        if (guiApp.videoWindow.isFullScreen)
            btnSet(evFullScreen, btnPressed);
        else
            btnSet(evFullScreen, btnReleased);

        break;

    case evSetAspect:

        switch (iparam) {
        case 2:
            aspect = 16.0f / 9.0f;
            break;

        case 3:
            aspect = 4.0f / 3.0f;
            break;

        case 4:
            aspect = 2.35f;
            break;

        case 1:
        default:
            aspect = -1;
            break;
        }

        snprintf(cmd, sizeof(cmd), "pausing_keep switch_ratio %f", aspect);
        mp_input_queue_cmd(mp_input_parse_cmd(cmd));

        break;

    case evSetRotation:

        switch (iparam) {
        case 90:
            guiInfo.Rotation = 1;
            break;

        case -90:
            guiInfo.Rotation = 2;
            break;

        case 180:
            guiInfo.Rotation = 8;
            break;

        case 0:
        default:
            guiInfo.Rotation = -1;
            break;
        }

        guiInfo.MediumChanged = GUI_MEDIUM_SAME;

        break;

/* timer events */
    case ivRedraw:
    {
        unsigned int now = GetTimerMS();

        if ((now > last_redraw_time) &&
            (now < last_redraw_time + GUI_REDRAW_WAIT) &&
            !uiPlaybarFade && (iparam == 0))
            break;

        last_redraw_time = now;
    }
        uiMainRender = True;
        wsWindowRedraw(&guiApp.mainWindow);
        wsWindowRedraw(&guiApp.playbarWindow);
        break;

/* system events */
    case evNone:
        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[actions] uiEvent: evNone\n");
        break;

    default:
        mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[actions] uiEvent: unknown event %d, param %.2f\n", ev, param);
        break;
    }
}