Pues ahora hay doble buffer

Falta el hilo que descargue el video de youtube, que se dice mas pronto que se hace
This commit is contained in:
Ruben Garcalia
2026-08-12 01:48:29 +02:00
parent 4d1232821a
commit 81d9952fa3
4 changed files with 305 additions and 65 deletions

66
; Normal file
View File

@@ -0,0 +1,66 @@
#ifndef DOUBLEBUFFERPLAYER_H
#ifndef DOUBLEBUFFERPLAYER_H
#define DOUBLEBUFFERPLAYER_H
#include <QObject>
#include <QMediaPlayer>
#include <QUrl>
#include "logger.hpp"
class DoubleBufferPlayer : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* videoOutput READ videoOutput WRITE setVideoOutput NOTIFY videoOutputChanged)
public:
explicit DoubleBufferPlayer(QObject *parent = nullptr) : QObject(parent)
{
m_playerA = new QMediaPlayer(this);
m_playerB = new QMediaPlayer(this);
m_activePlayer = m_playerA;
m_nextPlayer = m_playerB;
}
QObject* videoOutput() const { return m_videoOutput; }
void setVideoOutput(QObject *output)
{
if (m_videoOutput != output) {
m_videoOutput = output;
if (m_videoOutput) {
m_activePlayer->setVideoOutput(m_videoOutput);
}
emit videoOutputChanged();
}
}
Q_INVOKABLE void prepareNext(const QUrl &url)
{
m_nextPlayer->setSource(url);
}
Q_INVOKABLE void swapAndPlay()
{
if (!m_videoOutput) return;
m_activePlayer->stop();
std::swap(m_activePlayer, m_nextPlayer);
m_activePlayer->setVideoOutput(m_videoOutput);
m_activePlayer->play();
}
signals:
void videoOutputChanged();
private:
QMediaPlayer *m_playerA{nullptr};
QMediaPlayer *m_playerB{nullptr};
QMediaPlayer *m_activePlayer{nullptr};
QMediaPlayer *m_nextPlayer{nullptr};
QObject *m_videoOutput{nullptr};
};
#endif

View File

@@ -1,60 +1,225 @@
#pragma once
#include <QObject>
#include <QString>
#include <QUrl>
#include <format>
#include <qcoreapplication.h>
#include <vector>
#include "logger.hpp"
#include <QAudioOutput>
#include <QMediaPlayer>
#include <QObject>
#include <QUrl>
#include <algorithm>
#include <qcoreapplication.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include <qurl.h>
#include <string>
#include <vector>
class PlaylistController : public QObject {
Q_OBJECT
Q_PROPERTY(QUrl currentVideoSource READ currentVideoSource NOTIFY
currentVideoSourceChanged)
Q_PROPERTY(QObject *videoOutputA READ videoOutputA WRITE setVideoOutputA
NOTIFY videoOutputChangedA)
Q_PROPERTY(QObject *audioOutputA READ audioOutputA WRITE setAudioOutputA
NOTIFY audioOutputChangedA)
Q_PROPERTY(QObject *videoOutputB READ videoOutputB WRITE setVideoOutputB
NOTIFY videoOutputChanged)
Q_PROPERTY(QObject *audioOutputB READ audioOutputB WRITE setAudioOutputB
NOTIFY audioOutputChangedB)
Q_PROPERTY(QString playingState READ playingState NOTIFY playingStateChanged)
public:
explicit PlaylistController(QObject *parent = nullptr) : QObject(parent) {}
explicit PlaylistController(QObject *parent = nullptr) : QObject(parent) {
m_playerA = new QMediaPlayer(this);
m_playerB = new QMediaPlayer(this);
QUrl currentVideoSource() const { return m_currentVideoSource; }
m_activePlayer = m_playerA;
m_nextPlayer = m_playerB;
Q_INVOKABLE void loadNextVideo() {
if (m_queue.size() == 0) {
Logger::info("Nothing left to play");
m_playing = false;
return;
}
connect(m_playerA, &QMediaPlayer::mediaStatusChanged, this,
[this](QMediaPlayer::MediaStatus status) {
onMediaStatusChanged(m_playerA, status);
});
QString video = m_queue.front();
Logger::info(std::format("Playing: {} in video {}", video.toStdString(), 1));
m_queue.erase(m_queue.begin());
connect(m_playerB, &QMediaPlayer::mediaStatusChanged, this,
[this](QMediaPlayer::MediaStatus status) {
onMediaStatusChanged(m_playerB, status);
});
}
QUrl newUrl = QUrl::fromLocalFile(video);
if (m_currentVideoSource != newUrl) {
m_currentVideoSource = newUrl;
emit currentVideoSourceChanged();
QObject *videoOutputA() const { return m_videoOutputA; }
QObject *videoOutputB() const { return m_videoOutputB; }
QObject *audioOutputA() const { return m_audioOutputA; }
QObject *audioOutputB() const { return m_audioOutputB; }
QString playingState() const { return m_playingState ? "playing" : "pause"; }
void setVideoOutputA(QObject *output) {
if (m_videoOutputA != output) {
m_videoOutputA = output;
if (m_videoOutputA) {
m_playerA->setVideoOutput(m_videoOutputA);
}
emit videoOutputChangedA();
}
}
// Tengo que rehacer esto en algun momento para que tome la url y la procese en otro thread
// Y ya que me pongo con ello usar el doble buffer y permitir cargar listas enteras
Q_INVOKABLE void addToQueue(const QString path) {
int n_elements = m_queue.size();
void setAudioOutputA(QObject *output) {
if (m_audioOutputA != output) {
m_audioOutputA = output;
m_queue.push_back(path);
Logger::info(std::format("Adding: {}", path.toStdString()));
if (m_videoOutputA) {
if (auto *audio = qobject_cast<QAudioOutput *>(m_audioOutputA)) {
m_playerA->setAudioOutput(audio);
}
}
if (n_elements == 0 && !m_playing) {
m_playing = true;
loadNextVideo();
emit audioOutputChangedA();
}
}
void setVideoOutputB(QObject *output) {
if (m_videoOutputB != output) {
m_videoOutputB = output;
if (m_videoOutputB) {
m_playerB->setVideoOutput(m_videoOutputB);
}
emit videoOutputChangedB();
}
}
void setAudioOutputB(QObject *output) {
if (m_audioOutputB != output) {
m_audioOutputB = output;
if (m_videoOutputB) {
if (auto *audio = qobject_cast<QAudioOutput *>(m_audioOutputB)) {
m_playerB->setAudioOutput(audio);
}
}
emit audioOutputChangedB();
}
}
Q_INVOKABLE void prepareNext(const QUrl &url) {
// Desplegar hilo que descargue y gestione el mediaplayer
Logger::info(url.toString().toStdString());
m_nextPlayer->setSource(url);
// Para que se quede listo
m_nextPlayer->play();
m_nextPlayer->pause();
m_nextPlayer->setPosition(0);
}
Q_INVOKABLE void swapAndPlay() {
std::swap(m_activePlayer, m_nextPlayer);
m_activePlayer->play();
m_playingState = true;
// m_activePlayer->setPlaybackRate(1);
if (m_queue.size() - 1 > m_queue_index) {
prepareNext(QUrl::fromLocalFile(m_queue.at(m_queue_index + 1)));
}
}
Q_INVOKABLE void setVolume(float volume) {
if (auto *audio = qobject_cast<QAudioOutput *>(m_audioOutputA)) {
audio->setVolume(std::clamp(m_volume + volume, 0.0f, 1.0f));
}
if (auto *audio = qobject_cast<QAudioOutput *>(m_audioOutputB)) {
audio->setVolume(std::clamp(m_volume + volume, 0.0f, 1.0f));
}
}
Q_INVOKABLE void seek(long long time) {
m_activePlayer->setPosition(std::clamp(m_activePlayer->position() + time,
0ll, m_activePlayer->duration()));
}
Q_INVOKABLE void addToQueue(const QString &url) {
int n_ele = m_queue.size();
m_queue.push_back(url);
if (m_nextPlayer->source() == QUrl()) {
prepareNext(m_queue.at(m_queue_index));
}
}
Q_INVOKABLE void togglePause() {
m_playingState = !m_playingState;
if (!m_playingState)
m_activePlayer->pause();
else
m_activePlayer->play();
emit playingStateChanged();
}
signals:
void currentVideoSourceChanged();
void videoOutputChanged();
void videoOutputChangedA();
void audioOutputChangedA();
void videoOutputChangedB();
void audioOutputChangedB();
void playingStateChanged();
private:
bool m_playing = false;
std::vector<QString> m_queue = std::vector<QString>();
QUrl m_currentVideoSource = QString("");
bool m_playing = false;
QMediaPlayer *m_playerA{nullptr};
QMediaPlayer *m_playerB{nullptr};
QMediaPlayer *m_activePlayer{nullptr};
QMediaPlayer *m_nextPlayer{nullptr};
QObject *m_videoOutputA{nullptr};
QObject *m_videoOutputB{nullptr};
QObject *m_audioOutputA{nullptr};
QObject *m_audioOutputB{nullptr};
float m_volume = 1;
bool m_playingState = false;
int m_queue_index = 0;
void onMediaStatusChanged(QMediaPlayer *player,
QMediaPlayer::MediaStatus status) {
bool isActive = (player == m_activePlayer);
switch (status) {
case QMediaPlayer::LoadedMedia:
Logger::info(isActive ? "Active media loaded"
: "Next media loaded and ready");
break;
case QMediaPlayer::BufferedMedia:
Logger::info("Media fully buffered");
if (!isActive) {
Logger::info("Next media fully buffered");
}
break;
case QMediaPlayer::EndOfMedia:
if (isActive) {
if (m_queue.size() - 1 > m_queue_index) {
m_queue_index++;
swapAndPlay();
}
}
break;
case QMediaPlayer::InvalidMedia:
Logger::error("Failed to load or play media");
break;
default:
break;
}
}
};

View File

@@ -13,8 +13,13 @@ int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
// qmlRegisterType<PlaylistController>("MediaSystem", 1, 0,
// "PlaylistController");
QQmlApplicationEngine engine;
// engine.addImportPath(app.applicationDirPath());
PlaylistController playlist;
engine.rootContext()->setContextProperty("playlist", &playlist);
@@ -28,8 +33,5 @@ int main(int argc, char *argv[]) {
Logger::info("Engine loaded");
playlist.addToQueue("../ui/media/video.mp4");
playlist.addToQueue("../ui/media/Puta.mp4");
return app.exec();
}

View File

@@ -14,7 +14,8 @@ Window {
color: "#1e1e2e"
function togglePause() {
videoPlayer.playbackState == MediaPlayer.PlayingState ? videoPlayer.pause() : videoPlayer.play();
// videoPlayer.playbackState == MediaPlayer.PlayingState ? videoPlayer.pause() : videoPlayer.play();
playlist.togglePause();
seekFeedback.hide();
playFeedback.trigger();
@@ -38,7 +39,8 @@ Window {
}
dir = 1;
videoPlayer.position = Math.min(videoPlayer.duration, videoPlayer.position + time);
// videoPlayer.position = Math.min(videoPlayer.duration, videoPlayer.position + time);
playlist.seek(time);
innerText.text = "+" + (5 * window.times);
} else if (time <= 0) {
if (dir > 0) {
@@ -46,7 +48,8 @@ Window {
}
dir = -1;
videoPlayer.position = Math.max(0, videoPlayer.position + time);
// videoPlayer.position = Math.max(0, videoPlayer.position + time);
playlist.seek(time);
innerText.text = "-" + (5 * window.times);
}
@@ -83,31 +86,34 @@ Window {
horizontalAlignment: Text.AlignHCenter
}
// Estudiar el doble buffer, parece ser no tan coñazo como podria esperar
// mañana lo intento implementar
Video {
id: videoPlayer
AudioOutput {
id: audioOutputA
}
VideoOutput {
id: displayOutputA
anchors.fill: parent
anchors.centerIn: parent
source: playlist.currentVideoSource
visible: true
loops: 0
playbackRate: 1
}
onSourceChanged: {
console.log("Now playing:", source);
if (source.toString() !== "") {
videoPlayer.play();
}
}
AudioOutput {
id: audioOutputB
}
onStopped: {
playlist.loadNextVideo();
}
VideoOutput {
id: displayOutputB
anchors.fill: parent
}
onVisibleChanged: {
console.log("Cambio la visibilidad");
}
Component.onCompleted: {
playlist.videoOutputA = displayOutputA;
playlist.audioOutputA = audioOutputA;
playlist.videoOutputB = displayOutputB;
playlist.audioOutputB = audioOutputB;
playlist.addToQueue("../ui/media/video.mp4");
playlist.addToQueue("../ui/media/Puta.mp4");
playlist.swapAndPlay();
}
FadingFeedback {
@@ -117,7 +123,8 @@ Window {
id: innerIcon
anchors.centerIn: parent
anchors.fill: parent
source: videoPlayer.playbackState === MediaPlayer.PlayingState ? "qrc:/App/ui/icons/play.svg" : "qrc:/App/ui/icons/pause.svg"
// source: videoPlayer.playbackState === MediaPlayer.PlayingState ? "qrc:/App/ui/icons/play.svg" : "qrc:/App/ui/icons/pause.svg"
source: playlist.playingState === "playing" ? "qrc:/App/ui/icons/play.svg" : "qrc:/App/ui/icons/pause.svg"
color: "white"
fillMode: Image.PreserveAspectFit