mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-16 12:49:59 +01:00
BugFix MediaPlayer speed change
Changed MediaPlayer::setEffect() mechanism to have a gst pipeline compatible with instant rate change.
This commit is contained in:
@@ -57,6 +57,7 @@ MediaPlayer::MediaPlayer()
|
|||||||
seeking_ = false;
|
seeking_ = false;
|
||||||
rewind_on_disable_ = false;
|
rewind_on_disable_ = false;
|
||||||
force_software_decoding_ = false;
|
force_software_decoding_ = false;
|
||||||
|
force_basic_speedchange_ = false;
|
||||||
decoder_name_ = "";
|
decoder_name_ = "";
|
||||||
rate_ = 1.0;
|
rate_ = 1.0;
|
||||||
position_ = GST_CLOCK_TIME_NONE;
|
position_ = GST_CLOCK_TIME_NONE;
|
||||||
@@ -294,7 +295,7 @@ void MediaPlayer::execute_open()
|
|||||||
// "uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! appsink "
|
// "uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! appsink "
|
||||||
// equivalent to command line
|
// equivalent to command line
|
||||||
// "gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink"
|
// "gst-launch-1.0 uridecodebin uri=file:///path_to_file/filename.mp4 ! videoconvert ! ximagesink"
|
||||||
std::string description = "uridecodebin name=decoder uri=" + uri_ + " ! queue max-size-time=0 ! ";
|
std::string description = "uridecodebin name=decoder uri=" + uri_ + " ! ";
|
||||||
// NB: queue adds some control over the buffer, thereby limiting the frame delay. zero size means no buffering
|
// NB: queue adds some control over the buffer, thereby limiting the frame delay. zero size means no buffering
|
||||||
|
|
||||||
// string description = "uridecodebin name=decoder uri=" + uri_ + " decoder. ! ";
|
// string description = "uridecodebin name=decoder uri=" + uri_ + " decoder. ! ";
|
||||||
@@ -310,11 +311,22 @@ void MediaPlayer::execute_open()
|
|||||||
if (media_.interlaced)
|
if (media_.interlaced)
|
||||||
description += "deinterlace method=2 ! ";
|
description += "deinterlace method=2 ! ";
|
||||||
|
|
||||||
// effect
|
// queue
|
||||||
description += "videoconvert name=prev ! identity name=effect ! ";
|
description += "queue max-size-time=0 name=prev ! ";
|
||||||
|
|
||||||
// video convertion algorithm (should only do colorspace conversion, no scaling)
|
// video convertion algorithm (should only do colorspace conversion, no scaling)
|
||||||
description += "videoconvert name=post ! "; // fast
|
// chroma-resampler:
|
||||||
|
// Duplicates the samples when upsampling and drops when downsampling 0
|
||||||
|
// Uses linear interpolation 1 (default)
|
||||||
|
// Uses cubic interpolation 2
|
||||||
|
// Uses sinc interpolation 3
|
||||||
|
// dither:
|
||||||
|
// no dithering 0
|
||||||
|
// propagate rounding errors downwards 1
|
||||||
|
// Dither with floyd-steinberg error diffusion 2
|
||||||
|
// Dither with Sierra Lite error diffusion 3
|
||||||
|
// ordered dither using a bayer pattern 4 (default)
|
||||||
|
description += "videoconvert chroma-resampler=1 dither=0 name=post ! "; // fast
|
||||||
|
|
||||||
// hack to compensate for lack of PTS in gif animations
|
// hack to compensate for lack of PTS in gif animations
|
||||||
if (media_.codec_name.compare("image/gst-libav-gif") == 0){
|
if (media_.codec_name.compare("image/gst-libav-gif") == 0){
|
||||||
@@ -433,26 +445,6 @@ bool MediaPlayer::setEffect(const std::string &pipeline_element)
|
|||||||
if ( pipeline_ == nullptr )
|
if ( pipeline_ == nullptr )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// try to create the pipeline element given
|
|
||||||
GError *error = NULL;
|
|
||||||
std::string elem = pipeline_element + " name=effect ";
|
|
||||||
GstElement *el = gst_parse_launch( elem.c_str(), &error);
|
|
||||||
if (error != NULL) {
|
|
||||||
Log::Warning("MediaPlayer %s Error constructing %s:\n%s", std::to_string(id_).c_str(), pipeline_element.c_str(), error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (el == NULL) {
|
|
||||||
Log::Warning("MediaPlayer %s Could not parse %s", std::to_string(id_).c_str(), pipeline_element.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find all GST elements needed
|
|
||||||
GstElement *effect = gst_bin_get_by_name (GST_BIN (pipeline_), "effect");
|
|
||||||
if (!effect) {
|
|
||||||
Log::Warning("MediaPlayer %s Could not find effect", std::to_string(id_).c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
GstElement *prev = gst_bin_get_by_name (GST_BIN (pipeline_), "prev");
|
GstElement *prev = gst_bin_get_by_name (GST_BIN (pipeline_), "prev");
|
||||||
if (!prev) {
|
if (!prev) {
|
||||||
Log::Warning("MediaPlayer %s Could not find prev", std::to_string(id_).c_str());
|
Log::Warning("MediaPlayer %s Could not find prev", std::to_string(id_).c_str());
|
||||||
@@ -464,45 +456,81 @@ bool MediaPlayer::setEffect(const std::string &pipeline_element)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try to create the pipeline element given
|
||||||
|
GError *error = NULL;
|
||||||
|
std::string elem = pipeline_element + " name=effect ";
|
||||||
|
GstElement *effect = gst_parse_launch( elem.c_str(), &error);
|
||||||
|
if (error != NULL) {
|
||||||
|
Log::Warning("MediaPlayer %s Error constructing %s:\n%s", std::to_string(id_).c_str(), pipeline_element.c_str(), error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (effect == NULL) {
|
||||||
|
Log::Warning("MediaPlayer %s Could not parse %s", std::to_string(id_).c_str(), pipeline_element.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to create the pipeline converter needed
|
||||||
|
elem = "videoconvert name=conv ";
|
||||||
|
GstElement *conv = gst_parse_launch( elem.c_str(), &error);
|
||||||
|
if (error != NULL) {
|
||||||
|
Log::Warning("MediaPlayer %s Error constructing %s:\n%s", std::to_string(id_).c_str(), elem.c_str(), error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (conv == NULL) {
|
||||||
|
Log::Warning("MediaPlayer %s Could not parse %s", std::to_string(id_).c_str(), elem.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// PAUSE pipeline
|
// PAUSE pipeline
|
||||||
execute_play_command(false);
|
execute_play_command(false);
|
||||||
|
|
||||||
// assume failure
|
// remove previous (if exists)
|
||||||
bool success = false;
|
GstElement *ef = gst_bin_get_by_name (GST_BIN (pipeline_), "effect");
|
||||||
|
if (ef) {
|
||||||
// remove the previous effect (this deconnects from prev and post)
|
// remove the previous effect (this deconnects from prev and post)
|
||||||
gst_object_ref(effect);
|
if ( !gst_bin_remove (GST_BIN (pipeline_), ef) ) {
|
||||||
if (gst_bin_remove (GST_BIN (pipeline_), effect) ) {
|
Log::Warning("MediaPlayer %s Could not remove effect", std::to_string(id_).c_str());
|
||||||
// add new element to the pipeline
|
return false;
|
||||||
if ( gst_bin_add (GST_BIN (pipeline_), el) ) {
|
}
|
||||||
|
gst_object_unref(ef);
|
||||||
|
}
|
||||||
|
|
||||||
|
GstElement *co = gst_bin_get_by_name (GST_BIN (pipeline_), "conv");
|
||||||
|
if (co) {
|
||||||
|
// remove the previous convertor (this deconnects from prev and post)
|
||||||
|
if ( !gst_bin_remove (GST_BIN (pipeline_), co) ) {
|
||||||
|
Log::Warning("MediaPlayer %s Could not remove convert for effect", std::to_string(id_).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gst_object_unref(co);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gst_element_unlink(prev, post);
|
||||||
|
|
||||||
|
// add new elements to the pipeline
|
||||||
|
if ( gst_bin_add (GST_BIN (pipeline_), conv) && gst_bin_add (GST_BIN (pipeline_), effect) ) {
|
||||||
// reconnect pipeline
|
// reconnect pipeline
|
||||||
if ( gst_element_link_many (prev, el, post, NULL) )
|
if ( !gst_element_link_many (prev, conv, effect, post, NULL) ) {
|
||||||
// all good !! success !!
|
gst_bin_remove (GST_BIN (pipeline_), conv);
|
||||||
success = true;
|
gst_bin_remove (GST_BIN (pipeline_), effect);
|
||||||
else {
|
// restore pileline without any effect
|
||||||
gst_bin_remove (GST_BIN (pipeline_), el);
|
gst_element_link_many (prev, post, NULL);
|
||||||
|
// inform
|
||||||
Log::Warning("MediaPlayer %s Could not re-link effect", std::to_string(id_).c_str());
|
Log::Warning("MediaPlayer %s Could not re-link effect", std::to_string(id_).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Log::Warning("MediaPlayer %s Could not add new effect", std::to_string(id_).c_str());
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
// can delete effect
|
|
||||||
gst_object_unref(effect);
|
|
||||||
else {
|
else {
|
||||||
// restore effect into the pileline
|
// restore pileline without any effect
|
||||||
gst_bin_add (GST_BIN (pipeline_), effect);
|
gst_element_link_many (prev, post, NULL);
|
||||||
gst_element_link_many (prev, effect, post, NULL);
|
Log::Warning("MediaPlayer %s Could not add new effect", std::to_string(id_).c_str());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
Log::Warning("MediaPlayer %s Could not remove effect", std::to_string(id_).c_str());
|
|
||||||
|
|
||||||
// PLAY pipeline
|
// PLAY pipeline
|
||||||
execute_play_command(true);
|
execute_play_command(true);
|
||||||
|
|
||||||
return success;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaPlayer::isOpen() const
|
bool MediaPlayer::isOpen() const
|
||||||
@@ -1217,13 +1245,15 @@ void MediaPlayer::setPlaySpeed(double s)
|
|||||||
// and the seek is not flushing. (Since: 1.18)
|
// and the seek is not flushing. (Since: 1.18)
|
||||||
#if GST_VERSION_MINOR > 17 && GST_VERSION_MAJOR > 0
|
#if GST_VERSION_MINOR > 17 && GST_VERSION_MAJOR > 0
|
||||||
// if all conditions to use GST_SEEK_FLAG_INSTANT_RATE_CHANGE are met
|
// if all conditions to use GST_SEEK_FLAG_INSTANT_RATE_CHANGE are met
|
||||||
if ( pipeline_ != nullptr && media_.seekable && !change_direction ) {
|
if ( pipeline_ != nullptr && media_.seekable && !change_direction && !force_basic_speedchange_) {
|
||||||
|
|
||||||
GstEvent *seek_event = gst_event_new_seek (rate_, GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
|
GstEvent *seek_event = gst_event_new_seek (rate_, GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
|
||||||
GST_SEEK_TYPE_NONE, 0, GST_SEEK_TYPE_NONE, 0);
|
GST_SEEK_TYPE_NONE, 0, GST_SEEK_TYPE_NONE, 0);
|
||||||
|
|
||||||
if (seek_event && !gst_element_send_event(pipeline_, seek_event) )
|
if (seek_event && !gst_element_send_event(pipeline_, seek_event) ) {
|
||||||
Log::Warning("MediaPlayer %s setPlaySpeed failed", std::to_string(id_).c_str());
|
Log::Info("MediaPlayer %s; cannot perform instantaneous speed change. Change of play speed will not be smooth.", std::to_string(id_).c_str());
|
||||||
|
force_basic_speedchange_ = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
seeking_ = true;
|
seeking_ = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ private:
|
|||||||
bool enabled_;
|
bool enabled_;
|
||||||
bool rewind_on_disable_;
|
bool rewind_on_disable_;
|
||||||
bool force_software_decoding_;
|
bool force_software_decoding_;
|
||||||
|
bool force_basic_speedchange_;
|
||||||
std::string decoder_name_;
|
std::string decoder_name_;
|
||||||
Metronome::Synchronicity metro_sync_;
|
Metronome::Synchronicity metro_sync_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user