diff --git a/include/AudioReaderSource.h b/include/AudioReaderSource.h index e5756afb..c788fab7 100644 --- a/include/AudioReaderSource.h +++ b/include/AudioReaderSource.h @@ -58,6 +58,7 @@ namespace openshot bool repeat; /// Repeat the audio source when finished int size; /// The size of the internal buffer AudioSampleBuffer *buffer; /// The audio sample buffer + int speed; /// The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) ReaderBase *reader; /// The reader to pull samples from int64 original_frame_number; /// The current frame to read from @@ -70,6 +71,9 @@ namespace openshot /// Get more samples from the reader void GetMoreSamplesFromReader(); + /// Reverse an audio buffer (for backwards audio) + juce::AudioSampleBuffer* reverse_buffer(juce::AudioSampleBuffer* buffer); + public: /// @brief Constructor that reads samples from a reader @@ -111,9 +115,26 @@ namespace openshot void setBuffer (AudioSampleBuffer *audio_buffer); const ReaderInfo & getReaderInfo() const { return reader->info; } + + /// Return the current frame object tr1::shared_ptr getFrame() const { return frame; } - int getFramePosition() const { return frame_position; } + + /// Get the estimate frame that is playing at this moment int getEstimatedFrame() const { return int(estimated_frame); } + + /// Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + void setSpeed(int new_speed) { speed = new_speed; } + /// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + int getSpeed() const { return speed; } + + /// Set Reader + void Reader(ReaderBase *audio_reader) { reader = audio_reader; } + /// Get Reader + ReaderBase* Reader() const { return reader; } + + /// Seek to a specific frame + int Seek(int64 new_position) { frame_number = new_position; } + }; } diff --git a/include/PlayerBase.h b/include/PlayerBase.h index c6d938f1..7c4cbd85 100644 --- a/include/PlayerBase.h +++ b/include/PlayerBase.h @@ -68,6 +68,9 @@ namespace openshot /// Display a loading animation virtual void Loading() = 0; + /// Get the current mode + virtual PlaybackMode Mode() = 0; + /// Play the video virtual void Play() = 0; @@ -81,25 +84,25 @@ namespace openshot virtual void Seek(int new_frame) = 0; /// Get the Playback speed - float Speed(); + virtual float Speed() = 0; /// Set the Playback speed (1.0 = normal speed, <1.0 = slower, >1.0 faster) - void Speed(float new_speed); + virtual void Speed(float new_speed) = 0; /// Stop the video player and clear the cached frames virtual void Stop() = 0; /// Get the current reader, such as a FFmpegReader - ReaderBase* Reader(); + virtual ReaderBase* Reader() = 0; /// Set the current reader, such as a FFmpegReader - void Reader(ReaderBase *new_reader); + virtual void Reader(ReaderBase *new_reader) = 0; /// Get the Volume - float Volume(); + virtual float Volume() = 0; /// Set the Volume (1.0 = normal volume, <1.0 = quieter, >1.0 louder) - void Volume(float new_volume); + virtual void Volume(float new_volume) = 0; }; diff --git a/include/Qt/PlayerDemo.h b/include/Qt/PlayerDemo.h index fa53972a..9f1a372a 100644 --- a/include/Qt/PlayerDemo.h +++ b/include/Qt/PlayerDemo.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace openshot { @@ -26,6 +27,9 @@ public: PlayerDemo(QWidget *parent = 0); ~PlayerDemo(); +protected: + void keyPressEvent(QKeyEvent *event); + private slots: void open(bool checked); diff --git a/include/QtPlayer.h b/include/QtPlayer.h index a7f24215..65bc30e5 100644 --- a/include/QtPlayer.h +++ b/include/QtPlayer.h @@ -47,6 +47,7 @@ namespace openshot class QtPlayer : public PlayerBase { PlayerPrivate *p; + bool threads_started; public: /// Default constructor @@ -64,6 +65,9 @@ namespace openshot /// Display a loading animation void Loading(); + /// Get the current mode + PlaybackMode Mode(); + /// Pause the video void Pause(); @@ -73,8 +77,26 @@ namespace openshot /// Seek to a specific frame in the player void Seek(int new_frame); + /// Get the Playback speed + float Speed(); + + /// Set the Playback speed (1.0 = normal speed, <1.0 = slower, >1.0 faster) + void Speed(float new_speed); + /// Stop the video player and clear the cached frames void Stop(); + + /// Set the current reader + void Reader(ReaderBase *new_reader); + + /// Get the current reader, such as a FFmpegReader + ReaderBase* Reader(); + + /// Get the Volume + float Volume(); + + /// Set the Volume (1.0 = normal volume, <1.0 = quieter, >1.0 louder) + void Volume(float new_volume); }; } diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp index 0569bf33..677ee308 100644 --- a/src/AudioReaderSource.cpp +++ b/src/AudioReaderSource.cpp @@ -33,7 +33,7 @@ using namespace openshot; // Constructor that reads samples from a reader AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64 starting_frame_number, int buffer_size) : reader(audio_reader), frame_number(starting_frame_number), original_frame_number(starting_frame_number), - size(buffer_size), position(0), frame_position(0), estimated_frame(0) { + size(buffer_size), position(0), frame_position(0), estimated_frame(0), speed(1) { // Initialize an audio buffer (based on reader) buffer = new juce::AudioSampleBuffer(reader->info.channels, size); @@ -79,13 +79,14 @@ void AudioReaderSource::GetMoreSamplesFromReader() { position = 0; // Loop through frames until buffer filled - while (amount_needed > 0) { + while (amount_needed > 0 && speed != 0) { // Get the next frame (if position is zero) if (frame_position == 0) { try { // Get frame object - frame = reader->GetFrameSafe(frame_number++); + frame = reader->GetFrameSafe(frame_number); + frame_number = frame_number + speed; } catch (const ReaderClosed & e) { break; @@ -110,7 +111,12 @@ void AudioReaderSource::GetMoreSamplesFromReader() { // Load all of its samples into the buffer for (int channel = 0; channel < new_buffer->getNumChannels(); channel++) - new_buffer->addFrom(channel, position, *frame->GetAudioSampleBuffer(), channel, frame_position, amount_to_copy); + if (speed >= 0) + // playback normal + new_buffer->addFrom(channel, position, *frame->GetAudioSampleBuffer(), channel, frame_position, amount_to_copy); + else + // reverse playback + new_buffer->addFrom(channel, position, *reverse_buffer(frame->GetAudioSampleBuffer()), channel, frame_position, amount_to_copy); // Adjust remaining samples position += amount_to_copy; @@ -131,6 +137,37 @@ void AudioReaderSource::GetMoreSamplesFromReader() { position = 0; } +// Reverse an audio buffer +juce::AudioSampleBuffer* AudioReaderSource::reverse_buffer(juce::AudioSampleBuffer* buffer) +{ + int number_of_samples = buffer->getNumSamples(); + int channels = buffer->getNumChannels(); + + // Reverse array (create new buffer to hold the reversed version) + AudioSampleBuffer *reversed = new juce::AudioSampleBuffer(channels, number_of_samples); + reversed->clear(); + + for (int channel = 0; channel < channels; channel++) + { + int n=0; + for (int s = number_of_samples - 1; s >= 0; s--, n++) + reversed->getSampleData(channel)[n] = buffer->getSampleData(channel)[s]; + } + + // Copy the samples back to the original array + buffer->clear(); + // Loop through channels, and get audio samples + for (int channel = 0; channel < channels; channel++) + // Get the audio samples for this channel + buffer->addFrom(channel, 0, reversed->getSampleData(channel), number_of_samples, 1.0f); + + delete reversed; + reversed = NULL; + + // return pointer or passed in object (so this method can be chained together) + return buffer; +} + // Get the next block of audio samples void AudioReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { @@ -182,7 +219,8 @@ void AudioReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) // Adjust estimate frame number (the estimated frame number that is being played) estimated_samples_per_frame = Frame::GetSamplesPerFrame(estimated_frame, reader->info.fps, reader->info.sample_rate); - estimated_frame += double(info.numSamples) / double(estimated_samples_per_frame); + if (speed != 0) + estimated_frame += double(info.numSamples) / double(estimated_samples_per_frame); } } diff --git a/src/Qt/AudioPlaybackThread.cpp b/src/Qt/AudioPlaybackThread.cpp index 2aff987d..10ba5d9b 100644 --- a/src/Qt/AudioPlaybackThread.cpp +++ b/src/Qt/AudioPlaybackThread.cpp @@ -2,6 +2,7 @@ * @file * @brief Source file for AudioPlaybackThread class * @author Duzy Chan + * @author Jonathan Thomas * * * @section LICENSE * @@ -44,6 +45,7 @@ namespace openshot } }; + // Construtor AudioPlaybackThread::AudioPlaybackThread() : Thread("audio-playback") , audioDeviceManager() @@ -57,28 +59,41 @@ namespace openshot { } + // Destructor AudioPlaybackThread::~AudioPlaybackThread() { } - void AudioPlaybackThread::setReader(ReaderBase *reader) + // Set the reader object + void AudioPlaybackThread::Reader(ReaderBase *reader) { - sampleRate = reader->info.sample_rate; - numChannels = reader->info.channels; - source = new AudioReaderSource(reader, 1, buffer_size); + if (!source) { + sampleRate = reader->info.sample_rate; + numChannels = reader->info.channels; + source = new AudioReaderSource(reader, 1, buffer_size); + } } + // Get the current frame object (which is filling the buffer) tr1::shared_ptr AudioPlaybackThread::getFrame() { if (source) return source->getFrame(); return tr1::shared_ptr(); } + // Get the currently playing frame number int AudioPlaybackThread::getCurrentFramePosition() { return source ? source->getEstimatedFrame() : 0; } + // Seek the audio thread + void AudioPlaybackThread::Seek(int new_position) + { + source->Seek(new_position); + } + + // Start audio thread void AudioPlaybackThread::run() { // Init audio device @@ -113,7 +128,7 @@ namespace openshot transport.start(); while (!threadShouldExit() && transport.isPlaying()) { - sleep(1); + sleep(100); } transport.stop(); @@ -124,5 +139,9 @@ namespace openshot audioDeviceManager.closeAudioDevice(); audioDeviceManager.removeAllChangeListeners(); audioDeviceManager.dispatchPendingMessages(); + + // Remove source + delete source; + source = NULL; } } diff --git a/src/Qt/AudioPlaybackThread.h b/src/Qt/AudioPlaybackThread.h index bb8b848c..770f8edc 100644 --- a/src/Qt/AudioPlaybackThread.h +++ b/src/Qt/AudioPlaybackThread.h @@ -2,6 +2,7 @@ * @file * @brief Source file for AudioPlaybackThread class * @author Duzy Chan + * @author Jonathan Thomas * * @section LICENSE * @@ -31,7 +32,7 @@ namespace openshot using juce::WaitableEvent; /** - * @brief The audio playback class. + * @brief The audio playback thread */ class AudioPlaybackThread : Thread { @@ -47,15 +48,32 @@ namespace openshot WaitableEvent played; int buffer_size; + /// Constructor AudioPlaybackThread(); + /// Destructor ~AudioPlaybackThread(); - void setReader(ReaderBase *reader); + /// Set the current thread's reader + void Reader(ReaderBase *reader); + + /// Get the current frame object (which is filling the buffer) tr1::shared_ptr getFrame(); + + /// Get the current frame number being played int getCurrentFramePosition(); + /// Seek the audio thread + void Seek(int new_position); + + /// Start thread void run(); + /// Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + void setSpeed(int new_speed) { if (source) source->setSpeed(new_speed); } + + /// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + int getSpeed() const { if (source) return source->getSpeed(); else return 1; } + friend class PlayerPrivate; }; diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp index 8185007c..5fbac427 100644 --- a/src/Qt/PlayerDemo.cpp +++ b/src/Qt/PlayerDemo.cpp @@ -2,6 +2,8 @@ * @file player.cpp */ +#include "stdio.h" +#include "string.h" #include "../../../include/QtPlayer.h" #include "../../../include/Qt/PlayerDemo.h" #include "../../../include/Qt/VideoRenderWidget.h" @@ -30,6 +32,10 @@ PlayerDemo::PlayerDemo(QWidget *parent) vbox->setMargin(0); vbox->setSpacing(0); resize(600, 480); + + // Accept keyboard event + setFocusPolicy(Qt::StrongFocus); + } PlayerDemo::~PlayerDemo() @@ -37,6 +43,42 @@ PlayerDemo::~PlayerDemo() delete player; } +void PlayerDemo::keyPressEvent(QKeyEvent *event) +{ + string key = event->text().toStdString(); + if (key == " ") { + cout << "START / STOP: " << player->Mode() << endl; + if (player->Mode() == openshot::PLAYBACK_PLAY) + player->Pause(); + else if (player->Mode() == openshot::PLAYBACK_PAUSED) + player->Play(); + + } + else if (key == "j") { + cout << "BACKWARD" << player->Speed() - 1 << endl; + int current_speed = player->Speed(); + player->Speed(current_speed - 1); // backwards + + } + else if (key == "k") { + cout << "PAUSE" << endl; + if (player->Mode() == openshot::PLAYBACK_PLAY) + player->Pause(); + else if (player->Mode() == openshot::PLAYBACK_PAUSED) + player->Play(); + + } + else if (key == "l") { + cout << "FORWARD" << player->Speed() + 1 << endl; + int current_speed = player->Speed(); + player->Speed(current_speed + 1); // backwards + + } + + event->accept(); + QWidget::keyPressEvent(event); +} + void PlayerDemo::open(bool checked) { const QString filename = QFileDialog::getOpenFileName(this, "Open Video File"); diff --git a/src/Qt/PlayerPrivate.cpp b/src/Qt/PlayerPrivate.cpp index 00707089..dd8408c9 100644 --- a/src/Qt/PlayerPrivate.cpp +++ b/src/Qt/PlayerPrivate.cpp @@ -25,22 +25,20 @@ * You should have received a copy of the GNU General Public License * along with OpenShot Library. If not, see . */ -#include "../include/ReaderBase.h" -#include "../include/RendererBase.h" -#include "../include/AudioReaderSource.h" + #include "PlayerPrivate.h" -#include "AudioPlaybackThread.h" -#include "VideoPlaybackThread.h" namespace openshot { + // Constructor PlayerPrivate::PlayerPrivate(RendererBase *rb) : Thread("player"), video_position(0), audio_position(0) , audioPlayback(new AudioPlaybackThread()) , videoPlayback(new VideoPlaybackThread(rb)) - { - } + , speed(1), reader(NULL) + { } + // Destructor PlayerPrivate::~PlayerPrivate() { if (isThreadRunning()) stopThread(500); @@ -50,13 +48,15 @@ namespace openshot delete videoPlayback; } + // Start thread void PlayerPrivate::run() { + // Kill audio and video threads (if they are currently running) if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(-1); if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(-1); // Set the reader for the Audio thread - audioPlayback->setReader(reader); + audioPlayback->Reader(reader); // Start the threads if (reader->info.has_audio) @@ -67,6 +67,15 @@ namespace openshot tr1::shared_ptr frame; while (!threadShouldExit()) { + // Calculate the milliseconds a single frame should stay on the screen + double frame_time = (1000.0 / reader->info.fps.ToDouble()); + + // Experimental Pausing Code + if (speed == 0) { + sleep(frame_time); + continue; + } + // Get the start time (to track how long a frame takes to render) const Time t1 = Time::getCurrentTime(); @@ -88,9 +97,6 @@ namespace openshot // Get the end time (to track how long a frame takes to render) const Time t2 = Time::getCurrentTime(); - // Calculate the milliseconds a single frame should stay on the screen - double frame_time = (1000.0 / reader->info.fps.ToDouble()); - // Determine how many milliseconds it took to render the frame int64 render_time = t2.toMilliseconds() - t1.toMilliseconds(); @@ -112,25 +118,24 @@ namespace openshot // Debug output std::cout << "video frame diff: " << video_frame_diff << std::endl; - // Determine if the next frame will be the end of stream - //if ((video_position + 1) > reader->info.video_length) - //{ - // End threads at END OF STREAM - //if (reader->info.has_audio) - // audioPlayback->stopThread(1); - //if (reader->info.has_video) - // videoPlayback->stopThread(2); - //} } + std::cout << "stopped thread" << endl; + + // Kill audio and video threads (if they are still running) if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(-1); if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(-1); } + // Get the next displayed frame (based on speed and direction) tr1::shared_ptr PlayerPrivate::getFrame() { try { - return reader->GetFrameSafe(video_position++); + + // Get the next frame (based on speed) + video_position = video_position + speed; + return reader->GetFrameSafe(video_position); + } catch (const ReaderClosed & e) { // ... } catch (const TooManySeeks & e) { @@ -141,6 +146,7 @@ namespace openshot return tr1::shared_ptr(); } + // Start video/audio playback bool PlayerPrivate::startPlayback() { if (video_position < 0) return false; @@ -149,9 +155,39 @@ namespace openshot return true; } + // Stop video/audio playback void PlayerPrivate::stopPlayback(int timeOutMilliseconds) { + std::cout << "stop playback!!!" << std::endl; if (isThreadRunning()) stopThread(timeOutMilliseconds); } + // Seek to a frame + void PlayerPrivate::Seek(int new_position) + { + // Check for seek + if (new_position > 0) { + // Update current position + video_position = new_position; + + // Notify audio thread that seek has occured + audioPlayback->Seek(video_position); + } + } + + // Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + void PlayerPrivate::Speed(int new_speed) + { + speed = new_speed; + if (reader->info.has_audio) + audioPlayback->setSpeed(new_speed); + } + + // Set the reader object + void PlayerPrivate::Reader(ReaderBase *new_reader) + { + reader = new_reader; + audioPlayback->Reader(new_reader); + } + } diff --git a/src/Qt/PlayerPrivate.h b/src/Qt/PlayerPrivate.h index c1e40993..d054ad17 100644 --- a/src/Qt/PlayerPrivate.h +++ b/src/Qt/PlayerPrivate.h @@ -26,6 +26,12 @@ * along with OpenShot Library. If not, see . */ +#include "../include/ReaderBase.h" +#include "../include/RendererBase.h" +#include "../include/AudioReaderSource.h" +#include "AudioPlaybackThread.h" +#include "VideoPlaybackThread.h" + namespace openshot { class AudioPlaybackThread; @@ -33,26 +39,47 @@ namespace openshot using juce::Thread; /** - * @brief The private part of QtPlayer class. + * @brief The private part of QtPlayer class, which contains an audio thread and video thread, + * and controls the video timing and audio synchronization code. */ class PlayerPrivate : Thread { int video_position; /// The current frame position. int audio_position; /// The current frame position. - ReaderBase *reader; - AudioPlaybackThread *audioPlayback; - VideoPlaybackThread *videoPlayback; + ReaderBase *reader; /// The reader which powers this player + AudioPlaybackThread *audioPlayback; /// The audio thread + VideoPlaybackThread *videoPlayback; /// The video thread + int speed; /// The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + /// Constructor PlayerPrivate(RendererBase *rb); + /// Destructor virtual ~PlayerPrivate(); + /// Start thread void run(); + /// Seek to a frame + void Seek(int new_position); + + /// Start the video/audio playback bool startPlayback(); + + /// Stop the video/audio playback void stopPlayback(int timeOutMilliseconds = -1); + /// Get the next frame (based on speed and direction) tr1::shared_ptr getFrame(); + /// Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + void Speed(int new_speed); + /// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) + int Speed() const { return speed; } + + /// Set the current reader + void Reader(ReaderBase *new_reader); + + /// The parent class of PlayerPrivate friend class QtPlayer; }; diff --git a/src/Qt/VideoPlaybackThread.cpp b/src/Qt/VideoPlaybackThread.cpp index ca69fde5..d41295cc 100644 --- a/src/Qt/VideoPlaybackThread.cpp +++ b/src/Qt/VideoPlaybackThread.cpp @@ -31,16 +31,19 @@ namespace openshot { + // Constructor VideoPlaybackThread::VideoPlaybackThread(RendererBase *rb) : Thread("video-playback"), renderer(rb) , render(), reset(false) { } + // Destructor VideoPlaybackThread::~VideoPlaybackThread() { } + // Get the currently playing frame number (if any) int VideoPlaybackThread::getCurrentFramePosition() { if (frame) @@ -49,11 +52,15 @@ namespace openshot return 0; } + // Start the thread void VideoPlaybackThread::run() { while (!threadShouldExit()) { - render.wait(); + // Make other threads wait on the render event + render.wait(); + // Render the frame to the screen renderer->paint(frame); + // Signal to other threads that the rendered event has completed rendered.signal(); } } diff --git a/src/Qt/VideoPlaybackThread.h b/src/Qt/VideoPlaybackThread.h index 0a56e2e2..aa0837e1 100644 --- a/src/Qt/VideoPlaybackThread.h +++ b/src/Qt/VideoPlaybackThread.h @@ -42,12 +42,18 @@ namespace openshot WaitableEvent rendered; bool reset; + /// Constructor VideoPlaybackThread(RendererBase *rb); + /// Destructor ~VideoPlaybackThread(); + + /// Get the currently playing frame number (if any) int getCurrentFramePosition(); + /// Start the thread void run(); + /// Parent class of VideoPlaybackThread friend class PlayerPrivate; }; diff --git a/src/QtPlayer.cpp b/src/QtPlayer.cpp index 3da864ec..0b28cc1a 100644 --- a/src/QtPlayer.cpp +++ b/src/QtPlayer.cpp @@ -32,15 +32,15 @@ using namespace openshot; -QtPlayer::QtPlayer(RendererBase *rb) : PlayerBase(), p(new PlayerPrivate(rb)) +QtPlayer::QtPlayer(RendererBase *rb) : PlayerBase(), p(new PlayerPrivate(rb)), threads_started(false) { - reader = NULL; + reader = NULL; } QtPlayer::~QtPlayer() { if (mode != PLAYBACK_STOPPED) { - //p->stop(); + Stop(); } delete p; } @@ -49,15 +49,21 @@ void QtPlayer::SetSource(const std::string &source) { reader = new FFmpegReader(source); reader->Open(); + p->Reader(reader); } void QtPlayer::Play() { - mode = PLAYBACK_PLAY; - p->stopPlayback(); - p->video_position = 0; - p->reader = reader; - p->startPlayback(); + cout << "PLAY() on QTPlayer" << endl; + if (reader && !threads_started) { + mode = PLAYBACK_PLAY; + p->Reader(reader); + p->startPlayback(); + threads_started = true; + } else { + mode = PLAYBACK_PLAY; + Speed(1); + } } void QtPlayer::Loading() @@ -65,9 +71,16 @@ void QtPlayer::Loading() mode = PLAYBACK_LOADING; } +/// Get the current mode +PlaybackMode QtPlayer::Mode() +{ + return mode; +} + void QtPlayer::Pause() { mode = PLAYBACK_PAUSED; + Speed(0); } int QtPlayer::Position() @@ -78,10 +91,46 @@ int QtPlayer::Position() void QtPlayer::Seek(int new_frame) { // Seek the reader to a new position - p->video_position = new_frame; + p->Seek(new_frame); } void QtPlayer::Stop() { mode = PLAYBACK_STOPPED; + p->stopPlayback(); + p->video_position = 0; + threads_started = false; +} + +// Set the reader object +void QtPlayer::Reader(ReaderBase *new_reader) +{ + reader = new_reader; + p->Reader(new_reader); +} + +// Get the current reader, such as a FFmpegReader +ReaderBase* QtPlayer::Reader() { + return reader; +} + +// Get the Playback speed +float QtPlayer::Speed() { + return speed; +} + +// Set the Playback speed multiplier (1.0 = normal speed, <1.0 = slower, >1.0 faster) +void QtPlayer::Speed(float new_speed) { + speed = new_speed; + p->Speed(new_speed); +} + +// Get the Volume +float QtPlayer::Volume() { + return volume; +} + +// Set the Volume multiplier (1.0 = normal volume, <1.0 = quieter, >1.0 louder) +void QtPlayer::Volume(float new_volume) { + volume = new_volume; }