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 369 of file KisPlaybackEngineMLT.cpp.

370 : KisPlaybackEngine(parent)
371 , m_d( new Private(this))
372{
374}
void sigChangeActiveCanvasFrame(int p_frame)
QScopedPointer< Private > m_d
void throttledShowFrame(const int frame)
throttledShowFrame
KisPlaybackEngine(QObject *parent=nullptr)

References sigChangeActiveCanvasFrame(), and throttledShowFrame().

◆ ~KisPlaybackEngineMLT()

KisPlaybackEngineMLT::~KisPlaybackEngineMLT ( )

Definition at line 376 of file KisPlaybackEngineMLT.cpp.

377{
378}

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 535 of file KisPlaybackEngineMLT.cpp.

536{
537 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeCanvas() != canvas);
538
543 for (auto it = m_d->canvasProducers.begin(); it != m_d->canvasProducers.end(); ++it) {
544 if (it.key() == canvas) {
545 m_d->canvasProducers.erase(it);
546 break;
547 }
548 }
549}
#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 592 of file KisPlaybackEngineMLT.cpp.

593{
594 return &m_d->frameWaitingInterface;
595}

References m_d.

◆ isMute

bool KisPlaybackEngineMLT::isMute ( )
overrideslot

Definition at line 615 of file KisPlaybackEngineMLT.cpp.

616{
617 return m_d->mute;
618}

References m_d.

◆ playbackStatistics

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

Definition at line 620 of file KisPlaybackEngineMLT.cpp.

621{
623
624 if (activeCanvas() && activeCanvas()->animationState() &&
625 m_d->activePlaybackMode() == PLAYBACK_PULL ) {
626
627 const int droppedFrames = m_d->frameStats.droppedFramesCount.rollingSum();
628 const int totalFrames =
629 m_d->frameStats.droppedFramesCount.rollingCount() +
630 droppedFrames;
631
632 stats.droppedFramesPortion = qreal(droppedFrames) / totalFrames;
633 stats.expectedFps = qreal(activeCanvas()->image()->animationInterface()->framerate()) * m_d->playbackSpeed;
634
635 const qreal avgTimePerFrame = m_d->frameStats.averageFrameDuration.rollingMeanSafe();
636 stats.realFps = !qFuzzyIsNull(avgTimePerFrame) ? 1000.0 / avgTimePerFrame : 0.0;
637
638 }
639
640 return stats;
641}
@ 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 380 of file KisPlaybackEngineMLT.cpp.

381{
382 KIS_ASSERT(activeCanvas() && activeCanvas()->animationState());
384
385 if (m_d->activePlaybackMode() == PLAYBACK_PUSH) {
386 m_d->canvasProducers[activeCanvas()]->seek(frameIndex);
387
388 if (flags & SEEK_PUSH_AUDIO) {
389
390 m_d->sigPushAudioCompressor->start(frameIndex);
391 }
392
393 animationState->showFrame(frameIndex, (flags & SEEK_FINALIZE) > 0);
394 }
395}
@ 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 581 of file KisPlaybackEngineMLT.cpp.

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

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 440 of file KisPlaybackEngineMLT.cpp.

441{
442 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(p_canvas);
443
444 if (activeCanvas() == canvas) {
445 return;
446 }
447
448 if (activeCanvas()) {
450
451 // Disconnect old canvas, prepare for new one..
452 if (animationState) {
453 this->disconnect(animationState);
454 animationState->disconnect(this);
455 }
456
457 // Disconnect old image, prepare for new one..
458 auto image = activeCanvas()->image();
459 if (image && image->animationInterface()) {
460 this->disconnect(image->animationInterface());
461 image->animationInterface()->disconnect(this);
462 }
463 }
464
465 StopAndResume stopResume(m_d.data(), true);
466
468
469 // Connect new canvas..
470 if (activeCanvas()) {
472 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
473
474 connect(animationState, &KisCanvasAnimationState::sigPlaybackStateChanged, this, [this](PlaybackState state){
475 Q_UNUSED(state); // We don't need the state yet -- we just want to stop and resume playback according to new state info.
476 StopAndResume callbackStopResume(m_d.data());
477 });
478
479 connect(animationState, &KisCanvasAnimationState::sigPlaybackMediaChanged, this, [this](){
481 if (animationState) {
482 setupProducer(animationState->mediaInfo());
483 }
484 });
485
486 connect(animationState, &KisCanvasAnimationState::sigPlaybackSpeedChanged, this, [this](qreal value){
487 m_d->sigSetPlaybackSpeed->start(value);
488 });
489 m_d->playbackSpeed = animationState->playbackSpeed();
490
492
493 auto image = activeCanvas()->image();
495
496 // Connect new image..
497 connect(image->animationInterface(), &KisImageAnimationInterface::sigFramerateChanged, this, [this](){
498 StopAndResume callbackStopResume(m_d.data());
499 m_d->profile->set_frame_rate(activeCanvas()->image()->animationInterface()->framerate(), 1);
500
509 KisCanvasAnimationState* animationState = activeCanvas()->animationState();
510 if (animationState) {
511 setupProducer(animationState->mediaInfo());
512 }
513 });
514
515 // cold init the framerate
516 m_d->profile->set_frame_rate(activeCanvas()->image()->animationInterface()->framerate(), 1);
517
518 connect(image->animationInterface(), &KisImageAnimationInterface::sigPlaybackRangeChanged, this, [this](){
519 QSharedPointer<Mlt::Producer> producer = m_d->canvasProducers[activeCanvas()];
520 auto image = activeCanvas()->image();
521 KIS_SAFE_ASSERT_RECOVER_RETURN(image);
522 producer->set("start_frame", image->animationInterface()->activePlaybackRange().start());
523 producer->set("end_frame", image->animationInterface()->activePlaybackRange().end());
524 });
525
526 setupProducer(animationState->mediaInfo());
527 }
528
529}
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(), 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 597 of file KisPlaybackEngineMLT.cpp.

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

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

◆ setMute

void KisPlaybackEngineMLT::setMute ( bool val)
overrideslot

Definition at line 605 of file KisPlaybackEngineMLT.cpp.

606{
609
610 qreal currentVolume = animationState->currentVolume();
611 m_d->mute = val;
612 setAudioVolume(currentVolume);
613}

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 397 of file KisPlaybackEngineMLT.cpp.

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

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

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 551 of file KisPlaybackEngineMLT.cpp.

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

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

◆ unsetCanvas

void KisPlaybackEngineMLT::unsetCanvas ( )
overrideprotectedslot

Definition at line 531 of file KisPlaybackEngineMLT.cpp.

531 {
532 setCanvas(nullptr);
533}
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: