Krita Source Code Documentation
Loading...
Searching...
No Matches
KisPlaybackEngineMLT Class Reference

The KisPlaybackEngineMLT class is an implementation of KisPlaybackEngine that uses MLT (Media Lovin' Toolkit) to drive image frame changes and animation audio with (hopefully) close to frame-perfect synchronization. More...

#include <KisPlaybackEngineMLT.h>

+ Inheritance diagram for KisPlaybackEngineMLT:

Classes

struct  FrameWaitingInterface
 
struct  Private
 
struct  StopAndResume
 The StopAndResumeConsumer struct is used to encapsulate optional stop-and-then-resume behavior of a consumer. Using RAII, we can stop a consumer at construction and simply resume it when it exits scope. More...
 

Public Slots

bool isMute () override
 
PlaybackStats playbackStatistics () const override
 
void seek (int frameIndex, SeekOptionFlags flags=SEEK_FINALIZE|SEEK_PUSH_AUDIO) override
 
void setDropFramesMode (bool value) override
 
void setMute (bool val) override
 
bool supportsAudio () override
 
bool supportsVariablePlaybackSpeed () override
 
- Public Slots inherited from KisPlaybackEngine
bool dropFrames () const
 
virtual void firstFrame ()
 
virtual bool isMute ()=0
 
virtual void lastFrame ()
 
virtual void nextFrame ()
 
virtual void nextKeyframe ()
 
virtual void nextMatchingKeyframe ()
 
virtual void nextUnfilteredKeyframe ()
 
virtual void pause ()
 
virtual void play ()
 
virtual PlaybackStats playbackStatistics () const =0
 
virtual void playPause ()
 
virtual void previousFrame ()
 
virtual void previousKeyframe ()
 
virtual void previousMatchingKeyframe ()
 previousMatchingKeyframe && nextMatchingKeyframe Navigate to the next keyframe that has the same color-label as the current keyframe. Useful to quickly navigate to user-specified 'similar' keyframes. E.g. Contact points in an animation might have a specific color to specify importance and be quickly swapped between.
 
virtual void previousUnfilteredKeyframe ()
 previousUnfilteredKeyframe && nextUnfilteredKeyframe Navigate to keyframes based on the current onion skin filtration. This lets users easily navigate to the next visible "onion-skinned" keyframe on the active layer.
 
virtual void seek (int frameIndex, SeekOptionFlags options=SEEK_FINALIZE|SEEK_PUSH_AUDIO)=0
 
virtual void setDropFramesMode (bool value)
 
virtual void setMute (bool val)=0
 
virtual void stop ()
 
virtual bool supportsAudio ()=0
 
virtual bool supportsVariablePlaybackSpeed ()=0
 

Signals

void sigChangeActiveCanvasFrame (int p_frame)
 
- Signals inherited from KisPlaybackEngine
void sigDropFramesModeChanged (bool value)
 

Public Member Functions

FrameWaitingInterfaceframeWaitingInterface ()
 
 KisPlaybackEngineMLT (QObject *parent=nullptr)
 
 ~KisPlaybackEngineMLT ()
 
- Public Member Functions inherited from KisPlaybackEngine
 KisPlaybackEngine (QObject *parent=nullptr)
 
 ~KisPlaybackEngine ()
 
- Public Member Functions inherited from KoCanvasObserverBase
 KoCanvasObserverBase ()
 
KoCanvasBaseobservedCanvas () const
 
virtual QString observerName ()
 
void setObservedCanvas (KoCanvasBase *canvas)
 
void unsetObservedCanvas ()
 
virtual ~KoCanvasObserverBase ()
 

Protected Slots

void canvasDestroyed (QObject *canvas)
 
void setAudioVolume (qreal volumeNormalized)
 setAudioVolume
 
void setCanvas (KoCanvasBase *canvas) override
 
void throttledSetSpeed (const double speed)
 throttledSetSpeed
 
void throttledShowFrame (const int frame)
 throttledShowFrame
 
void unsetCanvas () override
 
- Protected Slots inherited from KisPlaybackEngine
virtual void setCanvas (KoCanvasBase *p_canvas) override
 
virtual void unsetCanvas () override
 

Private Member Functions

void setupProducer (boost::optional< QFileInfo > file)
 Sets up an MLT::Producer object in response to audio being added to a Krita document or when canvas changes.
 

Private Attributes

QScopedPointer< Privatem_d
 

Additional Inherited Members

- Protected Member Functions inherited from KisPlaybackEngine
class KisCanvas2activeCanvas () const
 
int frameWrap (int frame, int startFrame, int endFrame)
 
- Protected Member Functions inherited from KoCanvasObserverBase
virtual void setCanvas (KoCanvasBase *canvas)=0
 
virtual void unsetCanvas ()=0
 

Detailed Description

The KisPlaybackEngineMLT class is an implementation of KisPlaybackEngine that uses MLT (Media Lovin' Toolkit) to drive image frame changes and animation audio with (hopefully) close to frame-perfect synchronization.

If MLT is unavailable or unwanted, Krita can instead use KisPlaybackEngineQT which may be simpler but has different characteristics and is not designed with audio-video synchronization in mind.

Definition at line 35 of file KisPlaybackEngineMLT.h.

Constructor & Destructor Documentation

◆ KisPlaybackEngineMLT()

KisPlaybackEngineMLT::KisPlaybackEngineMLT ( QObject * parent = nullptr)
explicit

Definition at line 367 of file KisPlaybackEngineMLT.cpp.

368 : KisPlaybackEngine(parent)
369 , m_d( new Private(this))
370{
372}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void sigChangeActiveCanvasFrame(int p_frame)
QScopedPointer< Private > m_d
void throttledShowFrame(const int frame)
throttledShowFrame
KisPlaybackEngine(QObject *parent=nullptr)

References connect(), sigChangeActiveCanvasFrame(), and throttledShowFrame().

◆ ~KisPlaybackEngineMLT()

KisPlaybackEngineMLT::~KisPlaybackEngineMLT ( )

Definition at line 374 of file KisPlaybackEngineMLT.cpp.

375{
376}

Member Function Documentation

◆ canvasDestroyed

void KisPlaybackEngineMLT::canvasDestroyed ( QObject * canvas)
protectedslot

We cannot use QMap::remove here, because the canvas is already half-destroyed and we cannot up-cast to KisCanvas2 anymore

Definition at line 533 of file KisPlaybackEngineMLT.cpp.

534{
535 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeCanvas() != canvas);
536
541 for (auto it = m_d->canvasProducers.begin(); it != m_d->canvasProducers.end(); ++it) {
542 if (it.key() == canvas) {
543 m_d->canvasProducers.erase(it);
544 break;
545 }
546 }
547}
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References KIS_SAFE_ASSERT_RECOVER_RETURN, and m_d.

◆ frameWaitingInterface()

KisPlaybackEngineMLT::FrameWaitingInterface * KisPlaybackEngineMLT::frameWaitingInterface ( )

Definition at line 590 of file KisPlaybackEngineMLT.cpp.

591{
592 return &m_d->frameWaitingInterface;
593}

References m_d.

◆ isMute

bool KisPlaybackEngineMLT::isMute ( )
overrideslot

Definition at line 613 of file KisPlaybackEngineMLT.cpp.

614{
615 return m_d->mute;
616}

References m_d.

◆ playbackStatistics

KisPlaybackEngine::PlaybackStats KisPlaybackEngineMLT::playbackStatistics ( ) const
overrideslot

Definition at line 618 of file KisPlaybackEngineMLT.cpp.

619{
621
622 if (activeCanvas() && activeCanvas()->animationState() &&
623 m_d->activePlaybackMode() == PLAYBACK_PULL ) {
624
625 const int droppedFrames = m_d->frameStats.droppedFramesCount.rollingSum();
626 const int totalFrames =
627 m_d->frameStats.droppedFramesCount.rollingCount() +
628 droppedFrames;
629
630 stats.droppedFramesPortion = qreal(droppedFrames) / totalFrames;
631 stats.expectedFps = qreal(activeCanvas()->image()->animationInterface()->framerate()) * m_d->playbackSpeed;
632
633 const qreal avgTimePerFrame = m_d->frameStats.averageFrameDuration.rollingMeanSafe();
634 stats.realFps = !qFuzzyIsNull(avgTimePerFrame) ? 1000.0 / avgTimePerFrame : 0.0;
635
636 }
637
638 return stats;
639}
@ PLAYBACK_PULL
class KisCanvas2 * activeCanvas() const
static bool qFuzzyIsNull(half h)

References KisPlaybackEngine::activeCanvas(), KisPlaybackEngine::PlaybackStats::droppedFramesPortion, KisPlaybackEngine::PlaybackStats::expectedFps, m_d, PLAYBACK_PULL, qFuzzyIsNull(), and KisPlaybackEngine::PlaybackStats::realFps.

◆ seek

void KisPlaybackEngineMLT::seek ( int frameIndex,
SeekOptionFlags flags = SEEK_FINALIZE | SEEK_PUSH_AUDIO )
overrideslot

Definition at line 378 of file KisPlaybackEngineMLT.cpp.

379{
380 KIS_ASSERT(activeCanvas() && activeCanvas()->animationState());
382
383 if (m_d->activePlaybackMode() == PLAYBACK_PUSH) {
384 m_d->canvasProducers[activeCanvas()]->seek(frameIndex);
385
386 if (flags & SEEK_PUSH_AUDIO) {
387
388 m_d->sigPushAudioCompressor->start(frameIndex);
389 }
390
391 animationState->showFrame(frameIndex, (flags & SEEK_FINALIZE) > 0);
392 }
393}
@ PLAYBACK_PUSH
@ SEEK_PUSH_AUDIO
@ SEEK_FINALIZE
KisCanvasAnimationState * animationState() const
The KisCanvasAnimationState class stores all of the canvas-specific animation state.
void showFrame(int frame, bool finalize=false)
#define KIS_ASSERT(cond)
Definition kis_assert.h:33

References KisPlaybackEngine::activeCanvas(), KisCanvas2::animationState(), KIS_ASSERT, m_d, PLAYBACK_PUSH, SEEK_FINALIZE, SEEK_PUSH_AUDIO, and KisCanvasAnimationState::showFrame().

◆ setAudioVolume

void KisPlaybackEngineMLT::setAudioVolume ( qreal volumeNormalized)
protectedslot

setAudioVolume

Parameters
volume(normalized)

Definition at line 579 of file KisPlaybackEngineMLT.cpp.

580{
581 if (m_d->mute) {
582 m_d->pullConsumer->set("volume", 0.0);
583 m_d->pushConsumer->set("volume", 0.0);
584 } else {
585 m_d->pullConsumer->set("volume", volumeNormalized);
586 m_d->pushConsumer->set("volume", volumeNormalized);
587 }
588}

References m_d.

◆ setCanvas

void KisPlaybackEngineMLT::setCanvas ( KoCanvasBase * canvas)
overrideprotectedslot

Both, producer and consumers store frame rate in their private properties and do not track updates in the profile, so we should recreate all the three to actually make the changes to take effect.

Theoretically, we could just call set_profile on all the three, but the fact that we embed one more producer into our own one, prevents it from working.

Definition at line 438 of file KisPlaybackEngineMLT.cpp.

439{
440 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(p_canvas);
441
442 if (activeCanvas() == canvas) {
443 return;
444 }
445
446 if (activeCanvas()) {
448
449 // Disconnect old canvas, prepare for new one..
450 if (animationState) {
451 this->disconnect(animationState);
452 animationState->disconnect(this);
453 }
454
455 // Disconnect old image, prepare for new one..
456 auto image = activeCanvas()->image();
457 if (image && image->animationInterface()) {
458 this->disconnect(image->animationInterface());
459 image->animationInterface()->disconnect(this);
460 }
461 }
462
463 StopAndResume stopResume(m_d.data(), true);
464
466
467 // Connect new canvas..
468 if (activeCanvas()) {
470 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
471
472 connect(animationState, &KisCanvasAnimationState::sigPlaybackStateChanged, this, [this](PlaybackState state){
473 Q_UNUSED(state); // We don't need the state yet -- we just want to stop and resume playback according to new state info.
474 StopAndResume callbackStopResume(m_d.data());
475 });
476
477 connect(animationState, &KisCanvasAnimationState::sigPlaybackMediaChanged, this, [this](){
479 if (animationState) {
480 setupProducer(animationState->mediaInfo());
481 }
482 });
483
484 connect(animationState, &KisCanvasAnimationState::sigPlaybackSpeedChanged, this, [this](qreal value){
485 m_d->sigSetPlaybackSpeed->start(value);
486 });
487 m_d->playbackSpeed = animationState->playbackSpeed();
488
490
491 auto image = activeCanvas()->image();
493
494 // Connect new image..
495 connect(image->animationInterface(), &KisImageAnimationInterface::sigFramerateChanged, this, [this](){
496 StopAndResume callbackStopResume(m_d.data());
497 m_d->profile->set_frame_rate(activeCanvas()->image()->animationInterface()->framerate(), 1);
498
507 KisCanvasAnimationState* animationState = activeCanvas()->animationState();
508 if (animationState) {
509 setupProducer(animationState->mediaInfo());
510 }
511 });
512
513 // cold init the framerate
514 m_d->profile->set_frame_rate(activeCanvas()->image()->animationInterface()->framerate(), 1);
515
516 connect(image->animationInterface(), &KisImageAnimationInterface::sigPlaybackRangeChanged, this, [this](){
517 QSharedPointer<Mlt::Producer> producer = m_d->canvasProducers[activeCanvas()];
518 auto image = activeCanvas()->image();
519 KIS_SAFE_ASSERT_RECOVER_RETURN(image);
520 producer->set("start_frame", image->animationInterface()->activePlaybackRange().start());
521 producer->set("end_frame", image->animationInterface()->activePlaybackRange().end());
522 });
523
524 setupProducer(animationState->mediaInfo());
525 }
526
527}
float value(const T *src, size_t ch)
KisImageWSP image() const
void sigAudioLevelChanged(qreal value)
void sigPlaybackStateChanged(PlaybackState state)
boost::optional< QFileInfo > mediaInfo()
Get the media file info associated with this canvas, if available.
void sigPlaybackSpeedChanged(qreal value)
void setupProducer(boost::optional< QFileInfo > file)
Sets up an MLT::Producer object in response to audio being added to a Krita document or when canvas c...
void setAudioVolume(qreal volumeNormalized)
setAudioVolume
virtual void setCanvas(KoCanvasBase *p_canvas) override

References KisPlaybackEngine::activeCanvas(), KisCanvas2::animationState(), connect(), KisCanvas2::image(), KIS_SAFE_ASSERT_RECOVER_RETURN, m_d, KisCanvasAnimationState::mediaInfo(), KisCanvasAnimationState::playbackSpeed(), setAudioVolume(), KisPlaybackEngine::setCanvas(), setupProducer(), KisCanvasAnimationState::sigAudioLevelChanged(), KisImageAnimationInterface::sigFramerateChanged(), KisCanvasAnimationState::sigPlaybackMediaChanged(), KisImageAnimationInterface::sigPlaybackRangeChanged(), KisCanvasAnimationState::sigPlaybackSpeedChanged(), KisCanvasAnimationState::sigPlaybackStateChanged(), and value().

◆ setDropFramesMode

void KisPlaybackEngineMLT::setDropFramesMode ( bool value)
overrideslot

Definition at line 595 of file KisPlaybackEngineMLT.cpp.

596{
597 // restart playback if it was active
598 StopAndResume r(m_d.data(), false);
599
601}
virtual void setDropFramesMode(bool value)

References m_d, KisPlaybackEngine::setDropFramesMode(), and value().

◆ setMute

void KisPlaybackEngineMLT::setMute ( bool val)
overrideslot

Definition at line 603 of file KisPlaybackEngineMLT.cpp.

604{
607
608 qreal currentVolume = animationState->currentVolume();
609 m_d->mute = val;
610 setAudioVolume(currentVolume);
611}

References KisPlaybackEngine::activeCanvas(), KisCanvas2::animationState(), KisCanvasAnimationState::currentVolume(), KIS_SAFE_ASSERT_RECOVER_RETURN, m_d, and setAudioVolume().

◆ setupProducer()

void KisPlaybackEngineMLT::setupProducer ( boost::optional< QFileInfo > file)
private

Sets up an MLT::Producer object in response to audio being added to a Krita document or when canvas changes.

Parameters
fileAn optional file to be loaded by MLT.

Definition at line 395 of file KisPlaybackEngineMLT.cpp.

396{
397 if (!m_d->canvasProducers.contains(activeCanvas())) {
398 connect(activeCanvas(), SIGNAL(destroyed(QObject*)), this, SLOT(canvasDestroyed(QObject*)));
399 }
400
401 //First, assign to "count" producer.
402 m_d->canvasProducers[activeCanvas()] = QSharedPointer<Mlt::Producer>(new Mlt::Producer(*m_d->profile, "krita_play_chunk", "count"));
403
404 //If we have a file and the file has a valid producer, use that. Otherwise, stick to our "default" producer.
405 if (file.has_value()) {
407
408#ifdef Q_OS_ANDROID
409 new Mlt::Producer(*m_d->profile,
410 "krita_play_chunk",
411 KisAndroidFileProxy::getFileFromContentUri(file->absoluteFilePath()).toUtf8().data()));
412#else
413 new Mlt::Producer(*m_d->profile, "krita_play_chunk", file->absoluteFilePath().toUtf8().data()));
414#endif
415 if (producer->is_valid()) {
416 m_d->canvasProducers[activeCanvas()] = producer;
417 } else {
418 // SANITY CHECK: Check that the MLT plugins and resources are where the program expects them to be.
419 // HINT -- Check krita/main.cc's mlt environment variable setup for appimage.
420 KIS_SAFE_ASSERT_RECOVER_NOOP(qEnvironmentVariableIsSet("MLT_REPOSITORY"));
421 KIS_SAFE_ASSERT_RECOVER_NOOP(qEnvironmentVariableIsSet("MLT_PROFILES_PATH"));
422 KIS_SAFE_ASSERT_RECOVER_NOOP(qEnvironmentVariableIsSet("MLT_PRESETS_PATH"));
423 qDebug() << "Warning: Invalid MLT producer for file: " << ppVar(file->absoluteFilePath()) << " Falling back to audio-less playback.";
424 }
425 }
426
428 QSharedPointer<Mlt::Producer> producer = m_d->canvasProducers[activeCanvas()];
429 KIS_ASSERT(producer->is_valid());
430 KIS_ASSERT(animInterface);
431
432 producer->set("start_frame", animInterface->documentPlaybackRange().start());
433 producer->set("end_frame", animInterface->documentPlaybackRange().end());
434 producer->set("limit_enabled", false);
435 producer->set("speed", m_d->playbackSpeed);
436}
static QString getFileFromContentUri(QString contentUri)
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
KisImageAnimationInterface * animationInterface() const
void canvasDestroyed(QObject *canvas)
int start() const
int end() const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155

References KisPlaybackEngine::activeCanvas(), KisImage::animationInterface(), canvasDestroyed(), connect(), KisImageAnimationInterface::documentPlaybackRange(), KisTimeSpan::end(), KisAndroidFileProxy::getFileFromContentUri(), KisCanvas2::image(), KIS_ASSERT, KIS_SAFE_ASSERT_RECOVER_NOOP, m_d, ppVar, and KisTimeSpan::start().

◆ sigChangeActiveCanvasFrame

void KisPlaybackEngineMLT::sigChangeActiveCanvasFrame ( int p_frame)
signal

◆ supportsAudio

bool KisPlaybackEngineMLT::supportsAudio ( )
inlineoverrideslot

Definition at line 51 of file KisPlaybackEngineMLT.h.

51{ return true; }

◆ supportsVariablePlaybackSpeed

bool KisPlaybackEngineMLT::supportsVariablePlaybackSpeed ( )
inlineoverrideslot

Definition at line 52 of file KisPlaybackEngineMLT.h.

52{ return true; }

◆ throttledSetSpeed

void KisPlaybackEngineMLT::throttledSetSpeed ( const double speed)
protectedslot

throttledSetSpeed

Parameters
speed

Because MLT needs to be stopped and restarted to change playback speed we use this function to limit the frequency of speed change requests.

