Make AddMenuItem(), AddSubMenu() and AddSeparator() static.
[mplayer.git] / mplayer.c
index ca4c0bb..f73ff06 100644 (file)
--- a/mplayer.c
+++ b/mplayer.c
@@ -360,9 +360,11 @@ void *mpctx_get_mixer(MPContext *mpctx)
     return &mpctx->mixer;
 }
 
-int mpctx_get_global_sub_size(MPContext *mpctx)
+void mpctx_get_global_sub_info(MPContext *mpctx, int *size, int *pos)
 {
-    return mpctx->global_sub_size;
+    mp_property_do("sub", M_PROPERTY_GET, pos, mpctx);
+
+    if (size) *size = mpctx->global_sub_size;
 }
 
 int mpctx_get_osd_function(MPContext *mpctx)
@@ -370,6 +372,16 @@ int mpctx_get_osd_function(MPContext *mpctx)
     return mpctx->osd_function;
 }
 
+void *mpctx_get_stream(MPContext *mpctx)
+{
+    return mpctx->stream;
+}
+
+void *mpctx_get_afilter(MPContext *mpctx)
+{
+    return mpctx->sh_audio ? mpctx->sh_audio->afilter : NULL;
+}
+
 static int is_valid_metadata_type(metadata_t type)
 {
     switch (type) {
@@ -455,8 +467,8 @@ char *get_metadata(metadata_t type)
         return mp_asprintf("%d x %d", sh_video->disp_w, sh_video->disp_h);
 
     case META_AUDIO_CODEC:
-        if (sh_audio->codec && sh_audio->codec->name)
-            return strdup(sh_audio->codec->name);
+        if (sh_audio->codec && sh_audio->codec->name_idx)
+            return strdup(codec_idx2str(sh_audio->codec->name_idx));
         break;
 
     case META_AUDIO_BITRATE:
@@ -499,7 +511,7 @@ static void print_file_properties(const MPContext *mpctx, const char *filename)
     double video_start_pts = MP_NOPTS_VALUE;
     mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_FILENAME=%s\n",
            filename_recode(filename));
-    mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DEMUXER=%s\n", mpctx->demuxer->desc->name);
+    mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_DEMUXER=%s\n", mpctx->demuxer ? mpctx->demuxer->desc->name : "none");
     if (mpctx->sh_video) {
         /* Assume FOURCC if all bytes >= 0x20 (' ') */
         if (mpctx->sh_video->format >= 0x20202020)
@@ -533,7 +545,7 @@ static void print_file_properties(const MPContext *mpctx, const char *filename)
         mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_START_TIME=%.2f\n", start_pts);
     else
         mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_START_TIME=unknown\n");
-    mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_LENGTH=%.2f\n", demuxer_get_time_length(mpctx->demuxer));
+    mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_LENGTH=%.2f\n", mpctx->demuxer ? demuxer_get_time_length(mpctx->demuxer) : 0);
     mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_SEEKABLE=%d\n",
            mpctx->stream->seek && (!mpctx->demuxer || mpctx->demuxer->seekable));
     if (mpctx->demuxer) {
@@ -568,10 +580,6 @@ void uninit_player(unsigned int mask)
         current_module     = "uninit_acodec";
         if (mpctx->sh_audio)
             uninit_audio(mpctx->sh_audio);
-#ifdef CONFIG_GUI
-        if (use_gui)
-            gui(GUI_SET_AFILTER, NULL);
-#endif
         mpctx->sh_audio      = NULL;
         mpctx->mixer.afilter = NULL;
     }
@@ -710,22 +718,7 @@ void exit_player_with_rc(enum exit_reason how, int rc)
     vo_uninit(); // Close the X11 connection (if any is open).
 #endif
 
-#ifdef CONFIG_FREETYPE
-    current_module = "uninit_font";
-    if (sub_font && sub_font != vo_font)
-        free_font_desc(sub_font);
-    sub_font = NULL;
-    if (vo_font)
-        free_font_desc(vo_font);
-    vo_font = NULL;
-    done_freetype();
-#endif
-    free_osd_list();
-
-#ifdef CONFIG_ASS
-    ass_library_done(ass_library);
-    ass_library = NULL;
-#endif
+    common_uninit();
 
     current_module = "exit_player";
 
@@ -762,6 +755,8 @@ void exit_player_with_rc(enum exit_reason how, int rc)
         m_config_free(mconfig);
     mconfig = NULL;
 
+    mp_msg_uninit();
+
     exit(rc);
 }
 
@@ -777,6 +772,9 @@ static void child_sighandler(int x)
     do {
         pid = waitpid(-1, NULL, WNOHANG);
     } while (pid > 0);
+    // Without this, we will be called only once at
+    // least on Linux 3.16.
+    signal(SIGCHLD, child_sighandler);
 }
 
 #endif
@@ -1074,11 +1072,12 @@ void add_subtitles(char *filename, float fps, int noerr)
     if (filename == NULL || mpctx->set_of_sub_size >= MAX_SUBTITLE_FILES)
         return;
 
+    enca_sub_cp = NULL;
     subd = sub_read_file(filename, fps);
 #ifdef CONFIG_ASS
     if (ass_enabled)
 #ifdef CONFIG_ICONV
-        asst = ass_read_stream(ass_library, filename, sub_cp);
+        asst = ass_read_stream(ass_library, filename, (enca_sub_cp ? enca_sub_cp : sub_cp));
 #else
         asst = ass_read_stream(ass_library, filename, 0);
 #endif
@@ -1327,10 +1326,6 @@ static int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data)
     int new_srate;
     int result;
     if (!sh_audio) {
-#ifdef CONFIG_GUI
-        if (use_gui)
-            gui(GUI_SET_AFILTER, NULL);
-#endif
         mpctx->mixer.afilter = NULL;
         return 0;
     }
@@ -1352,10 +1347,6 @@ static int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data)
     result = init_audio_filters(sh_audio, new_srate,
                                 &ao_data->samplerate, &ao_data->channels, &ao_data->format);
     mpctx->mixer.afilter = sh_audio->afilter;
-#ifdef CONFIG_GUI
-    if (use_gui)
-        gui(GUI_SET_AFILTER, sh_audio->afilter);
-#endif
     return result;
 }
 
@@ -1518,7 +1509,7 @@ void set_osd_bar(int type, const char *name, double min, double max, double val)
         return;
 
     if (mpctx->sh_video) {
-        osd_visible = (GetTimerMS() + 1000) | 1;
+        osd_visible = (GetTimerMS() + osd_duration) | 1;
         vo_osd_progbar_type  = type;
         vo_osd_progbar_value = 256 * (val - min) / (max - min);
         vo_osd_changed(OSDTYPE_PROGBAR);
@@ -1786,9 +1777,10 @@ static int generate_video_frame(sh_video_t *sh_video, demux_stream_t *d_video)
     int in_size;
     int hit_eof = 0;
     double pts;
+    double endpts;
 
     while (1) {
-        int drop_frame = check_framedrop(sh_video->frametime);
+        int drop_frame = 0;
         void *decoded_frame;
         current_module = "decode video";
         // XXX Time used in this call is not counted in any performance
@@ -1796,25 +1788,25 @@ static int generate_video_frame(sh_video_t *sh_video, demux_stream_t *d_video)
         if (vf_output_queued_frame(sh_video->vfilter))
             break;
         current_module = "video_read_frame";
-        in_size = ds_get_packet_pts(d_video, &start, &pts);
+        in_size = ds_get_packet_pts_endpts(d_video, &start, &pts, &endpts);
         if (in_size < 0) {
             // try to extract last frames in case of decoder lag
             in_size = 0;
             start   = NULL;
             pts     = MP_NOPTS_VALUE;
             hit_eof = 1;
-            drop_frame = 0;
-        }
+        } else
+           drop_frame = check_framedrop(sh_video->frametime);
         if (in_size > max_framesize)
             max_framesize = in_size;
         current_module = "decode video";
-        decoded_frame  = decode_video(sh_video, start, in_size, drop_frame, pts, NULL);
+        decoded_frame  = decode_video(sh_video, start, in_size, drop_frame, pts, endpts, NULL);
         if (decoded_frame) {
             update_subtitles(sh_video, sh_video->pts, mpctx->d_sub, 0);
             update_teletext(sh_video, mpctx->demuxer, 0);
             update_osd_msg();
             current_module = "filter video";
-            if (filter_video(sh_video, decoded_frame, sh_video->pts))
+            if (filter_video(sh_video, decoded_frame, sh_video->pts, sh_video->endpts))
                 break;
         } else if (drop_frame)
             return -1;
@@ -1836,7 +1828,7 @@ static float timing_sleep(float time_frame)
         current_module = "sleep_rtc";
         while (time_frame > 0.000) {
             unsigned long rtc_ts;
-            if (read(rtc_fd, &rtc_ts, sizeof(rtc_ts)) <= 0)
+            if (read(rtc_fd, &rtc_ts, sizeof(rtc_ts)) != sizeof(rtc_ts))
                 mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_LinuxRTCReadError, strerror(errno));
             time_frame -= GetRelativeTime();
         }
