mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-12 18:59:59 +01:00
Improved MediaPlayer memory consumption:
Avoid duplicating Timeline object and limit number of URI discoverers to two parallel threads.
This commit is contained in:
@@ -84,17 +84,29 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
|||||||
Log::Info("Checking file '%s'", uri.c_str());
|
Log::Info("Checking file '%s'", uri.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MediaInfo video_stream_info;
|
// Limiting the number of discoverer thread to TWO in parallel
|
||||||
|
// Otherwise, a large number of discoverers are executed (when loading a file)
|
||||||
|
// leading to a peak of memory and CPU usage : this causes slow down of FPS
|
||||||
|
// and a hungry consumption of RAM.
|
||||||
|
static std::mutex mtx_primary;
|
||||||
|
static std::mutex mtx_secondary;
|
||||||
|
bool use_primary = true;
|
||||||
|
if ( !mtx_primary.try_lock() ) { // non-blocking
|
||||||
|
use_primary = false;
|
||||||
|
mtx_secondary.lock(); // blocking
|
||||||
|
}
|
||||||
|
|
||||||
/* Instantiate the Discoverer */
|
MediaInfo video_stream_info;
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
GstDiscoverer *discoverer = gst_discoverer_new (15 * GST_SECOND, &err);
|
GstDiscoverer *discoverer = gst_discoverer_new (15 * GST_SECOND, &err);
|
||||||
|
|
||||||
|
/* Instantiate the Discoverer */
|
||||||
if (!discoverer) {
|
if (!discoverer) {
|
||||||
Log::Warning("MediaPlayer Error creating discoverer instance: %s\n", err->message);
|
Log::Warning("MediaPlayer Error creating discoverer instance: %s\n", err->message);
|
||||||
g_clear_error (&err);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GstDiscovererInfo *info = gst_discoverer_discover_uri (discoverer, uri.c_str(), &err);
|
GstDiscovererInfo *info = NULL;
|
||||||
|
info = gst_discoverer_discover_uri (discoverer, uri.c_str(), &err);
|
||||||
GstDiscovererResult result = gst_discoverer_info_get_result (info);
|
GstDiscovererResult result = gst_discoverer_info_get_result (info);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GST_DISCOVERER_URI_INVALID:
|
case GST_DISCOVERER_URI_INVALID:
|
||||||
@@ -142,7 +154,7 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
|||||||
video_stream_info.isimage = gst_discoverer_video_info_is_image(vinfo);
|
video_stream_info.isimage = gst_discoverer_video_info_is_image(vinfo);
|
||||||
// if its a video, set duration, framerate, etc.
|
// if its a video, set duration, framerate, etc.
|
||||||
if ( !video_stream_info.isimage ) {
|
if ( !video_stream_info.isimage ) {
|
||||||
video_stream_info.timeline.setEnd( gst_discoverer_info_get_duration (info) );
|
video_stream_info.end = gst_discoverer_info_get_duration (info) ;
|
||||||
video_stream_info.seekable = gst_discoverer_info_get_seekable (info);
|
video_stream_info.seekable = gst_discoverer_info_get_seekable (info);
|
||||||
video_stream_info.framerate_n = gst_discoverer_video_info_get_framerate_num(vinfo);
|
video_stream_info.framerate_n = gst_discoverer_video_info_get_framerate_num(vinfo);
|
||||||
video_stream_info.framerate_d = gst_discoverer_video_info_get_framerate_denom(vinfo);
|
video_stream_info.framerate_d = gst_discoverer_video_info_get_framerate_denom(vinfo);
|
||||||
@@ -150,9 +162,9 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
|||||||
video_stream_info.framerate_n = 25;
|
video_stream_info.framerate_n = 25;
|
||||||
video_stream_info.framerate_d = 1;
|
video_stream_info.framerate_d = 1;
|
||||||
}
|
}
|
||||||
video_stream_info.timeline.setStep( (GST_SECOND * static_cast<guint64>(video_stream_info.framerate_d)) / (static_cast<guint64>(video_stream_info.framerate_n)) );
|
video_stream_info.dt = ( (GST_SECOND * static_cast<guint64>(video_stream_info.framerate_d)) / (static_cast<guint64>(video_stream_info.framerate_n)) );
|
||||||
// confirm (or infirm) that its not a single frame
|
// confirm (or infirm) that its not a single frame
|
||||||
if ( video_stream_info.timeline.numFrames() < 2)
|
if ( video_stream_info.end < video_stream_info.dt * 2)
|
||||||
video_stream_info.isimage = true;
|
video_stream_info.isimage = true;
|
||||||
}
|
}
|
||||||
// try to fill-in the codec information
|
// try to fill-in the codec information
|
||||||
@@ -183,10 +195,19 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info)
|
||||||
gst_discoverer_info_unref (info);
|
gst_discoverer_info_unref (info);
|
||||||
|
|
||||||
g_object_unref( discoverer );
|
g_object_unref( discoverer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_clear_error (&err);
|
||||||
|
|
||||||
|
if (use_primary)
|
||||||
|
mtx_primary.unlock();
|
||||||
|
else
|
||||||
|
mtx_secondary.unlock();
|
||||||
|
|
||||||
// return the info
|
// return the info
|
||||||
return video_stream_info;
|
return video_stream_info;
|
||||||
}
|
}
|
||||||
@@ -204,8 +225,16 @@ void MediaPlayer::open(string path)
|
|||||||
|
|
||||||
// start URI discovering thread:
|
// start URI discovering thread:
|
||||||
discoverer_ = std::async( UriDiscoverer_, uri_);
|
discoverer_ = std::async( UriDiscoverer_, uri_);
|
||||||
|
|
||||||
// wait for discoverer to finish in the future (test in update)
|
// wait for discoverer to finish in the future (test in update)
|
||||||
|
|
||||||
|
// // debug without thread
|
||||||
|
// media_ = UriDiscoverer_(uri_);
|
||||||
|
// if (media_.valid) {
|
||||||
|
// timeline_.setEnd( media_.end );
|
||||||
|
// timeline_.setStep( media_.dt );
|
||||||
|
// execute_open();
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -350,10 +379,10 @@ void MediaPlayer::execute_open()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// in case discoverer failed to get duration
|
// in case discoverer failed to get duration
|
||||||
if (media_.timeline.end() == GST_CLOCK_TIME_NONE) {
|
if (timeline_.end() == GST_CLOCK_TIME_NONE) {
|
||||||
gint64 d = GST_CLOCK_TIME_NONE;
|
gint64 d = GST_CLOCK_TIME_NONE;
|
||||||
if ( gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &d) )
|
if ( gst_element_query_duration(pipeline_, GST_FORMAT_TIME, &d) )
|
||||||
media_.timeline.setEnd(d);
|
timeline_.setEnd(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
// all good
|
// all good
|
||||||
@@ -361,7 +390,7 @@ void MediaPlayer::execute_open()
|
|||||||
uri_.c_str(), media_.codec_name.c_str(), media_.width, media_.height);
|
uri_.c_str(), media_.codec_name.c_str(), media_.width, media_.height);
|
||||||
|
|
||||||
Log::Info("MediaPlayer %s Timeline [%ld %ld] %ld frames, %d gaps", std::to_string(id_).c_str(),
|
Log::Info("MediaPlayer %s Timeline [%ld %ld] %ld frames, %d gaps", std::to_string(id_).c_str(),
|
||||||
media_.timeline.begin(), media_.timeline.end(), media_.timeline.numFrames(), media_.timeline.numGaps());
|
timeline_.begin(), timeline_.end(), timeline_.numFrames(), timeline_.numGaps());
|
||||||
|
|
||||||
ready_ = true;
|
ready_ = true;
|
||||||
|
|
||||||
@@ -411,10 +440,8 @@ void MediaPlayer::close()
|
|||||||
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
|
|
||||||
// end pipeline
|
// end pipeline
|
||||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
|
gst_element_set_state (pipeline_, GST_STATE_NULL);
|
||||||
if (ret == GST_STATE_CHANGE_ASYNC) {
|
|
||||||
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
|
||||||
}
|
|
||||||
|
|
||||||
gst_object_unref (pipeline_);
|
gst_object_unref (pipeline_);
|
||||||
pipeline_ = nullptr;
|
pipeline_ = nullptr;
|
||||||
@@ -552,8 +579,8 @@ void MediaPlayer::play(bool on)
|
|||||||
|
|
||||||
// requesting to play, but stopped at end of stream : rewind first !
|
// requesting to play, but stopped at end of stream : rewind first !
|
||||||
if ( desired_state_ == GST_STATE_PLAYING) {
|
if ( desired_state_ == GST_STATE_PLAYING) {
|
||||||
if ( ( rate_ < 0.0 && position_ <= media_.timeline.next(0) )
|
if ( ( rate_ < 0.0 && position_ <= timeline_.next(0) )
|
||||||
|| ( rate_ > 0.0 && position_ >= media_.timeline.previous(media_.timeline.last()) ) )
|
|| ( rate_ > 0.0 && position_ >= timeline_.previous(timeline_.last()) ) )
|
||||||
rewind();
|
rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,13 +638,13 @@ void MediaPlayer::rewind()
|
|||||||
if (rate_ > 0.0) {
|
if (rate_ > 0.0) {
|
||||||
// begin is the end of a gab which includes the first PTS (if exists)
|
// begin is the end of a gab which includes the first PTS (if exists)
|
||||||
// normal case, begin is zero
|
// normal case, begin is zero
|
||||||
execute_seek_command( media_.timeline.next(0) );
|
execute_seek_command( timeline_.next(0) );
|
||||||
}
|
}
|
||||||
// playing backward, loop to endTimeInterval gap;
|
// playing backward, loop to endTimeInterval gap;
|
||||||
else {
|
else {
|
||||||
// end is the start of a gab which includes the last PTS (if exists)
|
// end is the start of a gab which includes the last PTS (if exists)
|
||||||
// normal case, end is last frame
|
// normal case, end is last frame
|
||||||
execute_seek_command( media_.timeline.previous(media_.timeline.last()) );
|
execute_seek_command( timeline_.previous(timeline_.last()) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,8 +655,8 @@ void MediaPlayer::step()
|
|||||||
if (!enabled_ || isPlaying())
|
if (!enabled_ || isPlaying())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( ( rate_ < 0.0 && position_ <= media_.timeline.next(0) )
|
if ( ( rate_ < 0.0 && position_ <= timeline_.next(0) )
|
||||||
|| ( rate_ > 0.0 && position_ >= media_.timeline.previous(media_.timeline.last()) ) )
|
|| ( rate_ > 0.0 && position_ >= timeline_.previous(timeline_.last()) ) )
|
||||||
rewind();
|
rewind();
|
||||||
|
|
||||||
// step
|
// step
|
||||||
@@ -644,7 +671,7 @@ bool MediaPlayer::go_to(GstClockTime pos)
|
|||||||
|
|
||||||
GstClockTime jumpPts = pos;
|
GstClockTime jumpPts = pos;
|
||||||
|
|
||||||
if (media_.timeline.gapAt(pos, gap)) {
|
if (timeline_.gapAt(pos, gap)) {
|
||||||
// if in a gap, find closest seek target
|
// if in a gap, find closest seek target
|
||||||
if (gap.is_valid()) {
|
if (gap.is_valid()) {
|
||||||
// jump in one or the other direction
|
// jump in one or the other direction
|
||||||
@@ -652,7 +679,7 @@ bool MediaPlayer::go_to(GstClockTime pos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ABS_DIFF (position_, jumpPts) > 2 * media_.timeline.step() ) {
|
if (ABS_DIFF (position_, jumpPts) > 2 * timeline_.step() ) {
|
||||||
ret = true;
|
ret = true;
|
||||||
seek( jumpPts );
|
seek( jumpPts );
|
||||||
}
|
}
|
||||||
@@ -666,7 +693,7 @@ void MediaPlayer::seek(GstClockTime pos)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// apply seek
|
// apply seek
|
||||||
GstClockTime target = CLAMP(pos, media_.timeline.begin(), media_.timeline.end());
|
GstClockTime target = CLAMP(pos, timeline_.begin(), timeline_.end());
|
||||||
execute_seek_command(target);
|
execute_seek_command(target);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -802,8 +829,11 @@ void MediaPlayer::update()
|
|||||||
{
|
{
|
||||||
media_ = discoverer_.get();
|
media_ = discoverer_.get();
|
||||||
// if its ok, open the media
|
// if its ok, open the media
|
||||||
if (media_.valid)
|
if (media_.valid) {
|
||||||
|
timeline_.setEnd( media_.end );
|
||||||
|
timeline_.setStep( media_.dt );
|
||||||
execute_open();
|
execute_open();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Log::Warning("MediaPlayer %s Loading cancelled", std::to_string(id_).c_str());
|
Log::Warning("MediaPlayer %s Loading cancelled", std::to_string(id_).c_str());
|
||||||
failed_ = true;
|
failed_ = true;
|
||||||
@@ -885,19 +915,18 @@ void MediaPlayer::update()
|
|||||||
else {
|
else {
|
||||||
// manage timeline: test if position falls into a gap
|
// manage timeline: test if position falls into a gap
|
||||||
TimeInterval gap;
|
TimeInterval gap;
|
||||||
if (position_ != GST_CLOCK_TIME_NONE && media_.timeline.gapAt(position_, gap)) {
|
if (position_ != GST_CLOCK_TIME_NONE && timeline_.gapAt(position_, gap)) {
|
||||||
// if in a gap, seek to next section
|
// if in a gap, seek to next section
|
||||||
if (gap.is_valid()) {
|
if (gap.is_valid()) {
|
||||||
// jump in one or the other direction
|
// jump in one or the other direction
|
||||||
GstClockTime jumpPts = (rate_>0.f) ? gap.end : gap.begin;
|
GstClockTime jumpPts = (rate_>0.f) ? gap.end : gap.begin;
|
||||||
// seek to next valid time (if not beginnig or end of timeline)
|
// seek to next valid time (if not beginnig or end of timeline)
|
||||||
if (jumpPts > media_.timeline.first() && jumpPts < media_.timeline.last())
|
if (jumpPts > timeline_.first() && jumpPts < timeline_.last())
|
||||||
seek( jumpPts );
|
seek( jumpPts );
|
||||||
// otherwise, we should loop
|
// otherwise, we should loop
|
||||||
else
|
else
|
||||||
need_loop = true;
|
need_loop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -934,7 +963,7 @@ void MediaPlayer::execute_seek_command(GstClockTime target)
|
|||||||
// create seek event with current position (rate changed ?)
|
// create seek event with current position (rate changed ?)
|
||||||
seek_pos = position_;
|
seek_pos = position_;
|
||||||
// target is given but useless
|
// target is given but useless
|
||||||
else if ( ABS_DIFF(target, position_) < media_.timeline.step()) {
|
else if ( ABS_DIFF(target, position_) < timeline_.step()) {
|
||||||
// ignore request
|
// ignore request
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -990,22 +1019,22 @@ double MediaPlayer::playSpeed() const
|
|||||||
|
|
||||||
Timeline *MediaPlayer::timeline()
|
Timeline *MediaPlayer::timeline()
|
||||||
{
|
{
|
||||||
return &media_.timeline;
|
return &timeline_;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MediaPlayer::currentTimelineFading()
|
float MediaPlayer::currentTimelineFading()
|
||||||
{
|
{
|
||||||
return media_.timeline.fadingAt(position_);
|
return timeline_.fadingAt(position_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::setTimeline(const Timeline &tl)
|
void MediaPlayer::setTimeline(const Timeline &tl)
|
||||||
{
|
{
|
||||||
media_.timeline = tl;
|
timeline_ = tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//void MediaPlayer::toggleGapInTimeline(GstClockTime from, GstClockTime to)
|
//void MediaPlayer::toggleGapInTimeline(GstClockTime from, GstClockTime to)
|
||||||
//{
|
//{
|
||||||
// return media_.timeline.toggleGaps(from, to);
|
// return timeline.toggleGaps(from, to);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
MediaInfo MediaPlayer::media() const
|
MediaInfo MediaPlayer::media() const
|
||||||
@@ -1074,8 +1103,8 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
|||||||
frame_[write_index_].position = buf->pts;
|
frame_[write_index_].position = buf->pts;
|
||||||
|
|
||||||
// set the start position (i.e. pts of first frame we got)
|
// set the start position (i.e. pts of first frame we got)
|
||||||
if (media_.timeline.begin() == GST_CLOCK_TIME_NONE) {
|
if (timeline_.begin() == GST_CLOCK_TIME_NONE) {
|
||||||
media_.timeline.setFirst(buf->pts);
|
timeline_.setFirst(buf->pts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// full but invalid frame : will be deleted next iteration
|
// full but invalid frame : will be deleted next iteration
|
||||||
@@ -1092,7 +1121,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
|||||||
// else; null buffer for EOS: give a position
|
// else; null buffer for EOS: give a position
|
||||||
else {
|
else {
|
||||||
frame_[write_index_].status = EOS;
|
frame_[write_index_].status = EOS;
|
||||||
frame_[write_index_].position = rate_ > 0.0 ? media_.timeline.end() : media_.timeline.begin();
|
frame_[write_index_].position = rate_ > 0.0 ? timeline_.end() : timeline_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlock access to frame
|
// unlock access to frame
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class Visitor;
|
|||||||
|
|
||||||
struct MediaInfo {
|
struct MediaInfo {
|
||||||
|
|
||||||
Timeline timeline;
|
|
||||||
guint width;
|
guint width;
|
||||||
guint par_width; // width to match pixel aspect ratio
|
guint par_width; // width to match pixel aspect ratio
|
||||||
guint height;
|
guint height;
|
||||||
@@ -34,6 +33,8 @@ struct MediaInfo {
|
|||||||
bool interlaced;
|
bool interlaced;
|
||||||
bool seekable;
|
bool seekable;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
GstClockTime dt;
|
||||||
|
GstClockTime end;
|
||||||
|
|
||||||
MediaInfo() {
|
MediaInfo() {
|
||||||
width = par_width = 640;
|
width = par_width = 640;
|
||||||
@@ -46,14 +47,15 @@ struct MediaInfo {
|
|||||||
interlaced = false;
|
interlaced = false;
|
||||||
seekable = false;
|
seekable = false;
|
||||||
valid = false;
|
valid = false;
|
||||||
|
dt = GST_CLOCK_TIME_NONE;
|
||||||
|
end = GST_CLOCK_TIME_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline MediaInfo& operator = (const MediaInfo& b)
|
inline MediaInfo& operator = (const MediaInfo& b)
|
||||||
{
|
{
|
||||||
if (this != &b) {
|
if (this != &b) {
|
||||||
this->timeline.setEnd( b.timeline.end() );
|
this->dt = b.dt;
|
||||||
this->timeline.setStep( b.timeline.step() );
|
this->end = b.end;
|
||||||
this->timeline.setFirst( b.timeline.first() );
|
|
||||||
this->width = b.width;
|
this->width = b.width;
|
||||||
this->par_width = b.par_width;
|
this->par_width = b.par_width;
|
||||||
this->height = b.height;
|
this->height = b.height;
|
||||||
@@ -273,6 +275,7 @@ private:
|
|||||||
|
|
||||||
// general properties of media
|
// general properties of media
|
||||||
MediaInfo media_;
|
MediaInfo media_;
|
||||||
|
Timeline timeline_;
|
||||||
std::future<MediaInfo> discoverer_;
|
std::future<MediaInfo> discoverer_;
|
||||||
|
|
||||||
// GST & Play status
|
// GST & Play status
|
||||||
|
|||||||
Reference in New Issue
Block a user