static void draw_frame(struct vo *vo, struct vo_frame *frame) { struct vo_priv *p = vo->priv; pthread_mutex_lock(&p->ctx->lock); talloc_free(p->ctx->waiting_frame); p->ctx->waiting_frame = vo_frame_ref(frame); pthread_mutex_unlock(&p->ctx->lock); }
static void draw_frame(struct vo *vo, struct vo_frame *frame) { struct vo_priv *p = vo->priv; pthread_mutex_lock(&p->ctx->lock); assert(!p->ctx->next_frame); p->ctx->next_frame = vo_frame_ref(frame); p->ctx->expected_flip_count = p->ctx->flip_count + 1; p->ctx->redrawing = frame->redraw || !frame->current; update(p); pthread_mutex_unlock(&p->ctx->lock); }
void write_video(struct MPContext *mpctx, double endpts) { struct MPOpts *opts = mpctx->opts; struct vo *vo = mpctx->video_out; if (!mpctx->d_video) return; // Actual playback starts when both audio and video are ready. if (mpctx->video_status == STATUS_READY) return; if (mpctx->paused && mpctx->video_status >= STATUS_READY) return; int r = video_output_image(mpctx, endpts); MP_TRACE(mpctx, "video_output_image: %d\n", r); if (r < 0) goto error; if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode. return; if (r == VD_EOF) { mpctx->video_status = vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF; mpctx->delay = 0; mpctx->last_av_difference = 0; MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status); return; } if (mpctx->video_status > STATUS_PLAYING) mpctx->video_status = STATUS_PLAYING; if (r != VD_NEW_FRAME) { mpctx->sleeptime = 0; // Decode more in next iteration. return; } // Filter output is different from VO input? struct mp_image_params p = mpctx->next_frames[0]->params; if (!vo->params || !mp_image_params_equal(&p, vo->params)) { // Changing config deletes the current frame; wait until it's finished. if (vo_still_displaying(vo)) return; const struct vo_driver *info = mpctx->video_out->driver; char extra[20] = {0}; if (p.w != p.d_w || p.h != p.d_h) snprintf(extra, sizeof(extra), " => %dx%d", p.d_w, p.d_h); MP_INFO(mpctx, "VO: [%s] %dx%d%s %s\n", info->name, p.w, p.h, extra, vo_format_name(p.imgfmt)); MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description); int vo_r = vo_reconfig(vo, &p, 0); if (vo_r < 0) { mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED; goto error; } init_vo(mpctx); } mpctx->time_frame -= get_relative_time(mpctx); update_avsync_before_frame(mpctx); double time_frame = MPMAX(mpctx->time_frame, -1); int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6); // wait until VO wakes us up to get more frames if (!vo_is_ready_for_frame(vo, pts)) { if (video_feed_async_filter(mpctx) < 0) goto error; return; } assert(mpctx->num_next_frames >= 1); struct vo_frame dummy = { .pts = pts, .duration = -1, .num_frames = mpctx->num_next_frames, }; for (int n = 0; n < dummy.num_frames; n++) dummy.frames[n] = mpctx->next_frames[n]; struct vo_frame *frame = vo_frame_ref(&dummy); double diff = -1; double vpts0 = mpctx->next_frames[0]->pts; double vpts1 = MP_NOPTS_VALUE; if (mpctx->num_next_frames >= 2) vpts1 = mpctx->next_frames[1]->pts; if (vpts0 != MP_NOPTS_VALUE && vpts1 != MP_NOPTS_VALUE) diff = vpts1 - vpts0; if (diff < 0 && mpctx->d_video->fps > 0) diff = 1.0 / mpctx->d_video->fps; // fallback to demuxer-reported fps if (opts->untimed || vo->driver->untimed) diff = -1; // disable frame dropping and aspects of frame timing if (diff >= 0) { // expected A/V sync correction is ignored diff /= opts->playback_speed; if (mpctx->time_frame < 0) diff += mpctx->time_frame; frame->duration = MPCLAMP(diff, 0, 10) * 1e6; } mpctx->video_pts = mpctx->next_frames[0]->pts; mpctx->last_vo_pts = mpctx->video_pts; mpctx->playback_pts = mpctx->video_pts; update_avsync_after_frame(mpctx); mpctx->osd_force_update = true; update_osd_msg(mpctx); update_subtitles(mpctx); vo_queue_frame(vo, frame); shift_frames(mpctx); // The frames were shifted down; "initialize" the new first entry. if (mpctx->num_next_frames >= 1) handle_new_frame(mpctx); mpctx->shown_vframes++; if (mpctx->video_status < STATUS_PLAYING) { mpctx->video_status = STATUS_READY; // After a seek, make sure to wait until the first frame is visible. vo_wait_frame(vo); MP_VERBOSE(mpctx, "first video frame after restart shown\n"); } screenshot_flip(mpctx); mp_notify(mpctx, MPV_EVENT_TICK, NULL); if (!mpctx->sync_audio_to_video) mpctx->video_status = STATUS_EOF; if (mpctx->video_status != STATUS_EOF) { if (mpctx->step_frames > 0) { mpctx->step_frames--; if (!mpctx->step_frames && !opts->pause) pause_player(mpctx); } if (mpctx->max_frames == 0 && !mpctx->stop_play) mpctx->stop_play = AT_END_OF_FILE; if (mpctx->max_frames > 0) mpctx->max_frames--; } mpctx->sleeptime = 0; return; error: MP_FATAL(mpctx, "Could not initialize video chain.\n"); uninit_video_chain(mpctx); error_on_track(mpctx, mpctx->current_track[STREAM_VIDEO][0]); handle_force_window(mpctx, true); mpctx->sleeptime = 0; }
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h) { assert(ctx->renderer); gl_video_set_gl_state(ctx->renderer); pthread_mutex_lock(&ctx->lock); struct vo *vo = ctx->active; ctx->force_update |= ctx->reconfigured; ctx->frozen = false; if (ctx->vp_w != vp_w || ctx->vp_h != vp_h) ctx->force_update = true; if (ctx->force_update && vo) { ctx->force_update = false; ctx->vp_w = vp_w; ctx->vp_h = vp_h; struct mp_rect src, dst; struct mp_osd_res osd; mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps, &ctx->img_params, vp_w, abs(vp_h), 1.0, &src, &dst, &osd); gl_video_resize(ctx->renderer, vp_w, vp_h, &src, &dst, &osd); } if (ctx->reconfigured) { gl_video_set_osd_source(ctx->renderer, vo ? vo->osd : NULL); gl_video_config(ctx->renderer, &ctx->img_params); } if (ctx->update_new_opts) { struct vo_priv *p = vo ? vo->priv : NULL; struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p; if (opts) { gl_video_set_options(ctx->renderer, opts->renderer_opts); gl_video_configure_queue(ctx->renderer, vo); ctx->gl->debug_context = opts->use_gl_debug; gl_video_set_debug(ctx->renderer, opts->use_gl_debug); frame_queue_shrink(ctx, opts->frame_queue_size); } } ctx->reconfigured = false; ctx->update_new_opts = false; struct mp_csp_equalizer *eq = gl_video_eq_ptr(ctx->renderer); if (ctx->eq_changed) { memcpy(eq->values, ctx->eq.values, sizeof(eq->values)); gl_video_eq_update(ctx->renderer); } ctx->eq_changed = false; ctx->eq = *eq; struct vo_frame *frame = frame_queue_pop(ctx); if (frame) { talloc_free(ctx->cur_frame); ctx->cur_frame = vo_frame_ref(frame); } else { frame = vo_frame_ref(ctx->cur_frame); } struct vo_frame dummy = {0}; if (!frame) frame = &dummy; if (ctx->approx_vsync > 0) { frame->prev_vsync = prev_sync(ctx, mp_time_us()); frame->next_vsync = frame->prev_vsync + ctx->approx_vsync; } pthread_mutex_unlock(&ctx->lock); gl_video_render_frame(ctx->renderer, frame, fbo); gl_video_unset_gl_state(ctx->renderer); if (frame != &dummy) talloc_free(frame); pthread_mutex_lock(&ctx->lock); const int left = ctx->queued_frames; if (vo && left > 0) update(vo->priv); pthread_mutex_unlock(&ctx->lock); return left; }
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h) { assert(ctx->renderer); gl_video_set_gl_state(ctx->renderer); pthread_mutex_lock(&ctx->lock); struct vo *vo = ctx->active; ctx->force_update |= ctx->reconfigured; if (ctx->vp_w != vp_w || ctx->vp_h != vp_h) ctx->force_update = true; if (ctx->force_update && vo) { ctx->force_update = false; ctx->vp_w = vp_w; ctx->vp_h = vp_h; struct mp_rect src, dst; struct mp_osd_res osd; mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps, &ctx->img_params, vp_w, abs(vp_h), 1.0, &src, &dst, &osd); gl_video_resize(ctx->renderer, vp_w, vp_h, &src, &dst, &osd); } if (ctx->reconfigured) { gl_video_set_osd_source(ctx->renderer, vo ? vo->osd : NULL); gl_video_config(ctx->renderer, &ctx->img_params); ctx->eq_changed = true; } if (ctx->update_new_opts) { struct vo_priv *p = vo ? vo->priv : NULL; struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p; if (opts) { gl_video_set_options(ctx->renderer, opts->renderer_opts); if (vo) gl_video_configure_queue(ctx->renderer, vo); ctx->gl->debug_context = opts->use_gl_debug; gl_video_set_debug(ctx->renderer, opts->use_gl_debug); } } ctx->reconfigured = false; ctx->update_new_opts = false; if (ctx->reset) { gl_video_reset(ctx->renderer); ctx->reset = false; if (ctx->cur_frame) ctx->cur_frame->still = true; } struct mp_csp_equalizer *eq = gl_video_eq_ptr(ctx->renderer); if (ctx->eq_changed) { memcpy(eq->values, ctx->eq.values, sizeof(eq->values)); gl_video_eq_update(ctx->renderer); } ctx->eq_changed = false; struct vo_frame *frame = ctx->next_frame; int64_t wait_present_count = ctx->present_count; if (frame) { ctx->next_frame = NULL; wait_present_count += 1; pthread_cond_signal(&ctx->wakeup); talloc_free(ctx->cur_frame); ctx->cur_frame = vo_frame_ref(frame); } else { frame = vo_frame_ref(ctx->cur_frame); if (frame) frame->redraw = true; MP_STATS(ctx, "glcb-noframe"); } struct vo_frame dummy = {0}; if (!frame) frame = &dummy; pthread_mutex_unlock(&ctx->lock); MP_STATS(ctx, "glcb-render"); gl_video_render_frame(ctx->renderer, frame, fbo); gl_video_unset_gl_state(ctx->renderer); if (frame != &dummy) talloc_free(frame); pthread_mutex_lock(&ctx->lock); while (wait_present_count > ctx->present_count) pthread_cond_wait(&ctx->wakeup, &ctx->lock); pthread_mutex_unlock(&ctx->lock); return 0; }