mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-19 19:15:26 +00:00
Qt: Add qt video source class for more generic video playback
This commit is contained in:
parent
fccb761ef2
commit
b9e0a36816
14 changed files with 372 additions and 204 deletions
|
@ -760,6 +760,7 @@
|
|||
<ClInclude Include="..\Utilities\Timer.h" />
|
||||
<ClInclude Include="util\types.hpp" />
|
||||
<ClInclude Include="..\Utilities\version.h" />
|
||||
<ClInclude Include="util\video_source.h" />
|
||||
<ClInclude Include="util\vm.hpp" />
|
||||
<ClInclude Include="util\asm.hpp" />
|
||||
<ClInclude Include="Crypto\aes.h" />
|
||||
|
|
|
@ -2716,6 +2716,9 @@
|
|||
<ClInclude Include="Emu\RSX\Program\FragmentProgramRegister.h">
|
||||
<Filter>Emu\GPU\RSX\Program</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="util\video_source.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||
|
|
|
@ -807,6 +807,7 @@
|
|||
<ClCompile Include="rpcs3qt\progress_indicator.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_camera_handler.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_music_handler.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_video_source.cpp" />
|
||||
<ClCompile Include="rpcs3qt\raw_mouse_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\render_creator.cpp" />
|
||||
|
@ -1514,6 +1515,7 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\qt_video_source.h" />
|
||||
<ClInclude Include="rpcs3qt\richtext_item_delegate.h" />
|
||||
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
|
|
|
@ -196,6 +196,9 @@
|
|||
<Filter Include="Gui\savestates">
|
||||
<UniqueIdentifier>{9b51636c-b371-425b-86d3-be003774a1b7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Io\video">
|
||||
<UniqueIdentifier>{2bb5cec5-5acb-40c0-a388-68db05dff305}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
|
@ -1176,6 +1179,9 @@
|
|||
<ClCompile Include="rpcs3qt\gui_game_info.cpp">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\qt_video_source.cpp">
|
||||
<Filter>Io\video</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
|
@ -1385,6 +1391,9 @@
|
|||
<ClInclude Include="rpcs3qt\gui_game_info.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\qt_video_source.h">
|
||||
<Filter>Io\video</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
|
|
@ -73,6 +73,7 @@ add_library(rpcs3_ui STATIC
|
|||
qt_camera_video_sink.cpp
|
||||
qt_music_handler.cpp
|
||||
qt_utils.cpp
|
||||
qt_video_source.cpp
|
||||
raw_mouse_settings_dialog.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
|
|
|
@ -33,7 +33,7 @@ void game_list_base::repaint_icons(std::vector<game_info>& game_data, const QCol
|
|||
IconLoadFunction(game, device_pixel_ratio, cancel);
|
||||
});
|
||||
|
||||
item->call_icon_func();
|
||||
item->image_change_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ game_list_grid::game_list_grid()
|
|||
|
||||
connect(this, &game_list_grid::IconReady, this, [this](const movie_item_base* item)
|
||||
{
|
||||
if (item) item->call_icon_func();
|
||||
if (item) item->image_change_callback();
|
||||
}, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case...
|
||||
|
||||
connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index)
|
||||
|
@ -81,7 +81,7 @@ void game_list_grid::populate(
|
|||
item->setToolTip(tr("%0 [%1]").arg(title).arg(serial));
|
||||
}
|
||||
|
||||
item->set_icon_func([this, item, game](const QVideoFrame& frame)
|
||||
item->set_image_change_callback([this, item, game](const QVideoFrame& frame)
|
||||
{
|
||||
if (!item || !game)
|
||||
{
|
||||
|
@ -109,7 +109,7 @@ void game_list_grid::populate(
|
|||
|
||||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
item->set_movie_path(QString::fromStdString(game->info.movie_path));
|
||||
item->set_video_path(game->info.movie_path);
|
||||
}
|
||||
|
||||
if (selected_item_id == game->info.path + game->info.icon_path)
|
||||
|
@ -154,7 +154,7 @@ void game_list_grid::repaint_icons(std::vector<game_info>& game_data, const QCol
|
|||
{
|
||||
// We don't have an icon. Set a placeholder to initialize the layout.
|
||||
game->pxmap = placeholder;
|
||||
item->call_icon_func();
|
||||
item->image_change_callback();
|
||||
}
|
||||
|
||||
item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int)
|
||||
|
|
|
@ -54,7 +54,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr<persist
|
|||
|
||||
connect(this, &game_list::IconReady, this, [this](const movie_item_base* item)
|
||||
{
|
||||
if (item) item->call_icon_func();
|
||||
if (item) item->image_change_callback();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ void game_list_table::populate(
|
|||
custom_table_widget_item* icon_item = new custom_table_widget_item;
|
||||
game->item = icon_item;
|
||||
|
||||
icon_item->set_icon_func([this, icon_item, game](const QVideoFrame& frame)
|
||||
icon_item->set_image_change_callback([this, icon_item, game](const QVideoFrame& frame)
|
||||
{
|
||||
if (!icon_item || !game)
|
||||
{
|
||||
|
@ -292,7 +292,7 @@ void game_list_table::populate(
|
|||
|
||||
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
|
||||
{
|
||||
icon_item->set_movie_path(QString::fromStdString(game->info.movie_path));
|
||||
icon_item->set_video_path(game->info.movie_path);
|
||||
}
|
||||
|
||||
icon_item->setData(Qt::UserRole, index, true);
|
||||
|
|
|
@ -1,24 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "movie_item_base.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
movie_item_base::movie_item_base()
|
||||
movie_item_base::movie_item_base() : qt_video_source()
|
||||
{
|
||||
init_pointers();
|
||||
}
|
||||
|
||||
movie_item_base::~movie_item_base()
|
||||
{
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->stop();
|
||||
}
|
||||
stop_movie();
|
||||
|
||||
wait_for_icon_loading(true);
|
||||
wait_for_size_on_disk_loading(true);
|
||||
|
@ -30,146 +20,6 @@ void movie_item_base::init_pointers()
|
|||
m_size_on_disk_loading_aborted.reset(new atomic_t<bool>(false));
|
||||
}
|
||||
|
||||
void movie_item_base::set_active(bool active)
|
||||
{
|
||||
if (!std::exchange(m_active, active) && active)
|
||||
{
|
||||
init_movie();
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->jumpToFrame(1);
|
||||
m_movie->start();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::init_movie()
|
||||
{
|
||||
if (m_movie || m_media_player)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_icon_callback || m_movie_path.isEmpty() || !QFile::exists(m_movie_path))
|
||||
{
|
||||
m_movie_path.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString lower = m_movie_path.toLower();
|
||||
|
||||
if (lower.endsWith(".gif"))
|
||||
{
|
||||
m_movie.reset(new QMovie(m_movie_path));
|
||||
m_movie_path.clear();
|
||||
|
||||
if (!m_movie->isValid())
|
||||
{
|
||||
m_movie.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
|
||||
{
|
||||
m_icon_callback({});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (lower.endsWith(".pam"))
|
||||
{
|
||||
// We can't set PAM files as source of the video player, so we have to feed them as raw data.
|
||||
QFile file(m_movie_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_movie_data = file.readAll();
|
||||
if (m_movie_data.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_movie_buffer.reset(new QBuffer(&m_movie_data));
|
||||
m_movie_buffer->open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
m_video_sink.reset(new QVideoSink());
|
||||
QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
|
||||
{
|
||||
m_icon_callback(frame);
|
||||
});
|
||||
|
||||
m_media_player.reset(new QMediaPlayer());
|
||||
m_media_player->setVideoSink(m_video_sink.get());
|
||||
m_media_player->setLoops(QMediaPlayer::Infinite);
|
||||
|
||||
if (m_movie_buffer)
|
||||
{
|
||||
m_media_player->setSourceDevice(m_movie_buffer.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_media_player->setSource(m_movie_path);
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::stop_movie()
|
||||
{
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
m_video_sink.reset();
|
||||
m_media_player.reset();
|
||||
m_movie_buffer.reset();
|
||||
m_movie_data.clear();
|
||||
}
|
||||
|
||||
QPixmap movie_item_base::get_movie_image(const QVideoFrame& frame) const
|
||||
{
|
||||
if (!m_active)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
return m_movie->currentPixmap();
|
||||
}
|
||||
|
||||
if (!frame.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
return QPixmap::fromImage(frame.toImage());
|
||||
}
|
||||
|
||||
void movie_item_base::call_icon_func() const
|
||||
{
|
||||
if (m_icon_callback)
|
||||
{
|
||||
m_icon_callback({});
|
||||
}
|
||||
}
|
||||
|
||||
void movie_item_base::set_icon_func(const icon_callback_t& func)
|
||||
{
|
||||
m_icon_callback = func;
|
||||
}
|
||||
|
||||
void movie_item_base::call_icon_load_func(int index)
|
||||
{
|
||||
if (!m_icon_load_callback || m_icon_loading || m_icon_loading_aborted->load())
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "movie_item_base.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
#include "qt_video_source.h"
|
||||
|
||||
#include <QMovie>
|
||||
#include <QThread>
|
||||
#include <QBuffer>
|
||||
#include <QMediaPlayer>
|
||||
#include <QVideoSink>
|
||||
#include <QVideoFrame>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
using icon_callback_t = std::function<void(const QVideoFrame&)>;
|
||||
using icon_load_callback_t = std::function<void(int)>;
|
||||
using size_calc_callback_t = std::function<void()>;
|
||||
|
||||
class movie_item_base
|
||||
class movie_item_base : public qt_video_source
|
||||
{
|
||||
public:
|
||||
movie_item_base();
|
||||
|
@ -27,25 +18,6 @@ public:
|
|||
|
||||
void init_pointers();
|
||||
|
||||
void set_active(bool active);
|
||||
|
||||
[[nodiscard]] bool get_active() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
void set_movie_path(QString path)
|
||||
{
|
||||
m_movie_path = std::move(path);
|
||||
}
|
||||
|
||||
void init_movie();
|
||||
void stop_movie();
|
||||
QPixmap get_movie_image(const QVideoFrame& frame) const;
|
||||
|
||||
void call_icon_func() const;
|
||||
void set_icon_func(const icon_callback_t& func);
|
||||
|
||||
void call_icon_load_func(int index);
|
||||
void set_icon_load_func(const icon_load_callback_t& func);
|
||||
|
||||
|
@ -77,23 +49,13 @@ public:
|
|||
|
||||
shared_mutex pixmap_mutex;
|
||||
|
||||
protected:
|
||||
QString m_movie_path;
|
||||
QByteArray m_movie_data{};
|
||||
std::unique_ptr<QBuffer> m_movie_buffer;
|
||||
std::unique_ptr<QMediaPlayer> m_media_player;
|
||||
std::shared_ptr<QVideoSink> m_video_sink;
|
||||
std::shared_ptr<QMovie> m_movie;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QThread> m_icon_load_thread;
|
||||
std::unique_ptr<QThread> m_size_calc_thread;
|
||||
bool m_active = false;
|
||||
atomic_t<bool> m_size_on_disk_loading = false;
|
||||
atomic_t<bool> m_icon_loading = false;
|
||||
size_calc_callback_t m_size_calc_callback = nullptr;
|
||||
icon_load_callback_t m_icon_load_callback = nullptr;
|
||||
icon_callback_t m_icon_callback = nullptr;
|
||||
|
||||
std::shared_ptr<atomic_t<bool>> m_icon_loading_aborted;
|
||||
std::shared_ptr<atomic_t<bool>> m_size_on_disk_loading_aborted;
|
||||
|
|
244
rpcs3/rpcs3qt/qt_video_source.cpp
Normal file
244
rpcs3/rpcs3qt/qt_video_source.cpp
Normal file
|
@ -0,0 +1,244 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "qt_video_source.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
qt_video_source::qt_video_source()
|
||||
: video_source()
|
||||
{
|
||||
}
|
||||
|
||||
qt_video_source::~qt_video_source()
|
||||
{
|
||||
stop_movie();
|
||||
}
|
||||
|
||||
void qt_video_source::set_video_path(const std::string& path)
|
||||
{
|
||||
m_video_path = QString::fromStdString(path);
|
||||
}
|
||||
|
||||
void qt_video_source::set_active(bool active)
|
||||
{
|
||||
if (!m_active.exchange(active) && active)
|
||||
{
|
||||
start_movie();
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::image_change_callback() const
|
||||
{
|
||||
if (m_image_change_callback)
|
||||
{
|
||||
m_image_change_callback({});
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::set_image_change_callback(const std::function<void(const QVideoFrame&)>& func)
|
||||
{
|
||||
m_image_change_callback = func;
|
||||
}
|
||||
|
||||
void qt_video_source::init_movie()
|
||||
{
|
||||
if (m_movie || m_media_player)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_image_change_callback || m_video_path.isEmpty() || !QFile::exists(m_video_path))
|
||||
{
|
||||
m_video_path.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString lower = m_video_path.toLower();
|
||||
|
||||
if (lower.endsWith(".gif"))
|
||||
{
|
||||
m_movie.reset(new QMovie(m_video_path));
|
||||
m_video_path.clear();
|
||||
|
||||
if (!m_movie->isValid())
|
||||
{
|
||||
m_movie.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
|
||||
{
|
||||
m_image_change_callback({});
|
||||
m_has_new = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (lower.endsWith(".pam"))
|
||||
{
|
||||
// We can't set PAM files as source of the video player, so we have to feed them as raw data.
|
||||
QFile file(m_video_path);
|
||||
if (!file.open(QFile::OpenModeFlag::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Decode the pam properly before pushing it to the player
|
||||
m_video_data = file.readAll();
|
||||
if (m_video_data.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_video_buffer.reset(new QBuffer(&m_video_data));
|
||||
m_video_buffer->open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
m_video_sink.reset(new QVideoSink());
|
||||
QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
|
||||
{
|
||||
m_image_change_callback(frame);
|
||||
m_has_new = true;
|
||||
});
|
||||
|
||||
m_media_player.reset(new QMediaPlayer());
|
||||
m_media_player->setVideoSink(m_video_sink.get());
|
||||
m_media_player->setLoops(QMediaPlayer::Infinite);
|
||||
|
||||
if (m_video_buffer)
|
||||
{
|
||||
m_media_player->setSourceDevice(m_video_buffer.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_media_player->setSource(m_video_path);
|
||||
}
|
||||
}
|
||||
|
||||
void qt_video_source::start_movie()
|
||||
{
|
||||
init_movie();
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->jumpToFrame(1);
|
||||
m_movie->start();
|
||||
}
|
||||
|
||||
if (m_media_player)
|
||||
{
|
||||
m_media_player->play();
|
||||
}
|
||||
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void qt_video_source::stop_movie()
|
||||
{
|
||||
m_active = false;
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
m_movie->stop();
|
||||
}
|
||||
|
||||
m_video_sink.reset();
|
||||
m_media_player.reset();
|
||||
m_video_buffer.reset();
|
||||
m_video_data.clear();
|
||||
}
|
||||
|
||||
QPixmap qt_video_source::get_movie_image(const QVideoFrame& frame) const
|
||||
{
|
||||
if (!m_active)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_movie)
|
||||
{
|
||||
return m_movie->currentPixmap();
|
||||
}
|
||||
|
||||
if (!frame.isValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
return QPixmap::fromImage(frame.toImage());
|
||||
}
|
||||
|
||||
void qt_video_source::get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp)
|
||||
{
|
||||
if (!m_has_new.exchange(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_image_mutex);
|
||||
|
||||
if (m_image.isNull())
|
||||
{
|
||||
w = h = ch = bpp = 0;
|
||||
data.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
w = m_image.width();
|
||||
h = m_image.height();
|
||||
ch = m_image.colorCount();
|
||||
bpp = m_image.depth();
|
||||
|
||||
data.resize(m_image.height() * m_image.bytesPerLine());
|
||||
std::memcpy(data.data(), m_image.constBits(), data.size());
|
||||
}
|
||||
|
||||
qt_video_source_wrapper::~qt_video_source_wrapper()
|
||||
{
|
||||
Emu.BlockingCallFromMainThread([this]()
|
||||
{
|
||||
m_qt_video_source.reset();
|
||||
});
|
||||
}
|
||||
|
||||
void qt_video_source_wrapper::set_video_path(const std::string& path)
|
||||
{
|
||||
Emu.BlockingCallFromMainThread([this, &path]()
|
||||
{
|
||||
m_qt_video_source = std::make_unique<qt_video_source>();
|
||||
m_qt_video_source->m_image_change_callback = [this](const QVideoFrame& frame)
|
||||
{
|
||||
std::lock_guard lock(m_qt_video_source->m_image_mutex);
|
||||
|
||||
if (m_qt_video_source->m_movie)
|
||||
{
|
||||
m_qt_video_source->m_image = m_qt_video_source->m_movie->currentImage();
|
||||
}
|
||||
else if (frame.isValid())
|
||||
{
|
||||
// Get image. This usually also converts the image to ARGB32.
|
||||
m_qt_video_source->m_image = frame.toImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_qt_video_source->m_image.format() != QImage::Format_RGBA8888)
|
||||
{
|
||||
m_qt_video_source->m_image.convertTo(QImage::Format_RGBA8888);
|
||||
}
|
||||
};
|
||||
m_qt_video_source->set_video_path(path);
|
||||
m_qt_video_source->set_active(true);
|
||||
});
|
||||
}
|
||||
|
||||
void qt_video_source_wrapper::get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp)
|
||||
{
|
||||
ensure(m_qt_video_source);
|
||||
|
||||
m_qt_video_source->get_image(data, w, h, ch, bpp);
|
||||
}
|
76
rpcs3/rpcs3qt/qt_video_source.h
Normal file
76
rpcs3/rpcs3qt/qt_video_source.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/video_source.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
#include <QMovie>
|
||||
#include <QBuffer>
|
||||
#include <QMediaPlayer>
|
||||
#include <QVideoSink>
|
||||
#include <QVideoFrame>
|
||||
#include <QPixmap>
|
||||
|
||||
class qt_video_source : public video_source
|
||||
{
|
||||
public:
|
||||
qt_video_source();
|
||||
virtual ~qt_video_source();
|
||||
|
||||
void set_video_path(const std::string& path) override;
|
||||
const QString& video_path() const { return m_video_path; }
|
||||
|
||||
void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp) override;
|
||||
bool has_new() const override { return m_has_new; }
|
||||
|
||||
void set_active(bool active);
|
||||
[[nodiscard]] bool get_active() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
void start_movie();
|
||||
void stop_movie();
|
||||
|
||||
QPixmap get_movie_image(const QVideoFrame& frame) const;
|
||||
|
||||
void image_change_callback() const;
|
||||
void set_image_change_callback(const std::function<void(const QVideoFrame&)>& func);
|
||||
|
||||
protected:
|
||||
void init_movie();
|
||||
|
||||
shared_mutex m_image_mutex;
|
||||
|
||||
atomic_t<bool> m_active = false;
|
||||
atomic_t<bool> m_has_new = false;
|
||||
|
||||
QString m_video_path;
|
||||
QByteArray m_video_data{};
|
||||
QImage m_image{};
|
||||
std::vector<u8> m_image_path;
|
||||
|
||||
std::unique_ptr<QBuffer> m_video_buffer;
|
||||
std::unique_ptr<QMediaPlayer> m_media_player;
|
||||
std::shared_ptr<QVideoSink> m_video_sink;
|
||||
std::shared_ptr<QMovie> m_movie;
|
||||
|
||||
std::function<void(const QVideoFrame&)> m_image_change_callback = nullptr;
|
||||
|
||||
friend class qt_video_source_wrapper;
|
||||
};
|
||||
|
||||
// Wrapper for emulator usage
|
||||
class qt_video_source_wrapper : public video_source
|
||||
{
|
||||
public:
|
||||
qt_video_source_wrapper() : video_source() {}
|
||||
virtual ~qt_video_source_wrapper();
|
||||
|
||||
void set_video_path(const std::string& path) override;
|
||||
void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp) override;
|
||||
bool has_new() const override { return m_qt_video_source && m_qt_video_source->has_new(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<qt_video_source> m_qt_video_source;
|
||||
};
|
|
@ -208,7 +208,7 @@ void save_manager_dialog::Init()
|
|||
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon)))
|
||||
{
|
||||
item->setData(SaveUserRole::PixmapScaled, pixmap);
|
||||
item->call_icon_func();
|
||||
item->image_change_callback();
|
||||
}
|
||||
});
|
||||
connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed);
|
||||
|
@ -344,14 +344,14 @@ void save_manager_dialog::UpdateList()
|
|||
|
||||
if (const std::string movie_path = dir_path + localized_movie; fs::is_file(movie_path))
|
||||
{
|
||||
icon_item->set_movie_path(QString::fromStdString(movie_path));
|
||||
icon_item->set_video_path(movie_path);
|
||||
}
|
||||
else if (const std::string movie_path = dir_path + "ICON1.PAM"; fs::is_file(movie_path))
|
||||
{
|
||||
icon_item->set_movie_path(QString::fromStdString(movie_path));
|
||||
icon_item->set_video_path(movie_path);
|
||||
}
|
||||
|
||||
icon_item->set_icon_func([this, icon_item](const QVideoFrame& frame)
|
||||
icon_item->set_image_change_callback([this, icon_item](const QVideoFrame& frame)
|
||||
{
|
||||
if (!icon_item)
|
||||
{
|
||||
|
|
20
rpcs3/util/video_source.h
Normal file
20
rpcs3/util/video_source.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
class video_source
|
||||
{
|
||||
public:
|
||||
video_source() {};
|
||||
virtual ~video_source() {};
|
||||
virtual void set_video_path(const std::string& path) { static_cast<void>(path); }
|
||||
virtual bool has_new() const { return false; };
|
||||
virtual void get_image(std::vector<u8>& data, int& w, int& h, int& ch, int& bpp)
|
||||
{
|
||||
static_cast<void>(data);
|
||||
static_cast<void>(w);
|
||||
static_cast<void>(h);
|
||||
static_cast<void>(ch);
|
||||
static_cast<void>(bpp);
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue