📄 video_renderer.cpp
字号:
// This file is part of Ambulant Player, www.ambulantplayer.org.//// Copyright (C) 2003-2007 Stichting CWI, // Kruislaan 413, 1098 SJ Amsterdam, The Netherlands.//// Ambulant Player is free software; you can redistribute it and/or modify// it under the terms of the GNU Lesser General Public License as published by// the Free Software Foundation; either version 2.1 of the License, or// (at your option) any later version.//// Ambulant Player is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Lesser General Public License for more details.//// You should have received a copy of the GNU Lesser General Public License// along with Ambulant Player; if not, write to the Free Software// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA/* * @$Id: video_renderer.cpp,v 1.27 2007/02/12 14:14:48 jackjansen Exp $ */#include "ambulant/lib/logger.h"//#include "ambulant/lib/transition_info.h"#include "ambulant/common/video_renderer.h"//#include "ambulant/gui/none/none_gui.h"//#include "ambulant/net/datasource.h"//#define AM_DBG#ifndef AM_DBG#define AM_DBG if(0)#endifusing namespace ambulant;using namespace common;video_renderer::video_renderer( playable_notification *context, playable_notification::cookie_type cookie, const lib::node * node, lib::event_processor * evp, common::factories *factory): renderer_playable (context, cookie, node, evp), m_src(NULL), m_audio_ds(NULL), m_audio_renderer(NULL), m_timer(NULL), m_epoch(0), m_activated(false), m_is_paused(false), m_paused_epoch(0), m_last_frame_timestamp(-1), m_frame_displayed(0), m_frame_duplicate(0), m_frame_early(0), m_frame_late(0), m_frame_missing(0){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("video_renderer::video_renderer() (this = 0x%x): Constructor ", (void *) this); net::url url = node->get_url("src"); _init_clip_begin_end(); m_src = factory->get_datasource_factory()->new_video_datasource(url,m_clip_begin, m_clip_end); if (m_src == NULL) { lib::logger::get_logger()->warn(gettext("Cannot open video: %s"), url.get_url().c_str()); m_lock.leave(); return; } if (m_src->has_audio()) { m_audio_ds = m_src->get_audio_datasource(); if (m_audio_ds) { AM_DBG lib::logger::get_logger()->debug("active_video_renderer::active_video_renderer: creating audio renderer !"); m_audio_renderer = factory->get_playable_factory()->new_aux_audio_playable(context, cookie, node, evp, (net::audio_datasource*) m_audio_ds); //KB XXXX cast AM_DBG lib::logger::get_logger()->debug("active_video_renderer::active_video_renderer: audio renderer created(0x%x)!", (void*) m_audio_renderer); } else { m_audio_renderer = NULL; } } m_lock.leave();}video_renderer::~video_renderer() { // m_audio_ds released by audio renderer stop(); // releases m_src, m_audio_renderer (in most cases) AM_DBG lib::logger::get_logger()->debug("~video_renderer(0x%x)", (void*)this); m_lock.enter(); if (m_audio_renderer) m_audio_renderer->release(); if (m_src) m_src->release(); m_src = NULL; m_lock.leave();}voidvideo_renderer::start (double where){ m_lock.enter(); if (m_activated) { lib::logger::get_logger()->trace("video_renderer.start(0x%x): already started", (void*)this); m_lock.leave(); return; } if (!m_src) { lib::logger::get_logger()->trace("video_renderer.start: no datasource, skipping media item"); m_context->stopped(m_cookie, 0); m_lock.leave(); return; } if (!m_dest) { lib::logger::get_logger()->trace("video_renderer.start: no destination surface, skipping media item"); m_context->stopped(m_cookie, 0); m_lock.leave(); return; } m_activated = true;#if 1 m_timer = m_event_processor->get_timer();#else // XXX Note: comment below is possibly incorrect, but at the very least the // code does not work, because video_datasource::start_frame() assumes a shared clock. // // This is a workaround for a bug: the "normal" timer // can be set back in time sometimes, and the video renderer // does not like that. For now use a private timer, will // have to be fixed eventually. m_timer = lib::realtime_timer_factory();#endif // Now we need to define where we start playback. This depends on m_clip_begin (microseconds) // and where (seconds). We use these to set m_epoch (milliseconds) to the time (m_timer-based) // at which we would have played the frame with timestamp 0. assert(m_clip_begin >= 0); assert(where >= 0); m_epoch = m_timer->elapsed() - (long)(m_clip_begin/1000) - (int)(where*1000); lib::event * e = new dataavail_callback (this, &video_renderer::data_avail); AM_DBG lib::logger::get_logger ()->debug ("video_renderer::start(%f) this = 0x%x, cookie=%d, dest=0x%x, timer=0x%x, epoch=%d", where, (void *) this, (int)m_cookie, (void*)m_dest, m_timer, m_epoch); m_src->start_frame (m_event_processor, e, 0); if (m_audio_renderer) m_audio_renderer->start(where); m_dest->show(this); m_lock.leave();}voidvideo_renderer::stop(){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("video_renderer::stop() this=0x%x, dest=0x%x", (void *) this, (void*)m_dest); if (!m_activated) { lib::logger::get_logger()->trace("video_renderer.stop(0x%x): not started", (void*)this); m_lock.leave(); return; } m_context->stopped(m_cookie, 0); m_activated = false; if (m_dest) { m_dest->renderer_done(this); m_dest = NULL; } if (m_audio_renderer) { m_audio_renderer->stop(); m_audio_renderer->release(); m_audio_renderer = NULL; } if (m_src) { m_src->stop(); m_src->release(); m_src = NULL; } lib::logger::get_logger()->debug("video_renderer: displayed %d frames; skipped %d dups, %d late, %d early, %d NULL", m_frame_displayed, m_frame_duplicate, m_frame_late, m_frame_early, m_frame_missing); m_lock.leave();}voidvideo_renderer::seek(double t){ lib::logger::get_logger()->trace("video_renderer: seek(%f) not implemented", t);}common::duration video_renderer::get_dur(){ common::duration rv(false, 0.0); m_lock.enter(); // video is the important one so we ask the video source if (m_src) { rv = m_src->get_dur(); AM_DBG lib::logger::get_logger()->trace("video_renderer: get_dur() duration = %f", rv.second); } m_lock.leave(); return rv;}// now() returns the time in seconds !doublevideo_renderer::now() { assert( m_timer ); // private method - no locking double rv; unsigned long elapsed; if (m_is_paused) elapsed = m_paused_epoch; else elapsed = m_timer->elapsed(); if (elapsed < m_epoch) rv = 0; else rv = (double)(elapsed - m_epoch) / 1000; AM_DBG lib::logger::get_logger()->trace("video_renderer: now(0x%x): m_paused_epoch=%d, m_epoch=%d rv=%lf", this, m_paused_epoch, m_epoch, rv); return rv;}voidvideo_renderer::pause(pause_display d){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("video_renderer::pause() this=0x%x, dest=0x%x", (void *) this, (void*)m_dest); // XXX if d==display_hide we should hide the content if (m_activated && !m_is_paused) { if (m_audio_renderer) m_audio_renderer->pause(d); m_is_paused = true; m_paused_epoch = m_timer->elapsed(); } m_lock.leave();}voidvideo_renderer::resume(){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("video_renderer::resume() this=0x%x, dest=0x%x", (void *) this, (void*)m_dest); if (m_activated && m_is_paused) { if (m_audio_renderer) m_audio_renderer->resume(); m_is_paused = false; unsigned long int pause_length = m_timer->elapsed() - m_paused_epoch; m_epoch += pause_length; } m_lock.leave();}voidvideo_renderer::data_avail(){ m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail(this = 0x%x):", (void *) this); if (!m_activated || !m_src) { AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: returning (already shutting down)"); m_lock.leave(); return; } m_size.w = m_src->width(); m_size.h = m_src->height(); AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: size=(%d, %d)", m_size.w, m_size.h); // Get the next frame, dropping any frames whose timestamp has passed. char *buf = NULL; int size = 0; net::timestamp_t now_micros = (net::timestamp_t)(now()*1000000); net::timestamp_t frame_ts_micros; // Timestamp of frame in "buf" (in microseconds) buf = m_src->get_frame(now_micros, &frame_ts_micros, &size); net::timestamp_t frame_duration = m_src->frameduration(); // XXX For now: assume 30fps // If we are at the end of the clip we stop and signal the scheduler. if (m_src->end_of_file() || (m_clip_end > 0 && frame_ts_micros > m_clip_end)) { AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: stopping playback. eof=%d, ts=%lld, now=%lld, clip_end=%lld ", (int)m_src->end_of_file(), frame_ts_micros, now_micros, m_clip_end ); if (m_src) { m_src->stop(); m_src->release(); m_src = NULL; } m_lock.leave(); m_context->stopped(m_cookie, 0); //stop(); // XXX Attempt by Jack. I think this is really a bug in the scheduler, so it may need to go some time. return; } AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: buf=0x%x, size=%d, ts=%d, now=%d", (void *) buf, size, (int)frame_ts_micros, (int)now_micros); AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: frame_ts_micros=%lld (<=) now_micros(%lld) + frame_duration(%lld)= %lld", frame_ts_micros, now_micros, frame_duration, now_micros + frame_duration); // If we have a frame and it should be on-screen already we show it. // If the frame's timestamp is still in the future we fall through, and schedule another // callback at the time this frame is due. if (buf && (frame_ts_micros <= now_micros + (2*frame_duration)) && (frame_ts_micros >= m_clip_begin-frame_duration)) { // It could be we're displaying this frame already. In that case there's no point in // re-displaying. if (frame_ts_micros > m_last_frame_timestamp ) { AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: display frame (timestamp = %lld)",frame_ts_micros); show_frame(buf, size); m_dest->need_redraw(); m_last_frame_timestamp = frame_ts_micros; m_frame_displayed++; } else { m_frame_duplicate++; } m_src->frame_done(frame_ts_micros, true); // Now we need to decide when we want the next callback, by computing what the timestamp // of the next frame is expected to be. if(!(frame_ts_micros < (now_micros - frame_duration)))//if the current frames time was older than one frameduration don't increment the time to callback frame_ts_micros += frame_duration; } else if (frame_ts_micros <= now_micros - frame_duration) { m_frame_late++; AM_DBG lib::logger::get_logger()->debug("video_renderer: skip late frame, ts=%lld, now+dur=%lld", frame_ts_micros, now_micros+frame_duration); } else if (frame_ts_micros >= m_clip_begin-frame_duration) { AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: frame early ! (timestamp = %lld, start_time = %lld, diff = %lld)",frame_ts_micros, m_clip_begin - frame_duration, frame_ts_micros - (now_micros + frame_duration)); m_frame_early++; } else if (!buf) { m_frame_missing++; } AM_DBG lib::logger::get_logger()->debug("video_renderer::data_avail: start_frame(..., %d)", (int)frame_ts_micros); lib::event * e = new dataavail_callback (this, &video_renderer::data_avail); // Grmpf. frame_ts_micros is on the movie timescale, but start_frame() expects a time relative to // the m_event_processor clock (even though it is in microseconds, not milliseconds). Very bad design, // for now we hack around it. m_src->start_frame (m_event_processor, e, frame_ts_micros+(m_epoch*1000)); m_lock.leave();}void video_renderer::redraw(const lib::rect &dirty, common::gui_window *window){ AM_DBG lib::logger::get_logger ()->debug("video_renderer::redraw (this = 0x%x)", (void *) this);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -