📄 time_node.cpp
字号:
}common::playable *time_node::create_playable() { assert(is_playable()); if(m_ffwd_mode) return 0; AM_DBG m_logger->debug("%s[%s].create()", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str()); return m_context->create_playable(m_node);}time_node::time_type time_node::get_playable_dur() { common::duration dur_pair = m_context->get_dur(m_node); if(dur_pair.first && dur_pair.second>0) return secs_to_time_type(dur_pair.second)();#if 0 // Attempted fix by Jack: if we're fast-forwarding we return zero for // things we can't figure out the duration for. if (m_ffwd_mode) { m_logger->trace("fast-forward: ignoring unknown duration for %s", get_sig().c_str()); return secs_to_time_type(0)(); }#endif return time_type::unresolved;}// Prepare children playables without recursionvoid time_node::prepare_playables() {#ifdef WITH_AGGRESSIVE_PRELOADING // Note by Jack: this code is temporarily disabled. It has some serious // drawbacks, such as starting all playables for children of a <seq>, which // could (in some documents) cause an incredible number of playables to // be created long before they're actually needed. We need to come up // with a better preloading scheme at some point. if(m_ffwd_mode) return; std::list<time_node*> children; get_children(children); std::list<time_node*>::iterator it; for(it = children.begin(); it != children.end(); it++) { if((*it)->is_playable()) (*it)->create_playable(); }#endif}//////////////////////////// Driver shellvoid time_node::get_pending_events(std::map<time_type, std::list<time_node*> >& events) { if(!is_alive() || paused() || deferred()) return; if(m_interval.is_valid()) { if(!is_active()) { // If we are not active we schedule our own begin qtime_type timestamp(sync_node(), m_interval.begin); time_type doctime = timestamp.to_doc(); AM_DBG m_logger->debug("get_pending_events(0x%x %s): schedule begin for %d", this, get_sig().c_str(), doctime()); events[doctime].push_back(this); } else if(m_interval.end.is_definite()) { // If we are active and our end is known we schedule our own end qtime_type timestamp(sync_node(), get_interval_end()); time_type doctime = timestamp.to_doc(); AM_DBG m_logger->debug("get_pending_events(0x%x %s): schedule end for %d", this, get_sig().c_str(), doctime()); events[doctime].push_back(this); bool repeats = m_attrs.specified_rdur() || m_attrs.specified_rcount(); repeats = repeats && (m_last_cdur.is_definite() && m_last_cdur() != 0); if(repeats) { // And we also schedule our repeat, if applicable qtime_type ts(this, m_last_cdur); time_type dt = ts.to_doc(); AM_DBG m_logger->debug("get_pending_events(0x%x %s): schedule repeat for %d", this, get_sig().c_str(), dt()); events[dt].push_back(this); } } } if(m_update_event.first) { // Jack thinks (but not sure:-) this one has to do with multiple // begin/end times. qtime_type timestamp = m_update_event.second; time_type doctime = timestamp.to_doc(); AM_DBG m_logger->debug("get_pending_events(0x%x %s): schedule update_event for %d", this, get_sig().c_str(), doctime()); events[doctime].push_back(this); } if(m_transout_sr && !m_ffwd_mode) { // We schedule the start of our outtransition, if applicable time_mset set; m_transout_sr->get_instance_times(set); if(!set.empty()) { qtime_type timestamp(sync_node(), *set.begin()); time_type doctime = timestamp.to_doc(); AM_DBG m_logger->debug("get_pending_events(0x%x %s): schedule transout for %d", this, get_sig().c_str(), doctime()); events[doctime].push_back(this); } } std::list<time_node*> children; get_children(children); std::list<time_node*>::iterator it; for(it = children.begin(); it != children.end(); it++) { // Finally we recursively get all events for our childen. (*it)->get_pending_events(events); } AM_DBG lib::logger::get_logger()->debug(" time_node::get_pending_events: 0x%x", &events);}void time_node::exec(qtime_type timestamp) { AM_DBG m_logger->debug("time_node::exec(%ld) for %s ffwd %d is_alive()=%d is_active()=%d", timestamp.second(), get_sig().c_str(), (int)m_ffwd_mode, is_alive(), is_active()); if(!is_alive()) { // check for transOut return; } if(m_update_event.first) { sync_update(m_update_event.second); m_update_event.first = false; } timestamp.to_node(sync_node()); assert(timestamp.first == sync_node()); if(!is_active()) { // in this state, activation is the only interesting activity AM_DBG m_logger->debug("time_node::exec(%ld) for %s m_interval=(%ld,%ld) deferred=%d", timestamp.second(), get_sig().c_str(), m_interval.begin(), m_interval.end(), deferred()); if(begin_cond(timestamp)) { if(deferred()) defer_interval(timestamp); else set_state_ex(ts_active, timestamp); } return; } // The following code applies to active nodes assert(is_active()); if(m_transout_sr) { // timestamp.second >= m_interval.end time_mset set; m_transout_sr->get_instance_times(set); if(!set.empty()) { qtime_type ts(sync_node(), *set.begin()); if(timestamp.second >= ts.second) { // start trasnition AM_DBG m_logger->debug("%s[%s].start_transition() at %ld (end:%ld)", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), ts.second(), m_interval.end()); const lib::transition_info *trans_out = m_attrs.get_trans_out(); m_context->start_transition(m_node, trans_out, false); m_transout_sr->reset(0); } } } // Check for the EOI event if(end_cond(timestamp)) { // This node should go postactive time_type reftime; if(m_interval.end.is_definite()) reftime = m_interval.end; else reftime = timestamp.second; qtime_type qt(sync_node(), reftime); set_state_ex(ts_postactive, qt); return; } // Check for the EOSD event AM_DBG m_logger->debug("%s[%s] checking for end-of-sd (cdur=%ld)", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), m_last_cdur()); if(m_last_cdur.is_definite() && m_last_cdur() != 0 && timestamp.as_time_value_down_to(this) >= m_last_cdur()) on_eosd(timestamp);}// Returns true when the end conditions of this node evaluate to true// e.g. the node should be deactivated// Assumes the node is activebool time_node::end_cond(qtime_type timestamp) { assert(timestamp.first == sync_node()); assert(is_active()); bool ec = end_sync_cond_applicable() && end_sync_cond(); bool tc = !end_sync_cond_applicable() && timestamp.second >= get_interval_end(); if(is_time_container() && (ec || tc)) { AM_DBG m_logger->debug("%s[%s].end_cond() true [%s]", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), (ec?"end_sync_cond":"interval_end")); } // The "tc" condition is not sufficient when needs_implicit_dur()is true // for example: // a) a video has returned its implicit dur // b) calcs have been done based on this value // c) m_interval.end may have assumed this vale // d) the interval has not been updated yet // e) due to not controled delays the video is still playing bool specified_dur = m_attrs.specified_dur() || m_attrs.specified_rdur(); if(is_cmedia() && !is_animation() && tc && !specified_dur && m_time_calc->uses_dur()) { if(m_context->wait_for_eom() && !m_eom_flag) { tc = false; AM_DBG m_logger->debug("%s[%s].end_cond() waiting media end", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str()); } } return ec || tc;}// Returns true when the begin conditions of this node evaluate to true// e.g. the node should be activatedbool time_node::begin_cond(qtime_type timestamp) { return m_interval.is_valid() && m_interval.contains(timestamp.second);}//////////////////////////// Notifications// The clock used by the argument timestamp can be any// Convert it if possible to this sync_node clock timevoid time_node::sync_update(qtime_type timestamp) { time_type pt = timestamp.as_node_time(sync_node()); if(pt.is_resolved()) { timestamp = qtime_type(sync_node(), pt); m_state->sync_update(timestamp); }}// Called on the end of simple duration eventvoid time_node::on_eosd(qtime_type timestamp) { AM_DBG m_logger->debug("%s[%s].on_eosd() ST:%ld, PT:%ld, DT:%ld (sdur=%ld)", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), timestamp.as_time_value_down_to(this), timestamp.second(), timestamp.as_doc_time_value(), m_last_cdur() ); // update repeat registers m_rad += m_last_cdur(); m_precounter++; // Should this node repeat? if(m_attrs.specified_rdur()) { time_type rdur = m_attrs.get_rdur(); if(rdur == time_type::indefinite || rdur > m_rad) { repeat(timestamp); return; } } if(m_attrs.specified_rcount()) { double rcount = m_attrs.get_rcount(); if(m_attrs.is_rcount_indefinite() || rcount > double(m_precounter)) { repeat(timestamp); return; } }}// Begin of media notification// Currently this notification is not used.// Could be used to define the slip sync offset.void time_node::on_bom(qtime_type timestamp) { m_eom_flag = false; if(!is_discrete()) { qtime_type pt = timestamp.as_qtime_down_to(sync_node()); qtime_type st = pt.as_qtime_down_to(this); AM_DBG m_logger->debug("%s[%s].on_bom() ST:%ld, PT:%ld, DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), st.second(), pt.second(), timestamp.second()); } if(m_timer) m_timer->resume();}// Notification from the playable that has paused for fetching bitsvoid time_node::on_pom(qtime_type timestamp) { if(m_timer) m_timer->pause();}// Notification from the playable that has resumed playbackvoid time_node::on_rom(qtime_type timestamp) { if(m_timer) m_timer->resume();}// End of nedia notification// This notification is taken into account when this node is still active// and the implicit duration is involved in timing calculations.void time_node::on_eom(qtime_type timestamp) { AM_DBG m_logger->debug("%s[%s].on_eom()", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str()); m_eom_flag = true; if(is_playable() && !is_discrete()) { if(m_impldur == time_type::unresolved) { time_type pt = timestamp.as_node_time(sync_node()); m_impldur = pt - m_interval.begin(); } if (m_state->ident() == ts_active) { raise_update_event(timestamp); sync_node()->raise_update_event(timestamp); } qtime_type pt = timestamp.as_qtime_down_to(sync_node()); qtime_type st = pt.as_qtime_down_to(this); AM_DBG m_logger->debug("%s[%s].on_eom() ST:%ld, PT:%ld, DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), st.second(), pt.second(), timestamp.second()); }}// Return true if we want on on_eom callback.// This is a hack, really. The problem it tries to solve is that sometimes the// on_eom notification comes in late (because it goes through the event processor)// and by the time we get to on_eom we've already started with a next iteration.// The on_eom callback will then erronuously terminte the new iteration.// But: this solution is a hack, it would be much better if we (a) could make sure// this situation could never occur or (b) could detect this situation in on_eom().bool time_node::want_on_eom(){ return is_active() && !m_eom_flag;}// End of transition// This notification is sent when a transition ends. It is sent to// all nodes that overlap the transition that just finished.void time_node::on_transitioned(qtime_type timestamp) { AM_DBG m_logger->debug("%s[%s].on_transitioned() DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), timestamp.second()); // If this node is not in fill=fill_transition state we do nothing if (m_active || !m_needs_remove) return; const time_attrs* ta = get_time_attrs(); fill_behavior fb = ta->get_fill(); if (fb == fill_transition) remove(timestamp);}//////////////////////////// Node activities (see also activate())// The following function is called when the node should repeat.// It is responsible to execute the repeat actions for this node. void time_node::repeat(qtime_type timestamp) { AM_DBG m_logger->debug("%s[%s].repeat() ST:%ld, PT:%ld, DT:%ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), timestamp.as_time_value_down_to(this), timestamp.second(), timestamp.as_doc_time_value()); // raise_repeat_event async raise_repeat_event(timestamp); if(down()) { // The reset_children op ends all active children // In consequence for excl empties its pause queue reset_children(timestamp, this); startup_children(timestamp); } if(!is_time_container()) { repeat_playable(); }}// Pauses this node.// Excl element handling.void time_node::pause(qtime_type timestamp, pause_display d) { if(!is_active()) return; time_type self_simple_time = timestamp.as_time_down_to(this); qtime_type qt(this, self_simple_time); if(is_playable()) pause_playable(d); if(m_timer) m_timer->pause(); m_paused_sync_time = timestamp.as_time_down_to(sync_node()); // could deduce this from the fact that the node // is active and the timer is nor running set_paused(true); // Recalculate the interval of this node as if its dur was indefinite // The interval semantics are applicable when paused interval_type i1 = m_interval; m_time_calc->set_paused_mode(true); time_type iend = calc_current_interval_end(); if(iend != m_interval.end) { update_interval_end(timestamp, iend); } // Report changed interval AM_DBG m_logger->debug("%s[%s].pause(): %s -> %s at %ld", m_attrs.get_tag().c_str(), m_attrs.get_id().c_str(), ::repr(i1).c_str(), ::repr(m_interval).c_str(), self_simple_time()); // Pause playable active children time_node::iterator nit; time_node::iterator end_nit = end(); for(nit=begin(); nit != end_nit; nit++) { if(!(*nit).first && (*nit).second->is_playable() && (*nit).second->is_active()) (*nit).second->pause_playable(d);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -