mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Fixed Recorder and Session to allow transfer of recorders when changing
session.
This commit is contained in:
@@ -672,6 +672,9 @@ void Mixer::swap()
|
||||
session_ = back_session_;
|
||||
back_session_ = tmp;
|
||||
|
||||
// swap recorders
|
||||
back_session_->transferRecorders(session_);
|
||||
|
||||
// attach new session's nodes to views
|
||||
for (auto source_iter = session_->begin(); source_iter != session_->end(); source_iter++)
|
||||
{
|
||||
|
||||
82
Recorder.cpp
82
Recorder.cpp
@@ -69,7 +69,7 @@ void PNGRecorder::addFrame(FrameBuffer *frame_buffer, float)
|
||||
finished_ = true;
|
||||
}
|
||||
|
||||
const char* VideoRecorder::profile_name[4] = { "H264 (high)", "H264 (low)", "Apple ProRes 4444", "WebM VP9" };
|
||||
const char* VideoRecorder::profile_name[4] = { "H264 (low)", "H264 (high)", "Apple ProRes 4444", "WebM VP9" };
|
||||
const std::vector<std::string> VideoRecorder::profile_description {
|
||||
// Control x264 encoder quality :
|
||||
// pass=4
|
||||
@@ -82,8 +82,8 @@ const std::vector<std::string> VideoRecorder::profile_description {
|
||||
// veryfast (3)
|
||||
// faster (4)
|
||||
// fast (5)
|
||||
"x264enc pass=4 quantizer=20 speed-preset=3 ! video/x-h264, profile=high ! h264parse ! ",
|
||||
"x264enc pass=4 quantizer=23 speed-preset=3 ! video/x-h264, profile=baseline ! h264parse ! ",
|
||||
"x264enc pass=5 quantizer=18 speed-preset=4 ! video/x-h264, profile=high ! h264parse ! ",
|
||||
// Apple ProRes encoding parameters
|
||||
// pass=2
|
||||
// cbr (0) – Constant Bitrate Encoding
|
||||
@@ -97,9 +97,15 @@ const std::vector<std::string> VideoRecorder::profile_description {
|
||||
|
||||
};
|
||||
|
||||
// FAILED
|
||||
// x265 encoder quality
|
||||
// string description = "appsrc name=src ! videoconvert ! "
|
||||
// "x265enc tune=4 speed-preset=2 option-string='crf=28' ! h265parse ! "
|
||||
// "qtmux ! filesink name=sink";
|
||||
|
||||
|
||||
VideoRecorder::VideoRecorder() : Recorder(), frame_buffer_(nullptr), width_(0), height_(0), buf_size_(0),
|
||||
recording_(false), pipeline_(nullptr), src_(nullptr), timestamp_(0), time_(0), accept_buffer_(false)
|
||||
recording_(false), pipeline_(nullptr), src_(nullptr), timestamp_(0), timeframe_(0), accept_buffer_(false)
|
||||
{
|
||||
// auto filename
|
||||
std::string path = SystemToolkit::path_directory(Settings::application.record.path);
|
||||
@@ -110,7 +116,7 @@ VideoRecorder::VideoRecorder() : Recorder(), frame_buffer_(nullptr), width_(0),
|
||||
|
||||
// configure fix parameter
|
||||
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // 30 FPS
|
||||
time_ = 2 * frame_duration_;
|
||||
timeframe_ = 2 * frame_duration_;
|
||||
}
|
||||
|
||||
VideoRecorder::~VideoRecorder()
|
||||
@@ -134,7 +140,7 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
// first frame for initialization
|
||||
if (frame_buffer_ == nullptr) {
|
||||
|
||||
// accepting a new frame buffer as input
|
||||
// set frame buffer as input
|
||||
frame_buffer_ = frame_buffer;
|
||||
|
||||
// define stream properties
|
||||
@@ -147,45 +153,6 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
description += profile_description[Settings::application.record.profile];
|
||||
description += "qtmux ! filesink name=sink";
|
||||
|
||||
// Control x264 encoder quality :
|
||||
// pass=4
|
||||
// quant (4) – Constant Quantizer
|
||||
// qual (5) – Constant Quality
|
||||
// quantizer=23
|
||||
// The total range is from 0 to 51, where 0 is lossless, 18 can be considered ‘visually lossless’,
|
||||
// and 51 is terrible quality. A sane range is 18-26, and the default is 23.
|
||||
// speed-preset=3
|
||||
// veryfast (3)
|
||||
// faster (4)
|
||||
// fast (5)
|
||||
// string description = "appsrc name=src ! videoconvert ! "
|
||||
// "x264enc pass=4 quantizer=23 speed-preset=3 ! video/x-h264, profile=high ! h264parse ! "
|
||||
// "qtmux ! filesink name=sink";
|
||||
|
||||
// WebM VP9 encoding parameters
|
||||
// https://www.webmproject.org/docs/encoder-parameters/
|
||||
// https://developers.google.com/media/vp9/settings/vod/
|
||||
// string description = "appsrc name=src ! videoconvert ! "
|
||||
// "vp9enc end-usage=vbr end-usage=vbr cpu-used=3 max-quantizer=35 target-bitrate=200000 keyframe-max-dist=360 token-partitions=2 static-threshold=1000 ! "
|
||||
// "webmmux ! filesink name=sink";
|
||||
|
||||
// string description = "appsrc name=src ! videoconvert ! avenc_prores ! qtmux ! filesink name=sink";
|
||||
|
||||
// x265 encoder quality
|
||||
// string description = "appsrc name=src ! videoconvert ! "
|
||||
// "x265enc tune=4 speed-preset=2 option-string='crf=28' ! h265parse ! "
|
||||
// "qtmux ! filesink name=sink";
|
||||
|
||||
|
||||
// Apple ProRes encoding parameters
|
||||
// pass=2
|
||||
// cbr (0) – Constant Bitrate Encoding
|
||||
// quant (2) – Constant Quantizer
|
||||
// pass1 (512) – VBR Encoding - Pass 1
|
||||
// string description = "appsrc name=src ! videoconvert ! "
|
||||
// "avenc_prores bitrate=60000 pass=2 ! "
|
||||
// "qtmux ! filesink name=sink";
|
||||
|
||||
// parse pipeline descriptor
|
||||
GError *error = NULL;
|
||||
pipeline_ = gst_parse_launch (description.c_str(), &error);
|
||||
@@ -221,8 +188,8 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
// "do-timestamp", TRUE,
|
||||
NULL);
|
||||
|
||||
// 2 sec buffer
|
||||
gst_app_src_set_max_bytes( src_, 0 );
|
||||
// Direct encoding (no buffering)
|
||||
gst_app_src_set_max_bytes( src_, 0 );
|
||||
// gst_app_src_set_max_bytes( src_, 2 * buf_size_);
|
||||
|
||||
// instruct src to use the required caps
|
||||
@@ -271,9 +238,12 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
frame_buffer->height() != height_ ||
|
||||
frame_buffer->use_alpha() != frame_buffer_->use_alpha()) {
|
||||
|
||||
// end stream and stop
|
||||
gst_app_src_end_of_stream (src_);
|
||||
recording_ = false;
|
||||
stop();
|
||||
Log::Info("Recording interrupted: new session (%d x %d) incompatible with recording (%d x %d)", frame_buffer->width(), frame_buffer->height(), width_, height_);
|
||||
}
|
||||
else {
|
||||
// accepting a new frame buffer as input
|
||||
frame_buffer_ = frame_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,11 +253,11 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
if (recording_ && buf_size_ > 0)
|
||||
{
|
||||
// calculate dt in ns
|
||||
time_ += gst_gdouble_to_guint64( dt * 1000000.f);
|
||||
timeframe_ += gst_gdouble_to_guint64( dt * 1000000.f);
|
||||
|
||||
// if time is passed one frame duration (with 10% margin)
|
||||
// and if the encoder accepts data
|
||||
if ( time_ > frame_duration_ - 3000000 && accept_buffer_) {
|
||||
if ( timeframe_ > frame_duration_ - 3000000 && accept_buffer_) {
|
||||
|
||||
GstBuffer *buffer = gst_buffer_new_and_alloc (buf_size_);
|
||||
GLenum format = frame_buffer_->use_alpha() ? GL_RGBA : GL_RGB;
|
||||
@@ -307,18 +277,10 @@ void VideoRecorder::addFrame (FrameBuffer *frame_buffer, float dt)
|
||||
gst_app_src_push_buffer (src_, buffer);
|
||||
// NB: buffer will be unrefed by the appsrc
|
||||
|
||||
// // TODO : detect user request for end
|
||||
// count++;
|
||||
// if (count > 120)
|
||||
// {
|
||||
// stop();
|
||||
// }
|
||||
|
||||
// restart counter
|
||||
time_ = 0;
|
||||
timeframe_ = 0;
|
||||
// next timestamp
|
||||
timestamp_ += frame_duration_;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class VideoRecorder : public Recorder
|
||||
GstElement *pipeline_;
|
||||
GstAppSrc *src_;
|
||||
GstBaseSink *sink_;
|
||||
GstClockTime time_;
|
||||
GstClockTime timeframe_;
|
||||
GstClockTime timestamp_;
|
||||
GstClockTime frame_duration_;
|
||||
|
||||
|
||||
25
Session.cpp
25
Session.cpp
@@ -30,12 +30,14 @@ Session::Session() : filename_(""), failedSource_(nullptr), active_(true)
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
// delete all recorders
|
||||
clearRecorders();
|
||||
|
||||
// delete all sources
|
||||
for(auto it = sources_.begin(); it != sources_.end(); ) {
|
||||
// erase this source from the list
|
||||
it = deleteSource(*it);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Session::setActive (bool on)
|
||||
@@ -241,14 +243,35 @@ Recorder *Session::frontRecorder()
|
||||
return recorders_.front();
|
||||
}
|
||||
|
||||
void Session::stopRecorders()
|
||||
{
|
||||
std::list<Recorder *>::iterator iter;
|
||||
for (iter=recorders_.begin(); iter != recorders_.end(); )
|
||||
(*iter)->stop();
|
||||
}
|
||||
|
||||
void Session::clearRecorders()
|
||||
{
|
||||
std::list<Recorder *>::iterator iter;
|
||||
for (iter=recorders_.begin(); iter != recorders_.end(); )
|
||||
{
|
||||
Recorder *rec = *iter;
|
||||
rec->stop();
|
||||
iter = recorders_.erase(iter);
|
||||
delete rec;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::transferRecorders(Session *dest)
|
||||
{
|
||||
if (dest == nullptr)
|
||||
return;
|
||||
|
||||
std::list<Recorder *>::iterator iter;
|
||||
for (iter=recorders_.begin(); iter != recorders_.end(); )
|
||||
{
|
||||
dest->recorders_.push_back(*iter);
|
||||
iter = recorders_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,12 @@ public:
|
||||
// get frame result of render
|
||||
inline FrameBuffer *frame () const { return render_.frame(); }
|
||||
|
||||
// add recorders
|
||||
// Recorders
|
||||
void addRecorder(Recorder *rec);
|
||||
Recorder *frontRecorder();
|
||||
void stopRecorders();
|
||||
void clearRecorders();
|
||||
void transferRecorders(Session *dest);
|
||||
|
||||
// configure rendering resolution
|
||||
void setResolution(glm::vec3 resolution);
|
||||
|
||||
@@ -889,7 +889,7 @@ void UserInterface::RenderPreview()
|
||||
}
|
||||
if (ImGui::BeginMenu("Record"))
|
||||
{
|
||||
if ( ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Save frame") )
|
||||
if ( ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Capture frame") )
|
||||
Mixer::manager().session()->addRecorder(new PNGRecorder);
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -902,19 +902,37 @@ void UserInterface::RenderPreview()
|
||||
}
|
||||
// start recording menu
|
||||
else {
|
||||
// start rec
|
||||
if ( ImGui::MenuItem( ICON_FA_CIRCLE " Record") )
|
||||
Mixer::manager().session()->addRecorder(new VideoRecorder);
|
||||
|
||||
ImGui::SetNextItemWidth(300);
|
||||
ImGui::Combo("##RecProfile", &Settings::application.record.profile, VideoRecorder::profile_name, IM_ARRAYSIZE(VideoRecorder::profile_name) );
|
||||
}
|
||||
|
||||
// Options menu
|
||||
ImGui::MenuItem("Destination", nullptr, false, false);
|
||||
{
|
||||
static char* name_path[4] = { nullptr };
|
||||
if ( name_path[0] == nullptr ) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
name_path[i] = (char *) malloc( 1024 * sizeof(char));
|
||||
sprintf( name_path[1], "%s", ICON_FA_HOME " Home");
|
||||
sprintf( name_path[2], "%s", ICON_FA_FOLDER " Session location");
|
||||
sprintf( name_path[3], "%s", ICON_FA_FOLDER_PLUS " Select");
|
||||
}
|
||||
if (Settings::application.record.path.empty())
|
||||
Settings::application.record.path = SystemToolkit::home_path();
|
||||
sprintf( name_path[0], "%s", Settings::application.record.path.c_str());
|
||||
|
||||
if (ImGui::MenuItem( Settings::application.record.path.c_str() ) ){
|
||||
int selected_path = 0;
|
||||
ImGui::SetNextItemWidth(300);
|
||||
ImGui::Combo("##RecDestination", &selected_path, name_path, 4);
|
||||
if (selected_path > 2)
|
||||
std::thread (FolderDialogOpen, record_browser_path_, &record_path_selected, Settings::application.record.path).detach();
|
||||
}
|
||||
|
||||
else if (selected_path > 1)
|
||||
Settings::application.record.path = SystemToolkit::path_filename( Mixer::manager().session()->filename() );
|
||||
else if (selected_path > 0)
|
||||
Settings::application.record.path = SystemToolkit::home_path();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
|
||||
Reference in New Issue
Block a user