Definition at line 573 of file KisPlaybackEngineMLT.cpp.

574{
575 StopAndResume stopResume(m_d.data(), false);
576 m_d->playbackSpeed = speed;
577}

References m_d.

◆ throttledShowFrame

void KisPlaybackEngineMLT::throttledShowFrame ( const int frame)
protectedslot

throttledShowFrame

Parameters
frame

In order to throttle calls from MLT to respect our playback mode, we need to redirect showFrame calls to this thread and enforce that we only allow MLT to show frames when we are in PULL mode.

Definition at line 549 of file KisPlaybackEngineMLT.cpp.

550{
551 if (activeCanvas() && activeCanvas()->animationState() &&
552 m_d->activePlaybackMode() == PLAYBACK_PULL ) {
553
554 if (m_d->frameStats.lastRenderedFrame < 0) {
555 m_d->frameStats.timeSinceLastFrame.start();
556 } else {
557 const int droppedFrames = qMax(0, frame - m_d->frameStats.lastRenderedFrame - 1);
558 m_d->frameStats.averageFrameDuration(m_d->frameStats.timeSinceLastFrame.restart());
559 m_d->frameStats.droppedFramesCount(droppedFrames);
560 }
561 m_d->frameStats.lastRenderedFrame = frame;
562
564 }
565
566 {
567 QMutexLocker l(&m_d->frameWaitingInterface.renderingControlMutex);
568 m_d->frameWaitingInterface.waitingForFrame = false;
569 m_d->frameWaitingInterface.renderingWaitCondition.wakeAll();
570 }
571}

References KisPlaybackEngine::activeCanvas(), KisCanvas2::animationState(), m_d, PLAYBACK_PULL, and KisCanvasAnimationState::showFrame().

◆ unsetCanvas

void KisPlaybackEngineMLT::unsetCanvas ( )
overrideprotectedslot

Definition at line 529 of file KisPlaybackEngineMLT.cpp.

529 {
530 setCanvas(nullptr);
531}
void setCanvas(KoCanvasBase *canvas) override

References setCanvas().

Member Data Documentation

◆ m_d

QScopedPointer<Private> KisPlaybackEngineMLT::m_d
private

Definition at line 104 of file KisPlaybackEngineMLT.h.


The documentation for this class was generated from the following files: