void TransitionHandler::deleteTransition(QString tag, int /*a_track*/, int b_track, GenTime in, GenTime out, QDomElement /*xml*/, bool /*do_refresh*/) { QScopedPointer<Mlt::Field> field(m_tractor->field()); field->lock(); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString mlt_type = mlt_properties_get(properties, "mlt_type"); QString resource = mlt_properties_get(properties, "mlt_service"); const int old_pos = (int)((in + out).frames(m_fps) / 2); ////qDebug() << " del trans pos: " << in.frames(25) << '-' << out.frames(25); while (mlt_type == QLatin1String("transition")) { mlt_transition tr = (mlt_transition) nextservice; int currentTrack = mlt_transition_get_b_track(tr); int currentIn = (int) mlt_transition_get_in(tr); int currentOut = (int) mlt_transition_get_out(tr); ////qDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack; if (resource == tag && b_track == currentTrack && currentIn <= old_pos && currentOut >= old_pos) { mlt_field_disconnect_service(field->get_field(), nextservice); break; } nextservice = mlt_service_producer(nextservice); if (nextservice == NULL) break; properties = MLT_SERVICE_PROPERTIES(nextservice); mlt_type = mlt_properties_get(properties, "mlt_type"); resource = mlt_properties_get(properties, "mlt_service"); } field->unlock(); //askForRefresh(); //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1); }
bool TransitionHandler::moveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut) { double fps = m_tractor->get_fps(); int new_in = (int)newIn.frames(fps); int new_out = (int)newOut.frames(fps) - 1; if (new_in >= new_out) return false; int old_in = (int)oldIn.frames(fps); int old_out = (int)oldOut.frames(fps) - 1; bool doRefresh = true; // Check if clip is visible in monitor int position = mlt_producer_position(m_tractor->get_producer()); int diff = old_out - position; if (diff < 0 || diff > old_out - old_in) doRefresh = false; if (doRefresh) { diff = new_out - position; if (diff < 0 || diff > new_out - new_in) doRefresh = false; } QScopedPointer<Mlt::Field> field(m_tractor->field()); field->lock(); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString resource = mlt_properties_get(properties, "mlt_service"); int old_pos = (int)(old_in + old_out) / 2; bool found = false; mlt_service_type mlt_type = mlt_service_identify( nextservice ); while (mlt_type == transition_type) { Mlt::Transition transition((mlt_transition) nextservice); nextservice = mlt_service_producer(nextservice); int currentTrack = transition.get_b_track(); int currentIn = (int) transition.get_in(); int currentOut = (int) transition.get_out(); if (resource == type && startTrack == currentTrack && currentIn <= old_pos && currentOut >= old_pos) { found = true; if (newTrack - startTrack != 0) { Mlt::Properties trans_props(transition.get_properties()); Mlt::Transition new_transition(*m_tractor->profile(), transition.get("mlt_service")); Mlt::Properties new_trans_props(new_transition.get_properties()); // We cannot use MLT's property inherit because it also clones internal values like _unique_id which messes up the playlist cloneProperties(new_trans_props, trans_props); new_transition.set_in_and_out(new_in, new_out); field->disconnect_service(transition); plantTransition(field.data(), new_transition, newTransitionTrack, newTrack); } else transition.set_in_and_out(new_in, new_out); break; } if (nextservice == NULL) break; properties = MLT_SERVICE_PROPERTIES(nextservice); mlt_type = mlt_service_identify( nextservice ); resource = mlt_properties_get(properties, "mlt_service"); } field->unlock(); if (doRefresh) refresh(); //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1); return found; }
void TransitionHandler::deleteTrackTransitions(int ix) { QScopedPointer<Mlt::Field> field(m_tractor->field()); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_service_type type = mlt_service_identify( nextservice ); while (type == transition_type) { Mlt::Transition transition((mlt_transition) nextservice); nextservice = mlt_service_producer(nextservice); int currentTrack = transition.get_b_track(); if (ix == currentTrack) { field->disconnect_service(transition); } if (nextservice == NULL) break; type = mlt_service_identify(nextservice ); } }
// adds the transition by keeping the instance order from topmost track down to background void TransitionHandler::plantTransition(Mlt::Field *field, Mlt::Transition &tr, int a_track, int b_track) { mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString mlt_type = mlt_properties_get(properties, "mlt_type"); QString resource = mlt_properties_get(properties, "mlt_service"); QList <Mlt::Transition *> trList; mlt_properties insertproperties = tr.get_properties(); QString insertresource = mlt_properties_get(insertproperties, "mlt_service"); bool isMixTransition = insertresource == QLatin1String("mix"); while (mlt_type == QLatin1String("transition")) { Mlt::Transition transition((mlt_transition) nextservice); nextservice = mlt_service_producer(nextservice); int aTrack = transition.get_a_track(); int bTrack = transition.get_b_track(); int internal = transition.get_int("internal_added"); if ((isMixTransition || resource != QLatin1String("mix")) && (internal > 0 || aTrack < a_track || (aTrack == a_track && bTrack > b_track))) { Mlt::Properties trans_props(transition.get_properties()); Mlt::Transition *cp = new Mlt::Transition(*m_tractor->profile(), transition.get("mlt_service")); Mlt::Properties new_trans_props(cp->get_properties()); //new_trans_props.inherit(trans_props); cloneProperties(new_trans_props, trans_props); trList.append(cp); field->disconnect_service(transition); } //else qDebug() << "// FOUND TRANS OK, "<<resource<< ", A_: " << aTrack << ", B_ "<<bTrack; if (nextservice == NULL) break; properties = MLT_SERVICE_PROPERTIES(nextservice); mlt_type = mlt_properties_get(properties, "mlt_type"); resource = mlt_properties_get(properties, "mlt_service"); } field->plant_transition(tr, a_track, b_track); // re-add upper transitions for (int i = trList.count() - 1; i >= 0; --i) { ////qDebug()<< "REPLANT ON TK: "<<trList.at(i)->get_a_track()<<", "<<trList.at(i)->get_b_track(); field->plant_transition(*trList.at(i), trList.at(i)->get_a_track(), trList.at(i)->get_b_track()); } qDeleteAll(trList); }
static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // internal intialization mlt_frame frame = NULL; int last_position = -1; int eos = 0; int eos_threshold = 20; if ( self->play ) eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" ); // Determine if the application is dealing with the preview int preview_off = mlt_properties_get_int( properties, "preview_off" ); pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_get_frame( consumer ); // Ensure that we have a frame if ( self->running && frame != NULL ) { // Get the speed of the frame double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); // Lock during the operation mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) ); // Get refresh request for the current frame int refresh = mlt_properties_get_int( properties, "refresh" ); // Decrement refresh and clear changed mlt_events_block( properties, properties ); mlt_properties_set_int( properties, "refresh", 0 ); mlt_events_unblock( properties, properties ); // Unlock after the operation mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) ); // Set the changed property on this frame for the benefit of still mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh ); // Make sure the recipient knows that this frame isn't really rendered mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 ); // Optimisation to reduce latency if ( speed == 1.0 ) { if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) mlt_consumer_purge( self->play ); last_position = mlt_frame_get_position( frame ); } else { //mlt_consumer_purge( self->play ); last_position = -1; } // If we aren't playing normally, then use the still if ( speed != 1 ) { mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) ); mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1; int pause = 0; #ifndef SKIP_WAIT_EOS if ( self->active == self->play ) { // Do not interrupt the play consumer near the end if ( duration - self->last_position > eos_threshold ) { // Get a new frame at the sought position mlt_frame_close( frame ); if ( producer ) mlt_producer_seek( producer, self->last_position ); frame = mlt_consumer_get_frame( consumer ); pause = 1; } else { // Send frame with speed 0 to stop it if ( frame && !mlt_consumer_is_stopped( self->play ) ) { mlt_consumer_put_frame( self->play, frame ); frame = NULL; eos = 1; } // Check for end of stream if ( mlt_consumer_is_stopped( self->play ) ) { // Stream has ended mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" ); pause = 1; eos = 0; // reset eos indicator } else { // Prevent a tight busy loop struct timespec tm = { 0, 100000L }; // 100 usec nanosleep( &tm, NULL ); } } } #else pause = self->active == self->play; #endif if ( pause ) { // Start the still consumer if ( !mlt_consumer_is_stopped( self->play ) ) mlt_consumer_stop( self->play ); self->last_speed = speed; self->active = self->still; self->ignore_change = 0; mlt_consumer_start( self->still ); } // Send the frame to the active child if ( frame && !eos ) { mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 ); if ( self->active ) mlt_consumer_put_frame( self->active, frame ); } if ( pause && speed == 0.0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); } } // Allow a little grace time before switching consumers on speed changes else if ( self->ignore_change -- > 0 && self->active != NULL && !mlt_consumer_is_stopped( self->active ) ) { mlt_consumer_put_frame( self->active, frame ); } // Otherwise use the normal player else { if ( !mlt_consumer_is_stopped( self->still ) ) mlt_consumer_stop( self->still ); if ( mlt_consumer_is_stopped( self->play ) ) { self->last_speed = speed; self->active = self->play; self->ignore_change = 0; mlt_consumer_start( self->play ); } if ( self->play ) mlt_consumer_put_frame( self->play, frame ); } // Copy the rectangle info from the active consumer if ( self->running && preview_off == 0 && self->active ) { mlt_properties active = MLT_CONSUMER_PROPERTIES( self->active ); mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) ); mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) ); mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) ); mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) ); mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) ); mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) ); } if ( self->active == self->still ) { pthread_mutex_lock( &self->refresh_mutex ); if ( self->running && speed == 0 && self->refresh_count <= 0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } self->refresh_count --; pthread_mutex_unlock( &self->refresh_mutex ); } } else { if ( frame ) mlt_frame_close( frame ); mlt_consumer_put_frame( self->active, NULL ); self->running = 0; } } if ( self->play ) mlt_consumer_stop( self->play ); if ( self->still ) mlt_consumer_stop( self->still ); return NULL; }
void TransitionHandler::updateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml) { QScopedPointer<Mlt::Field> field(m_tractor->field()); field->lock(); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString mlt_type = mlt_properties_get(properties, "mlt_type"); QString resource = mlt_properties_get(properties, "mlt_service"); int in_pos = (int) in.frames(m_fps); int out_pos = (int) out.frames(m_fps) - 1; while (mlt_type == QLatin1String("transition")) { mlt_transition tr = (mlt_transition) nextservice; int currentTrack = mlt_transition_get_b_track(tr); int currentBTrack = mlt_transition_get_a_track(tr); int currentIn = (int) mlt_transition_get_in(tr); int currentOut = (int) mlt_transition_get_out(tr); // //qDebug()<<"Looking for transition : " << currentIn <<'x'<<currentOut<< ", OLD oNE: "<<in_pos<<'x'<<out_pos; if (resource == type && b_track == currentTrack && currentIn == in_pos && currentOut == out_pos) { QMap<QString, QString> map = getTransitionParamsFromXml(xml); QMap<QString, QString>::Iterator it; QString key; mlt_properties transproperties = MLT_TRANSITION_PROPERTIES(tr); QString currentId = mlt_properties_get(transproperties, "kdenlive_id"); if (currentId != xml.attribute(QStringLiteral("id"))) { // The transition ID is not the same, so reset all properties mlt_properties_set(transproperties, "kdenlive_id", xml.attribute(QStringLiteral("id")).toUtf8().constData()); // Cleanup previous properties QStringList permanentProps; permanentProps << QStringLiteral("factory") << QStringLiteral("kdenlive_id") << QStringLiteral("mlt_service") << QStringLiteral("mlt_type") << QStringLiteral("in"); permanentProps << QStringLiteral("out") << QStringLiteral("a_track") << QStringLiteral("b_track"); for (int i = 0; i < mlt_properties_count(transproperties); ++i) { QString propName = mlt_properties_get_name(transproperties, i); if (!propName.startsWith('_') && ! permanentProps.contains(propName)) { mlt_properties_set(transproperties, propName.toUtf8().constData(), ""); } } } mlt_properties_set_int(transproperties, "force_track", xml.attribute(QStringLiteral("force_track")).toInt()); mlt_properties_set_int(transproperties, "automatic", xml.attribute(QStringLiteral("automatic"), QStringLiteral("0")).toInt()); if (currentBTrack != a_track) { mlt_properties_set_int(transproperties, "a_track", a_track); } for (it = map.begin(); it != map.end(); ++it) { key = it.key(); mlt_properties_set(transproperties, key.toUtf8().constData(), it.value().toUtf8().constData()); ////qDebug() << " ------ UPDATING TRANS PARAM: " << key.toUtf8().constData() << ": " << it.value().toUtf8().constData(); //filter->set("kdenlive_id", id); } break; } nextservice = mlt_service_producer(nextservice); if (nextservice == NULL) break; properties = MLT_SERVICE_PROPERTIES(nextservice); mlt_type = mlt_properties_get(properties, "mlt_type"); resource = mlt_properties_get(properties, "mlt_service"); } field->unlock(); //askForRefresh(); //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1); }
void TransitionHandler::enableMultiTrack(bool enable) { int tracks = m_tractor->count(); if (tracks < 3) { // we need at leas 3 tracks (black bg track + 2 tracks to use this) return; } QScopedPointer<Mlt::Service> service(m_tractor->field()); QScopedPointer<Mlt::Field> field(m_tractor->field()); field->lock(); if (enable) { // disable track composition (frei0r.cairoblend) QScopedPointer<Mlt::Field> field(m_tractor->field()); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_service_type type = mlt_service_identify( nextservice ); while (type == transition_type) { Mlt::Transition transition((mlt_transition) nextservice); nextservice = mlt_service_producer(nextservice); int added = transition.get_int("internal_added"); if (added == 237) { QString mlt_service = transition.get("mlt_service"); if (mlt_service == QLatin1String("frei0r.cairoblend") && transition.get_int("disable") == 0) { transition.set("disable", 1); transition.set("split_disable", 1); } } if (nextservice == NULL) break; type = mlt_service_identify(nextservice); } for (int i = 1, screen = 0; i < tracks && screen < 4; ++i) { Mlt::Producer trackProducer(m_tractor->track(i)); if (QString(trackProducer.get("hide")).toInt() != 1) { Mlt::Transition transition(*m_tractor->profile(), "composite"); transition.set("mlt_service", "composite"); transition.set("a_track", 0); transition.set("b_track", i); transition.set("distort", 0); transition.set("aligned", 0); // 200 is an arbitrary number so we can easily remove these transition later transition.set("internal_added", 200); QString geometry; switch (screen) { case 0: geometry = QStringLiteral("0/0:50%x50%"); break; case 1: geometry = QStringLiteral("50%/0:50%x50%"); break; case 2: geometry = QStringLiteral("0/50%:50%x50%"); break; case 3: default: geometry = QStringLiteral("50%/50%:50%x50%"); break; } transition.set("geometry", geometry.toUtf8().constData()); transition.set("always_active", 1); field->plant_transition(transition, 0, i); screen++; } } } else { QScopedPointer<Mlt::Field> field(m_tractor->field()); mlt_service nextservice = mlt_service_get_producer(field->get_service()); mlt_service_type type = mlt_service_identify( nextservice ); while (type == transition_type) { Mlt::Transition transition((mlt_transition) nextservice); nextservice = mlt_service_producer(nextservice); int added = transition.get_int("internal_added"); if (added == 200) { field->disconnect_service(transition); } else if (added == 237) { // re-enable track compositing QString mlt_service = transition.get("mlt_service"); if (mlt_service == QLatin1String("frei0r.cairoblend") && transition.get_int("split_disable") == 1) { transition.set("disable", 0); transition.set("split_disable", (char*) NULL); } } if (nextservice == NULL) break; type = mlt_service_identify(nextservice); } } field->unlock(); emit refresh(); }