BugFix MediaPlayer speed change

Changed MediaPlayer::setEffect() mechanism to have a gst pipeline compatible with instant rate change.
This commit is contained in:
Bruno Herbelin
2023-07-05 21:59:28 +02:00
parent d6a684bbe7
commit 74337b2699
2 changed files with 85 additions and 54 deletions

View File

@@ -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;
} }

View File

@@ -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_;