📄 time_state.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: time_state.cpp,v 1.23 2007/02/12 14:15:23 jackjansen Exp $ */#include "ambulant/smil2/time_state.h"#include "ambulant/smil2/time_node.h"#include "ambulant/lib/logger.h"//#define AM_DBG if(1)#ifndef AM_DBG#define AM_DBG if(0)#endifusing namespace ambulant;using namespace smil2;///////////////////////// time_state// Base class for time states.// Offers the common time states interface.// Brings into time states scope, node's common state variables.//// The FSM implemented by the time states asserts that on any transition// the exit() actions of the previous state are always executed // and then the enter() actions of the new state // (this is true also for self transitions).//time_state::time_state(time_node *tn) : m_self(tn), m_interval(tn->m_interval), m_active(tn->m_active), m_needs_remove(tn->m_needs_remove), m_last_cdur(tn->m_last_cdur), m_rad(tn->m_rad), m_pad(tn->m_pad), m_precounter(tn->m_precounter), m_impldur(tn->m_impldur), m_attrs(tn->m_attrs) {}void time_state::sync_update(qtime_type timestamp) {}void time_state::kill(qtime_type timestamp, time_node *oproot) { m_self->set_state(ts_dead, timestamp, oproot);}void time_state::reset(qtime_type timestamp, time_node *oproot) { m_self->set_state(ts_reset, timestamp, oproot);}void time_state::exit(qtime_type timestamp, time_node *oproot) {}void time_state::report_state(qtime_type timestamp) { AM_DBG logger::get_logger()->debug("%s[%s] --> %s at PT:%ld, DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), time_state_str(this->ident()), timestamp.second(), timestamp.as_doc_time()());}void time_state::report_state() { AM_DBG logger::get_logger()->debug("%s[%s] --> %s", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), time_state_str(this->ident()));}///////////////////////// reset_state// An element enters this state when an ancestor begins // or repeats (e.g. on an ancestor beginEvent or repeat).// Element's instance lists are reset with respect to that ancestor.// Therefore resetting an element while in the reset state is meaningful. // An element will remain in this state until its parent is activated.// Before the document starts all elements are in this state.// Reset enter / update state variables// Reset do / on parent beginEvent transition to PROACTIVE// Reset exit / ?void reset_state::enter(qtime_type timestamp) { m_interval = interval_type::unresolved; m_active = false; m_needs_remove = false; m_last_cdur = time_type::unresolved; m_rad = 0; m_pad = 0; m_precounter = 0; m_impldur = time_type::unresolved; m_self->clear_history(); // Resetting the above variables is only part of the reset process. // See time_node::reset() report_state();}void reset_state::sync_update(qtime_type timestamp) {}void reset_state::exit(qtime_type timestamp, time_node *oproot) { // next is proactive when the parent begins (normal) // next is a self-transition to reset when an encestor begins or repeats (reset)} ///////////////////////// proactive_state// An element enters this state when its parent begins // or repeats (e.g. on parent beginEvent or repeat).//// Proactive enter / update state variables, evaluate first interval// Proactive do / on sync update re-evaluate interval, enter active on timer event// Proactive exit / ?void proactive_state::enter(qtime_type timestamp) { report_state(timestamp); interval_type i = m_self->calc_first_interval(); AM_DBG logger::get_logger()->debug("%s[%s].proactive_state::enter calc_first_interval -> %s at DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), ::repr(i).c_str(), timestamp.as_doc_time()()); if(i.is_valid()) { // Set the calc interval as current and update dependents m_self->set_interval(timestamp, i); // The above call may result // a) to a transition to active if the interval contains timestamp // b) to remain proactive waiting for the scheduled interval // c) to a jump to postactive if the interval is in the past. } // else wait in the current state for new happenings }void proactive_state::sync_update(qtime_type timestamp) { interval_type i = m_self->calc_first_interval(); AM_DBG logger::get_logger()->debug("%s[%s].proactive_state::sync_update %s --> %s at DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), ::repr(m_interval).c_str(), ::repr(i).c_str(), timestamp.as_doc_time()()); if(m_interval.is_valid() && i.is_valid() && i != m_interval) { m_self->update_interval(timestamp, i); } else if(m_interval.is_valid() && !i.is_valid()) { m_self->cancel_interval(timestamp); } else if(!m_interval.is_valid() && i.is_valid()) { m_self->set_interval(timestamp, i); }}void proactive_state::kill(qtime_type timestamp, time_node *oproot) { // cancels any interval and transitions to dead if(m_interval.is_valid()) m_self->cancel_interval(timestamp); m_self->set_state(ts_dead, timestamp, oproot);}void proactive_state::reset(qtime_type timestamp, time_node *oproot) { // cancels any interval and transitions to reset if(m_interval.is_valid()) m_self->cancel_interval(timestamp); m_self->set_state(ts_reset, timestamp, oproot);} void proactive_state::exit(qtime_type timestamp, time_node *oproot) { // next is active when the interval is within parent AD (normal) // next is postactive if // the interval is in the past (jump) // the interval will not be exec because of peers="never" excl semantics // next is reset if the parent repeats or restarts (reset) // next is dead if the parent ends (kill) }///////////////////////// active_state// An element enters the active state when it has a valid// interval and begin <= parent->get_simple_time().// An element will remain in this state until its interval completes// or its parent ends.// A node with a valid interval may never enter the active state,// or, may enter the active state halfway through its interval.// In the cases that an interval or part of it cannot be played // the respective real events will not be raised but// dependents should be updated for the interval. // // Active enter / update state variables, raise_begin_event, peer.start(t)?// Active do /// on repeat: m_last_cdur=clock_value, m_rad += m_last_cdur, m_precounter++;// on sync update re-evaluate current interval's 'end', consider restart semantics// Active exit / update state variables, raise_end_event, peer.pause() or peer.stop()//// timestamp: "scheduled now" in parent simple time//void active_state::enter(qtime_type timestamp) { report_state(timestamp); m_active = true; m_needs_remove = true; // if this is in a seq should remove any freeze effect of previous // XXX Not strictly correct for fill=transition, which now behaves identical to fill=hold if(m_self->up() && m_self->up()->is_seq()) { time_node *prev = m_self->previous(); if(prev) { const time_attrs* ta = prev->get_time_attrs(); fill_behavior fb = ta->get_fill(); if(fb != fill_hold && fb != fill_transition) prev->remove(timestamp); } } // Send feedback to the upper layers about what we're doing m_self->node_started(); // The timestamp in parent simple time // Children should convert it to their parent m_self->reset_children(timestamp, m_self); // Prepare children playables without recursion m_self->prepare_playables(); // Use can slip sync behavior // To avoid flashing use async activation // for audio and video only. // XXX: check that discrete media are local //if(m_self->is_cmedia()) // m_self->activate_async(timestamp); //else m_self->activate(timestamp); // The timestamp in parent simple time // Children should convert it to their parent m_self->startup_children(timestamp); // raise_begin_event async // XXX: Check excl pause m_self->raise_begin_event(timestamp);}void active_state::sync_update(qtime_type timestamp) { // Update end, consider restart semantics AM_DBG logger::get_logger()->debug("%s[%s].active_state::sync_update() at ST:%ld PT:%ld, DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), timestamp.as_time_value_down_to(m_self), timestamp.second(), timestamp.as_doc_time_value()); time_type end = m_self->calc_current_interval_end(); if(end != m_interval.end) { m_self->update_interval_end(timestamp, end); } restart_behavior rb = m_attrs.get_restart(); if(rb == restart_always && !m_self->sync_node()->is_seq()) { interval_type candidate(m_interval.begin, timestamp.second); interval_type i = m_self->calc_next_interval(candidate); if(i.is_valid()) { AM_DBG logger::get_logger()->debug("%s[%s] restart interval %s", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), ::repr(i).c_str()); m_self->set_state(ts_postactive, timestamp, m_self);#if 1 // XXX Attempt by Jack to fix bug #1627916: // The original code here is completely different from what happens in // postactive/preactive. Try to run the original code by getting our // time_node to do sync_update recursively. m_self->sync_update(timestamp);#else m_self->set_begin_event_inst(timestamp.second); m_self->raise_update_event(timestamp); //m_self->sync_update(timestamp);#endif } }}void active_state::kill(qtime_type timestamp, time_node *oproot) { // Forced transition to dead originated by oproot // The exit() actions will be executed // e.g. fill, raise events, update dependents, etc m_self->set_state(ts_dead, timestamp, oproot); }void active_state::reset(qtime_type timestamp, time_node *oproot) { // Forced transition to reset originated by oproot // The exit() actions will be executed m_self->remove(timestamp); m_self->set_state(ts_reset, timestamp, oproot); m_self->reset_children(timestamp, oproot); } void active_state::exit(qtime_type timestamp, time_node *oproot) { m_active = false; m_self->fill(timestamp); // pause or stop m_self->kill_children(timestamp, oproot); m_self->raise_end_event(timestamp, oproot); m_self->played_interval(timestamp); // next is postactive if its interval was not cut short (normal) // next is reset if the parent repeats or restarts (reset) // next is dead if the parent ends (kill) // Add to history and invalidate}///////////////////////// postactive_state// An element enters the postactive state when an interval ends.// An element will remain in this state until the// next interval begins or the parent ends.// While in this state any fill effects are applied.// Postactive enter / update state variables, compute next interval if restart != never// Postactive do / on sync update re-evaluate interval, // enter active on timer event,// enter dead on parent end// Postactive exit / ?void postactive_state::enter(qtime_type timestamp) { report_state(timestamp); // m_interval = unchanged (last played interval that is now in the past); // m_needs_remove = SET true or false depending on the fill attribute; if(m_self->sync_node()->is_seq()) { m_self->set_state(ts_dead, timestamp, m_self->sync_node()); return; } restart_behavior rb = m_attrs.get_restart(); if(rb == restart_never) return; // evaluate next interval if restart != never. interval_type i = m_self->calc_next_interval(); if(i.is_valid()) { // Set the calc interval as current and update dependents m_self->set_interval(timestamp, i); // The above call may result // a) to a transition to active if the interval starts at timestamp // b) to remain postactive waiting for the scheduled interval } // else wait in the current state for new happenings }void postactive_state::sync_update(qtime_type timestamp) { restart_behavior rb = m_attrs.get_restart(); if(rb == restart_never) return; interval_type i = m_self->calc_next_interval(); if(m_interval.is_valid() && i.is_valid() && i != m_interval) { m_self->update_interval(timestamp, i); } else if(m_interval.is_valid() && !i.is_valid()) { m_self->cancel_interval(timestamp); } else if(!m_interval.is_valid() && i.is_valid()) { m_self->set_interval(timestamp, i); }}void postactive_state::kill(qtime_type timestamp, time_node *oproot) { // cancels any interval and transitions to dead if(m_interval.is_valid()) m_self->cancel_interval(timestamp); // fill remains m_self->set_state(ts_dead, timestamp, oproot);}void postactive_state::reset(qtime_type timestamp, time_node *oproot) { // cancels any interval and transitions to reset if(m_interval.is_valid()) m_self->cancel_interval(timestamp); // fill goes m_self->remove(timestamp); m_self->set_state(ts_reset, timestamp, oproot);} void postactive_state::exit(qtime_type timestamp, time_node *oproot) { // next is active when restart != never and next interval is within parent AD // next is reset if the parent repeats or restarts // next is dead if the parent ends}///////////////////////// dead_state// An element enters the dead state when its parent ends.// While in this state any fill effects are applied.// Dead enter / update state variables// Dead do / nothing// Dead exit / ?void dead_state::enter(qtime_type timestamp) { report_state();}void dead_state::kill(qtime_type timestamp, time_node *oproot) {}void dead_state::reset(qtime_type timestamp, time_node *oproot) { m_self->set_state(ts_reset, timestamp, oproot);} void dead_state::exit(qtime_type timestamp, time_node *oproot) { // next is reset if the parent restarts m_self->remove(timestamp);}//////////////////////////// tracing helperconst char* smil2::time_state_str(time_state_type state) { switch(state) { case ts_reset: return "reset"; case ts_proactive: return "proactive"; case ts_active: return "active"; case ts_postactive: return "postactive"; case ts_dead: return "dead"; } return "unknown";}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -