Skip to content

Commit

Permalink
Add work-in-progress implementation/experiment of stream history.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mysticial committed Dec 4, 2024
1 parent 2beb1d5 commit 177e8cc
Show file tree
Hide file tree
Showing 17 changed files with 796 additions and 24 deletions.
7 changes: 5 additions & 2 deletions SerialPrograms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,11 @@ file(GLOB MAIN_SOURCES
Source/CommonFramework/PersistentSettings.h
Source/CommonFramework/ProgramSession.cpp
Source/CommonFramework/ProgramSession.h
Source/CommonFramework/Recording/RecentHistory.cpp
Source/CommonFramework/Recording/RecentHistory.h
Source/CommonFramework/Recording/StreamHistorySession.cpp
Source/CommonFramework/Recording/StreamHistorySession.h
Source/CommonFramework/Recording/StreamHistoryTracker_Null.h
Source/CommonFramework/Recording/StreamHistoryTracker_RecordOnTheFly.h
Source/CommonFramework/Recording/StreamHistoryTracker_SaveFrames.h
Source/CommonFramework/Resources/SpriteDatabase.cpp
Source/CommonFramework/Resources/SpriteDatabase.h
Source/CommonFramework/SetupSettings.cpp
Expand Down
6 changes: 4 additions & 2 deletions SerialPrograms/SerialPrograms.pro
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ SOURCES += \
Source/CommonFramework/Panels/UI/SettingsPanelWidget.cpp \
Source/CommonFramework/PersistentSettings.cpp \
Source/CommonFramework/ProgramSession.cpp \
Source/CommonFramework/Recording/RecentHistory.cpp \
Source/CommonFramework/Recording/StreamHistorySession.cpp \
Source/CommonFramework/Resources/SpriteDatabase.cpp \
Source/CommonFramework/SetupSettings.cpp \
Source/CommonFramework/Tools/BlackBorderCheck.cpp \
Expand Down Expand Up @@ -1367,7 +1367,9 @@ HEADERS += \
Source/CommonFramework/Panels/UI/SettingsPanelWidget.h \
Source/CommonFramework/PersistentSettings.h \
Source/CommonFramework/ProgramSession.h \
Source/CommonFramework/Recording/RecentHistory.h \
Source/CommonFramework/Recording/StreamHistorySession.h \
Source/CommonFramework/Recording/StreamHistoryTracker_RecordOnTheFly.h \
Source/CommonFramework/Recording/StreamHistoryTracker_SaveFrames.h \
Source/CommonFramework/Resources/SpriteDatabase.h \
Source/CommonFramework/SetupSettings.h \
Source/CommonFramework/Tools/BlackBorderCheck.h \
Expand Down
26 changes: 19 additions & 7 deletions SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ ResolutionOption::ResolutionOption(
PA_ADD_OPTION(HEIGHT);
}

StreamHistoryOption::StreamHistoryOption()
: GroupOption("Stream History", LockMode::LOCK_WHILE_RUNNING, true, false)
, DESCRIPTION(
"Keep a record of this many seconds of video+audio. This will allow video capture for unexpected events.<br>"
"<font color=\"red\">Warning (Developer Only): The current implementation is inefficient and requires "
"10 GB of ram to store just 30 seconds of video. This feature is still a work-in-progress."
"</font>"
)
, VIDEO_HISTORY_SECONDS(
"<b>Video History (seconds):</b><br>"
"Keep this many seconds of video feed for video capture and debugging purposes.",
LockMode::UNLOCK_WHILE_RUNNING,
30
)
{
PA_ADD_STATIC(DESCRIPTION);
PA_ADD_OPTION(VIDEO_HISTORY_SECONDS);
}



Expand Down Expand Up @@ -201,12 +219,6 @@ GlobalSettings::GlobalSettings()
true
)
#endif
, VIDEO_HISTORY_SECONDS(
"<b>Video History (seconds):</b><br>"
"Keep this many seconds of video feed for video capture and debugging purposes.",
LockMode::UNLOCK_WHILE_RUNNING,
30
)
, AUTO_RESET_AUDIO_SECONDS(
"<b>Audio Auto-Reset:</b><br>"
"Attempt to reset the audio if this many seconds has elapsed since the last audio frame (in order to fix issues with RDP disconnection, etc).",
Expand Down Expand Up @@ -275,7 +287,7 @@ GlobalSettings::GlobalSettings()
#endif
#if QT_VERSION_MAJOR >= 6
if (PreloadSettings::instance().DEVELOPER_MODE){ // REMOVE
PA_ADD_OPTION(VIDEO_HISTORY_SECONDS);
PA_ADD_OPTION(STREAM_HISTORY);
}
#endif

Expand Down
12 changes: 9 additions & 3 deletions SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ class ResolutionOption : public GroupOption{
SimpleIntegerOption<uint32_t> WIDTH;
SimpleIntegerOption<uint32_t> HEIGHT;
};


struct DebugSettings{
bool COLOR_CHECK = false;
bool IMAGE_TEMPLATE_MATCHING = false;
bool IMAGE_DICTIONARY_MATCHING = false;
};
class StreamHistoryOption : public GroupOption{
public:
StreamHistoryOption();

StaticTextOption DESCRIPTION;
SimpleIntegerOption<uint16_t> VIDEO_HISTORY_SECONDS;
};



Expand Down Expand Up @@ -117,7 +122,8 @@ class GlobalSettings : public BatchOption, private ConfigOption::Listener{
#if QT_VERSION_MAJOR == 5
BooleanCheckBoxOption ENABLE_FRAME_SCREENSHOTS;
#endif
SimpleIntegerOption<uint16_t> VIDEO_HISTORY_SECONDS;
// SimpleIntegerOption<uint16_t> VIDEO_HISTORY_SECONDS;
StreamHistoryOption STREAM_HISTORY;

SimpleIntegerOption<uint8_t> AUTO_RESET_AUDIO_SECONDS;
SimpleIntegerOption<uint8_t> AUTO_RESET_VIDEO_SECONDS;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/* Stream History Session
*
* From: https://github.com/PokemonAutomation/Arduino-Source
*
*/

#include "Common/Cpp/Exceptions.h"
#include "Common/Cpp/Containers/Pimpl.tpp"
#include "Common/Cpp/Concurrency/SpinLock.h"
#include "CommonFramework/GlobalSettingsPanel.h"
#include "CommonFramework/VideoPipeline/Backends/VideoFrameQt.h"

#if (QT_VERSION_MAJOR == 6) && (QT_VERSION_MINOR >= 8)
#include "StreamHistoryTracker_SaveFrames.h"
//#include "StreamHistoryTracker_RecordOnTheFly.h"
#else
#include "StreamHistoryTracker_Null.h"
#endif


#include "StreamHistorySession.h"



namespace PokemonAutomation{





struct StreamHistorySession::Data{
Logger& m_logger;
mutable SpinLock m_lock;
std::chrono::seconds m_window;
AudioChannelFormat m_audio_format;
std::shared_ptr<StreamHistoryTracker> m_current;

Data(Logger& logger)
: m_logger(logger)
, m_window(GlobalSettings::instance().STREAM_HISTORY.VIDEO_HISTORY_SECONDS)
, m_audio_format(AudioChannelFormat::NONE)
{}
};




StreamHistorySession::StreamHistorySession(Logger& logger)
: AudioFloatStreamListener(1)
, m_data(CONSTRUCT_TOKEN, logger)
{}
void StreamHistorySession::start(AudioChannelFormat format){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
if (!data.m_current){
data.m_audio_format = format;
initialize();
}
}


class HistorySaverThread : public QThread{
public:
HistorySaverThread(Logger& logger, const StreamHistoryTracker& tracker, const std::string& filename)
: m_logger(logger)
, m_tracker(tracker)
, m_filename(filename)
{
start();
}
~HistorySaverThread(){
quit();
wait();
}
virtual void run() override{
m_tracker.save(m_logger, m_filename);
}

private:
Logger& m_logger;
const StreamHistoryTracker& m_tracker;
const std::string& m_filename;
};

void StreamHistorySession::save(const std::string& filename) const{
const Data& data = *m_data;

// Get an owning reference to the current tracker.
// This will allow us to promptly release the lock and unblock the UI from
// changing the streams (which will wipe the history).
std::shared_ptr<StreamHistoryTracker> tracker;
{
WriteSpinLock lg(data.m_lock);
if (!data.m_current){
data.m_logger.log("Cannot save stream history: Stream history is not enabled.", COLOR_RED);
return;
}
tracker = data.m_current;
}

// tracker->save(m_logger, filename);
HistorySaverThread saver(data.m_logger, *tracker, filename);
}
void StreamHistorySession::on_samples(const float* samples, size_t frames){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
if (data.m_current){
data.m_current->on_samples(samples, frames);
}
}
void StreamHistorySession::on_frame(std::shared_ptr<VideoFrame> frame){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
if (data.m_current){
data.m_current->on_frame(std::move(frame));
}
}


void StreamHistorySession::clear(){
// Must call under lock.
Data& data = *m_data;
data.m_logger.log("Clearing stream history...", COLOR_ORANGE);
data.m_current.reset();
expected_samples_per_frame = 0;
data.m_audio_format = AudioChannelFormat::NONE;
}
void StreamHistorySession::initialize(){
if (!GlobalSettings::instance().STREAM_HISTORY.enabled()){
return;
}

// Must call under lock.
Data& data = *m_data;
data.m_logger.log("Starting stream history...", COLOR_ORANGE);
switch (data.m_audio_format){
case AudioChannelFormat::NONE:
expected_samples_per_frame = 0;
data.m_current.reset(new StreamHistoryTracker(0, 0, data.m_window));
return;
case AudioChannelFormat::MONO_48000:
data.m_current.reset(new StreamHistoryTracker(1, 48000, data.m_window));
return;
case AudioChannelFormat::DUAL_44100:
data.m_current.reset(new StreamHistoryTracker(1, 44100, data.m_window));
return;
case AudioChannelFormat::DUAL_48000:
case AudioChannelFormat::MONO_96000:
case AudioChannelFormat::INTERLEAVE_LR_96000:
case AudioChannelFormat::INTERLEAVE_RL_96000:
data.m_current.reset(new StreamHistoryTracker(2, 48000, data.m_window));
return;
default:
throw InternalProgramError(
nullptr, PA_CURRENT_FUNCTION,
"Invalid AudioFormat: " + std::to_string((size_t)data.m_audio_format)
);
}
}
void StreamHistorySession::pre_input_change(){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
clear();
}
void StreamHistorySession::post_input_change(const std::string& file, const AudioDeviceInfo& device, AudioChannelFormat format){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
data.m_audio_format = format;
initialize();
}
void StreamHistorySession::pre_shutdown(){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
clear();
}
void StreamHistorySession::post_new_source(const CameraInfo& device, Resolution resolution){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
initialize();
}
void StreamHistorySession::pre_resolution_change(Resolution resolution){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
clear();
}
void StreamHistorySession::post_resolution_change(Resolution resolution){
Data& data = *m_data;
WriteSpinLock lg(data.m_lock);
initialize();
}







}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* Stream History Session
*
* From: https://github.com/PokemonAutomation/Arduino-Source
*
* Capture the last X seconds of audio and video.
*
*/

#ifndef PokemonAutomation_StreamHistorySession_H
#define PokemonAutomation_StreamHistorySession_H

#include "Common/Cpp/AbstractLogger.h"
#include "Common/Cpp/Containers/Pimpl.h"
#include "CommonFramework/AudioPipeline/AudioSession.h"
#include "CommonFramework/VideoPipeline/CameraSession.h"

namespace PokemonAutomation{


class StreamHistorySession
: public AudioFloatStreamListener
, public VideoFrameListener
, public AudioSession::StateListener
, public CameraSession::StateListener
{
public:
StreamHistorySession(Logger& logger);
void start(AudioChannelFormat format);
void save(const std::string& filename) const;

public:
virtual void on_samples(const float* data, size_t frames) override;
virtual void on_frame(std::shared_ptr<VideoFrame> frame) override;

public:
virtual void pre_input_change() override;
virtual void post_input_change(const std::string& file, const AudioDeviceInfo& device, AudioChannelFormat format) override;

virtual void pre_shutdown() override;
virtual void post_new_source(const CameraInfo& device, Resolution resolution) override;
virtual void pre_resolution_change(Resolution resolution) override;
virtual void post_resolution_change(Resolution resolution) override;

private:
void clear();
void initialize();

private:
struct Data;
Pimpl<Data> m_data;
};



}
#endif
Loading

0 comments on commit 177e8cc

Please sign in to comment.