@@ -1950,15 +1942,12 @@ static void mp_dvdnav_reset_stream(MPContext *ctx)
         ctx->demuxer->stream_pts = MP_NOPTS_VALUE;
     }
 
-    if (ctx->sh_audio) {
-        // free audio packets and reset
-        ds_free_packs(ctx->d_audio);
-        audio_delay -= ctx->sh_audio->stream_delay;
-        ctx->delay   = -audio_delay;
-        ctx->audio_out->reset();
-        resync_audio_stream(ctx->sh_audio);
-    }
-
+    // This is necessary to make video start in sync after
+    // a still frame. But do not discard pending audio packets,
+    // that causes issues since this code is also called on
+    // title changes (which is possibly a bug in itself *sigh*),
+    // and thus cause tiny audio skips.
+    ctx->delay   = -audio_delay;
     audio_delay = 0.0f;
     mpctx->sub_counts[SUB_SOURCE_DEMUX] = mp_dvdnav_number_of_subs(mpctx->stream);
     if (dvdsub_lang && dvdsub_id == dvdsub_lang_id) {
@@ -2135,7 +2124,6 @@ static int fill_audio_out_buffers(void)
     int playflags = 0;
     int audio_eof = 0;
     int bytes_to_write;
-    int format_change = 0;
     int timeout = 0;
     sh_audio_t *const sh_audio = mpctx->sh_audio;
 
@@ -2174,11 +2162,11 @@ static int fill_audio_out_buffers(void)
         // Fill buffer if needed:
         current_module = "decode_audio";
         t = GetTimer();
-        if (!format_change) {
+        if (!sh_audio->a_buffer_format_change) {
             res = mp_decode_audio(sh_audio, playsize);
-            format_change = res == -2;
+            sh_audio->a_buffer_format_change = res == -2;
         }
-        if (!format_change && res < 0) // EOF or error
+        if (!sh_audio->a_buffer_format_change && res < 0) // EOF or error
             if (mpctx->d_audio->eof) {
                 audio_eof = 1;
                 if (sh_audio->a_out_buffer_len == 0)
@@ -2189,7 +2177,7 @@ static int fill_audio_out_buffers(void)
         audio_time_usage += tt;
         if (playsize > sh_audio->a_out_buffer_len) {
             playsize = sh_audio->a_out_buffer_len;
-            if (audio_eof || format_change)
+            if (audio_eof || sh_audio->a_buffer_format_change)
                 playflags |= AOPLAY_FINAL_CHUNK;
         }
         if (!playsize)
@@ -2209,16 +2197,21 @@ static int fill_audio_out_buffers(void)
             memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize],
                     sh_audio->a_out_buffer_len);
             mpctx->delay += playback_speed * playsize / (double)ao_data.bps;
-        } else if ((format_change || audio_eof) && mpctx->audio_out->get_delay() < .04) {
+        } else if ((sh_audio->a_buffer_format_change || audio_eof) &&
+                   mpctx->audio_out->get_delay() < .04) {
             // Sanity check to avoid hanging in case current ao doesn't output
             // partial chunks and doesn't check for AOPLAY_FINAL_CHUNK
             mp_msg(MSGT_CPLAYER, MSGL_WARN, MSGTR_AudioOutputTruncated);
             sh_audio->a_out_buffer_len = 0;
         }
     }
-    if (format_change) {
+    if (sh_audio->a_buffer_format_change && !sh_audio->a_out_buffer_len) {
         uninit_player(INITIALIZED_AO);
+        af_uninit(sh_audio->afilter);
+        free(sh_audio->afilter);
+        sh_audio->afilter = NULL;
         reinit_audio_chain();
+        sh_audio->a_buffer_format_change = 0;
     }
     return 1;
 }
@@ -2316,6 +2309,27 @@ int reinit_video_chain(void)
 {
     sh_video_t *const sh_video = mpctx->sh_video;
     double ar = -1.0;
+    if (!video_read_properties(mpctx->sh_video)) {
+        mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_CannotReadVideoProperties);
+        goto err_out;
+    } else {
+        mp_msg(MSGT_CPLAYER, MSGL_V, MSGTR_FilefmtFourccSizeFpsFtime,
+               mpctx->demuxer->file_format, mpctx->sh_video->format, mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
+               mpctx->sh_video->fps, mpctx->sh_video->frametime
+               );
+
+        /* need to set fps here for output encoders to pick it up in their init */
+        if (force_fps) {
+            mpctx->sh_video->fps       = force_fps;
+            mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
+        }
+        vo_fps = mpctx->sh_video->fps;
+
+        if (!mpctx->sh_video->fps && !force_fps && !correct_pts) {
+            mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_FPSnotspecified);
+            correct_pts = 1;
+        }
+    }
     //================== Init VIDEO (codec & libvo) ==========================
     if (!fixed_vo || !(initialized_flags & INITIALIZED_VO)) {
         current_module = "preinit_libvo";
@@ -2394,7 +2408,7 @@ int reinit_video_chain(void)
     initialized_flags |= INITIALIZED_VCODEC;
 
     if (sh_video->codec)
-        mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_CODEC=%s\n", sh_video->codec->name);
+        mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_CODEC=%s\n", codec_idx2str(sh_video->codec->name_idx));
 
     sh_video->last_pts = MP_NOPTS_VALUE;
     sh_video->num_buffered_pts = 0;
@@ -2422,6 +2436,13 @@ err_out:
     return 0;
 }
 
+static void advance_timer(double amount)
+{
+    mpctx->sh_video->timer += amount;
+    if (mpctx->sh_audio)
+        mpctx->delay -= amount;
+}
+
 static double update_video(int *blit_frame)
 {
     sh_video_t *const sh_video = mpctx->sh_video;
@@ -2471,15 +2492,13 @@ static double update_video(int *blit_frame)
             // still frame has been reached, no need to decode
             if ((in_size > 0 || flush) && !decoded_frame)
             decoded_frame = decode_video(sh_video, start, in_size, drop_frame,
-                                         sh_video->pts, &full_frame);
+                                         sh_video->pts, sh_video->endpts, &full_frame);
 
             if (flush && !decoded_frame)
                 return -1;
 
             if (full_frame) {
-                sh_video->timer += frame_time;
-                if (mpctx->sh_audio)
-                    mpctx->delay -= frame_time;
+                advance_timer(frame_time);
                 // video_read_frame can change fps (e.g. for ASF video)
                 vo_fps = sh_video->fps;
                 update_subtitles(sh_video, sh_video->pts, mpctx->d_sub, 0);
@@ -2494,13 +2513,15 @@ static double update_video(int *blit_frame)
 
         current_module = "filter_video";
         *blit_frame    = (decoded_frame && filter_video(sh_video, decoded_frame,
-                                                        sh_video->pts));
+                                                        sh_video->pts, sh_video->endpts));
     } else {
         int res = generate_video_frame(sh_video, mpctx->d_video);
         if (!res)
             return -1;
         ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter,
                                                       VFCTRL_GET_PTS, &sh_video->pts);
+        ((vf_instance_t *)sh_video->vfilter)->control(sh_video->vfilter,
+                                                      VFCTRL_GET_ENDPTS, &sh_video->endpts);
         if (sh_video->pts == MP_NOPTS_VALUE) {
             mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_PtsAfterFiltersMissing);
             sh_video->pts = sh_video->last_pts;
@@ -2518,12 +2539,12 @@ static double update_video(int *blit_frame)
             mp_msg(MSGT_CPLAYER, MSGL_V, "pts value < previous\n");
         }
         frame_time = sh_video->pts - sh_video->last_pts;
-        if (!frame_time)
+        // The first frame should be displayed directly,
+        // all others should get a default frame_time
+        if (!frame_time && mpctx->startup_decode_retry == 0)
             frame_time = sh_video->frametime;
         sh_video->last_pts = sh_video->pts;
-        sh_video->timer   += frame_time;
-        if (mpctx->sh_audio)
-            mpctx->delay -= frame_time;
+        advance_timer(frame_time);
         *blit_frame = res > 0;
     }
     return frame_time;
@@ -2743,7 +2764,7 @@ static int seek(MPContext *mpctx, double amount, int style)
     }
 
 #ifdef CONFIG_ASS
-    if (ass_enabled && mpctx->d_sub->sh && ((sh_sub_t *)mpctx->d_sub->sh)->ass_track)
+    if (ass_enabled && mpctx->d_sub && mpctx->d_sub->sh && ((sh_sub_t *)mpctx->d_sub->sh)->ass_track)
         ass_flush_events(((sh_sub_t *)mpctx->d_sub->sh)->ass_track);
 #endif
 
@@ -2805,21 +2826,19 @@ int main(int argc, char *argv[])
 
     mpctx->playtree = m_config_parse_mp_command_line(mconfig, argc, argv);
     if (mpctx->playtree == NULL) {
+        // parse error
         opt_exit = 1;
-    } else {
+    } else
         mpctx->playtree = play_tree_cleanup(mpctx->playtree);
-        if (mpctx->playtree) {
-            mpctx->playtree_iter = play_tree_iter_new(mpctx->playtree, mconfig);
-            if (mpctx->playtree_iter) {
-                if (play_tree_iter_step(mpctx->playtree_iter, 0, 0) != PLAY_TREE_ITER_ENTRY) {
-                    play_tree_iter_free(mpctx->playtree_iter);
-                    mpctx->playtree_iter = NULL;
-                }
-                filename = play_tree_iter_get_file(mpctx->playtree_iter, 1);
-            }
-        }
+
+    mpctx->playtree_iter = mpctx->playtree ? play_tree_iter_new(mpctx->playtree, mconfig) : NULL;
+    if (mpctx->playtree_iter && play_tree_iter_step(mpctx->playtree_iter, 0, 0) != PLAY_TREE_ITER_ENTRY) {
+        play_tree_iter_free(mpctx->playtree_iter);
+        mpctx->playtree_iter = NULL;
     }
 
+    filename = mpctx->playtree_iter ? play_tree_iter_get_file(mpctx->playtree_iter, 1) : NULL;
+
     print_version("MPlayer");
 #if (defined(__MINGW32__) || defined(__CYGWIN__)) && defined(CONFIG_GUI)
     void *runningmplayer = FindWindow("MPlayer GUI for Windows", "MPlayer for Windows");
@@ -3023,6 +3042,8 @@ int main(int argc, char *argv[])
 
     // Catch signals
 #ifndef __MINGW32__
+    // TODO: use newer POSIX SIG_IGN behaviour instead to
+    // automatically handle children?
     signal(SIGCHLD, child_sighandler);
 #endif
 
@@ -3057,7 +3078,7 @@ int main(int argc, char *argv[])
     if (use_gui) {
         guiInit();
         gui(GUI_SET_CONTEXT, mpctx);
-        gui(GUI_SET_STATE, (void *)(filename ? GUI_PLAY : GUI_STOP));
+        gui(GUI_SET_STATE, (void *)(intptr_t)(filename ? GUI_PLAY : GUI_STOP));
     }
 #endif
 
@@ -3099,7 +3120,7 @@ play_next_file:
                 if (cmd->id == MP_CMD_GUI)
                     gui(GUI_RUN_MESSAGE, cmd->args[0].v.s);
                 else
-                    gui(GUI_RUN_COMMAND, (void *)cmd->id);
+                    gui(GUI_RUN_COMMAND, (void *)(intptr_t)cmd->id);
                 mp_cmd_free(cmd);
             }
         }
@@ -3309,6 +3330,15 @@ play_next_file:
             dvdsub_id = bd_sid_from_lang(mpctx->stream, dvdsub_lang);
     }
 
+#ifdef CONFIG_LIBBLURAY
+    if (mpctx->stream->type == STREAMTYPE_BLURAY) {
+        if (audio_lang && audio_id == -1)
+            audio_id = bluray_id_from_lang(mpctx->stream, stream_ctrl_audio, audio_lang);
+        if (dvdsub_lang && dvdsub_id == -1)
+            dvdsub_id = bluray_id_from_lang(mpctx->stream, stream_ctrl_sub, dvdsub_lang);
+    }
+#endif
+
 #ifdef CONFIG_DVDREAD
     if (mpctx->stream->type == STREAMTYPE_DVD) {
         current_module = "dvd lang->id";
@@ -3426,6 +3456,7 @@ goto_enable_cache:
             if (extract_embedded_fonts &&
                 att->name && att->type && att->data && att->data_size &&
                 (strcmp(att->type, "application/x-truetype-font") == 0 ||
+                 strcmp(att->type, "application/vnd.ms-opentype") == 0 ||
                  strcmp(att->type, "application/x-font") == 0))
                 ass_add_font(ass_library, att->name, att->data, att->data_size);
         }
@@ -3442,6 +3473,10 @@ goto_enable_cache:
         int tmp = ts_prog;
         mp_property_do("switch_program", M_PROPERTY_SET, &tmp, mpctx);
     }
+
+    // select video stream
+    select_video(mpctx->demuxer, video_id);
+
     // select audio stream
     select_audio(mpctx->demuxer, audio_id, audio_lang);
 
@@ -3463,10 +3498,12 @@ goto_enable_cache:
         // disable other streams:
         if (mpctx->d_audio && mpctx->d_audio != ds) {
             ds_free_packs(mpctx->d_audio);
+            select_audio(mpctx->demuxer, -2, NULL);
             mpctx->d_audio->id = -2;
         }
         if (mpctx->d_video && mpctx->d_video != ds) {
             ds_free_packs(mpctx->d_video);
+            select_video(mpctx->demuxer, -2);
             mpctx->d_video->id = -2;
         }
         if (mpctx->d_sub && mpctx->d_sub != ds) {
@@ -3480,6 +3517,10 @@ goto_enable_cache:
             exit_player(EXIT_ERROR);
         }
         stream_dump_progress_start();
+
+        // force retry in case bad interleaving caused EOF before.
+        ds->fill_count = 0;
+        ds->eof = 0;
         while (!ds->eof) {
             unsigned char *start;
             double pts;
@@ -3510,27 +3551,7 @@ goto_enable_cache:
 
     if (mpctx->sh_video) {
         current_module = "video_read_properties";
-        if (!video_read_properties(mpctx->sh_video)) {
-            mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_CannotReadVideoProperties);
-            mpctx->sh_video = mpctx->d_video->sh = NULL;
-        } else {
-            mp_msg(MSGT_CPLAYER, MSGL_V, MSGTR_FilefmtFourccSizeFpsFtime,
-                   mpctx->demuxer->file_format, mpctx->sh_video->format, mpctx->sh_video->disp_w, mpctx->sh_video->disp_h,
-                   mpctx->sh_video->fps, mpctx->sh_video->frametime
-                   );
-
-            /* need to set fps here for output encoders to pick it up in their init */
-            if (force_fps) {
-                mpctx->sh_video->fps       = force_fps;
-                mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
-            }
-            vo_fps = mpctx->sh_video->fps;
-
-            if (!mpctx->sh_video->fps && !force_fps && !correct_pts) {
-                mp_msg(MSGT_CPLAYER, MSGL_ERR, MSGTR_FPSnotspecified);
-                correct_pts = 1;
-            }
-        }
+        reinit_video_chain();
     }
 
     if (!mpctx->sh_video && !mpctx->sh_audio) {
@@ -3596,9 +3617,6 @@ goto_enable_cache:
         }
     }
 
-    if (mpctx->sh_video)
-        reinit_video_chain();
-
     if (mpctx->sh_video) {
         if (vo_flags & 0x08 && vo_spudec)
             spudec_set_hw_spu(vo_spudec, mpctx->video_out);
@@ -3635,7 +3653,9 @@ goto_enable_cache:
         if (mpctx->sh_audio) {
             reinit_audio_chain();
             if (mpctx->sh_audio && mpctx->sh_audio->codec)
-                mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_CODEC=%s\n", mpctx->sh_audio->codec->name);
+                mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_CODEC=%s\n", codec_idx2str(mpctx->sh_audio->codec->name_idx));
+            if (mpctx->audio_out)
+                mpctx->audio_out->control(AOCONTROL_FILENAME, (void *)(vo_wintitle ? vo_wintitle : mp_basename(filename)));
         }
 
         current_module = "av_init";
@@ -3664,7 +3684,7 @@ goto_enable_cache:
             mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_Video_NoVideo);
             mp_msg(MSGT_CPLAYER, MSGL_V, "Freeing %d unused video chunks.\n", mpctx->d_video->packs);
             ds_free_packs(mpctx->d_video);
-            mpctx->d_video->id = -2;
+            //mpctx->d_video->id = -2;
             //if(!fixed_vo) uninit_player(INITIALIZED_VO);
         }
 
@@ -3743,6 +3763,15 @@ goto_enable_cache:
                 mpctx->sh_audio->ds = mpctx->d_audio;
                 reinit_audio_chain();
             }
+            // Note: the video_id != -2 is only there because
+            // some demuxers do not have support for disabling
+            // video.
+            if (video_id != -2 && mpctx->d_video->id != -2 &&
+                !mpctx->sh_video && mpctx->d_video->sh) {
+                mpctx->sh_video     = mpctx->d_video->sh;
+                mpctx->sh_video->ds = mpctx->d_video;
+                reinit_video_chain();
+            }
 
 /*========================== PLAY AUDIO ============================*/
 
@@ -3769,7 +3798,7 @@ goto_enable_cache:
                 update_osd_msg();
             } else {
                 int frame_time_remaining = 0;
-                int blit_frame = 1;
+                int blit_frame = mpctx->num_buffered_frames > 0;
                 // skip timing after seek
                 int skip_timing = mpctx->startup_decode_retry > 0;
 
@@ -3796,11 +3825,26 @@ goto_enable_cache:
                         goto goto_next_file;
                     }
                     if (frame_time < 0) {
+                        // try to figure out duration of last frame
+                        if (correct_pts && mpctx->sh_video->endpts != MP_NOPTS_VALUE &&
+                            mpctx->sh_video->pts != MP_NOPTS_VALUE &&
+                            mpctx->sh_video->endpts > mpctx->sh_video->pts) {
+                            frame_time = mpctx->sh_video->endpts - mpctx->sh_video->pts;
+                        } else {
+                            frame_time = mpctx->sh_video->frametime;
+                        }
+                        // mark as last frame. Note: needs to be reset on seeking
+                        mpctx->sh_video->frametime = -1;
+                        mpctx->sh_video->endpts = MP_NOPTS_VALUE;
+                    }
+                    if (frame_time < 0) {
                         // if we have no more video, sleep some arbitrary time
                         frame_time = 1.0 / 20.0;
+                        // Ensure vo_pts is updated so that ao_pcm will not hang.
+                        advance_timer(frame_time);
                         // only stop playing when audio is at end as well
-                        if (!mpctx->sh_audio || mpctx->d_audio->eof)
-                            mpctx->eof = 1;
+                        if (!mpctx->sh_audio || (mpctx->d_audio->eof && !ds_fill_buffer(mpctx->d_audio)))
+                            mpctx->eof = mpctx->time_frame <= 0;
                     } else {
                         // might return with !eof && !blit_frame if !correct_pts
                         mpctx->num_buffered_frames += blit_frame;
@@ -3906,6 +3950,9 @@ goto_enable_cache:
                     // clear highlight
                     if (vo_spudec)
                         spudec_apply_palette_crop(vo_spudec, 0, 0, 0, 0, 0);
+                    osd_set_nav_box(0, 0, 0, 0);
+                    vo_osd_changed(OSDTYPE_DVDNAV);
+                    vo_osd_changed(OSDTYPE_SPU);
                     if (mpctx->sh_video &&
                         stream_control(mpctx->demuxer->stream,
                                        STREAM_CTRL_GET_ASPECT_RATIO, &ar)
@@ -3986,6 +4033,9 @@ goto_enable_cache:
                     }
                 }
 
+                // reset last frame marker
+                if (mpctx->sh_video && mpctx->sh_video->frametime < 0)
+                    mpctx->sh_video->frametime = 1.0f / mpctx->sh_video->fps;
                 rel_seek_secs = 0;
                 abs_seek_pos  = 0;
                 loop_seek     = 0;
@@ -4000,18 +4050,19 @@ goto_enable_cache:
                 } else {
                     guiInfo.Position = demuxer_get_percent_pos(mpctx->demuxer);
                 }
+                guiInfo.ElapsedTime = -1;
                 if (mpctx->sh_video)
                     guiInfo.ElapsedTime = mpctx->sh_video->pts;
-                else if (mpctx->sh_audio)
+                if (guiInfo.ElapsedTime < 0 && mpctx->sh_audio)
                     guiInfo.ElapsedTime = playing_audio_pts(mpctx->sh_audio, mpctx->d_audio, mpctx->audio_out);
                 guiInfo.RunningTime = demuxer_get_time_length(mpctx->demuxer);
-                gui(GUI_SET_MIXER, 0);
+                gui(GUI_SET_VOLUME_BALANCE, &mpctx->mixer);
                 gui(GUI_REDRAW, 0);
                 if (guiInfo.Playing == GUI_STOP)
                     break;                  // STOP
                 if (guiInfo.Playing == GUI_PAUSE)
                     mpctx->osd_function = OSD_PAUSE;
-                if (guiInfo.NewPlay)
+                if (guiInfo.MediumChanged)
                     goto goto_next_file;
 #ifdef CONFIG_DVDREAD
                 if (mpctx->stream->type == STREAMTYPE_DVD) {
@@ -4099,19 +4150,18 @@ goto_next_file:  // don't jump here after ao/vo/getch initialization!
 
     while (mpctx->playtree_iter != NULL) {
         filename = play_tree_iter_get_file(mpctx->playtree_iter, mpctx->eof);
-        if (filename == NULL) {
-            if (play_tree_iter_step(mpctx->playtree_iter, mpctx->eof, 0) != PLAY_TREE_ITER_ENTRY) {
-                play_tree_iter_free(mpctx->playtree_iter);
-                mpctx->playtree_iter = NULL;
-            }
-        } else
+        if (filename)
             break;
+        if (play_tree_iter_step(mpctx->playtree_iter, mpctx->eof, 0) != PLAY_TREE_ITER_ENTRY) {
+            play_tree_iter_free(mpctx->playtree_iter);
+            mpctx->playtree_iter = NULL;
+        }
     }
 
 #ifdef CONFIG_GUI
     if (use_gui)
-        if (guiInfo.NewPlay != GUI_FILE_SAME)
-            gui(GUI_END_FILE, 0);
+        if (guiInfo.MediumChanged != GUI_MEDIUM_SAME)
+            gui(GUI_END_PLAY, 0);
 #endif
 
     if (