diff --git a/MediaPlayer.cpp b/MediaPlayer.cpp index b22fe4b..a7d07ae 100644 --- a/MediaPlayer.cpp +++ b/MediaPlayer.cpp @@ -161,20 +161,23 @@ void MediaPlayer::execute_open() // instruct the sink to send samples synched in time gst_base_sink_set_sync (GST_BASE_SINK(sink), true); + gst_base_sink_set_max_lateness (GST_BASE_SINK(sink), 0 ); + gst_base_sink_set_processing_deadline (GST_BASE_SINK(sink), 0 ); // instruct sink to use the required caps gst_app_sink_set_caps (GST_APP_SINK(sink), caps); // Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached. - gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 50); + gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 100); gst_app_sink_set_drop (GST_APP_SINK(sink), true); // set the callbacks - GstAppSinkCallbacks callbacks = { NULL }; + GstAppSinkCallbacks callbacks; callbacks.eos = callback_end_of_stream; callbacks.new_preroll = callback_new_preroll; callbacks.new_sample = callback_new_sample; gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); + gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); // done with ref to sink gst_object_unref (sink); @@ -556,49 +559,53 @@ void MediaPlayer::fill_texture(guint index) // initialize texture init_texture(index); - return; - } - - // use dual Pixel Buffer Object - if (pbo_size_ > 0) { - // In dual PBO mode, increment current index first then get the next index - pbo_index_ = (pbo_index_ + 1) % 2; - pbo_next_index_ = (pbo_index_ + 1) % 2; - - // bind PBO to read pixels - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_index_]); - // copy pixels from PBO to texture object - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, 0); - // bind the next PBO to write pixels - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]); - // See http://www.songho.ca/opengl/gl_pbo.html#map for more details - glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW); - // map the buffer object into client's memory - GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - if (ptr) { - // update data directly on the mapped buffer - // NB : equivalent but faster (memmove instead of memcpy ?) than - // glNamedBufferSubData(pboIds[nextIndex], 0, imgsize, vp->getBuffer()) - memmove(ptr, frame_[index].vframe.data[0], pbo_size_); - - // release pointer to mapping buffer - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - } - // done with PBO - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } else { - // without PBO, use standard opengl (slower) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, - GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]); + glBindTexture(GL_TEXTURE_2D, textureindex_); + + // use dual Pixel Buffer Object + if (pbo_size_ > 0) { + // In dual PBO mode, increment current index first then get the next index + pbo_index_ = (pbo_index_ + 1) % 2; + pbo_next_index_ = (pbo_index_ + 1) % 2; + + // bind PBO to read pixels + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_index_]); + // copy pixels from PBO to texture object + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, 0); + // bind the next PBO to write pixels + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]); + // See http://www.songho.ca/opengl/gl_pbo.html#map for more details + glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW); + // map the buffer object into client's memory + GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (ptr) { + // update data directly on the mapped buffer + // NB : equivalent but faster (memmove instead of memcpy ?) than + // glNamedBufferSubData(pboIds[nextIndex], 0, imgsize, vp->getBuffer()) + memmove(ptr, frame_[index].vframe.data[0], pbo_size_); + + // release pointer to mapping buffer + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + // done with PBO + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + else { + // without PBO, use standard opengl (slower) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, + GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]); + } } } void MediaPlayer::update() { // discard - if (!ready_) + if (!ready_) { + timecount_.reset(); return; + } // done discovering stream if (discoverer_ != nullptr) { @@ -610,10 +617,6 @@ void MediaPlayer::update() if (!enabled_) return; - // bind texture in any case (except if not initialized yet) - if (textureindex_>0) - glBindTexture(GL_TEXTURE_2D, textureindex_); - // local variables before trying to update guint read_index = 0; bool need_loop = false; @@ -631,11 +634,14 @@ void MediaPlayer::update() index_lock_.unlock(); // lock frame while reading it - frame_[read_index].access.lock(); + if (!frame_[read_index].access.try_lock()) + // do not block rendering if everything is too busy + return; // do not fill a frame twice if (frame_[read_index].status != EMPTY ) { + // is this an End-of-Stream frame ? if (frame_[read_index].status == EOS ) { @@ -648,8 +654,8 @@ void MediaPlayer::update() // fill the texture with the frame at reading index fill_texture(read_index); - // double update for pre-roll (needed because of dual FPO) - if (frame_[read_index].status == PREROLL ) + // double update for pre-roll and dual FPO (ensure frame is displayed now) + if (frame_[read_index].status == PREROLL && pbo_size_ > 0) fill_texture(read_index); } @@ -658,6 +664,11 @@ void MediaPlayer::update() // avoid reading it again frame_[read_index].status = EMPTY; + +// // TODO : try to do something when the update is too slow :( +// if ( timecount_.dt() > frame_duration_ * 2) { +// Log::Info("frame late %d", 2 * frame_duration_); +// } } // unkock frame after reading it @@ -665,7 +676,6 @@ void MediaPlayer::update() // manage loop mode if (need_loop && !isimage_) { - execute_loop_command(); } @@ -841,7 +851,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, MediaPlayer::FrameStatus status) return true; } -void MediaPlayer::callback_end_of_stream (GstAppSink *sink, gpointer p) +void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p) { MediaPlayer *m = (MediaPlayer *)p; if (m) { @@ -899,6 +909,7 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p) MediaPlayer *m = (MediaPlayer *)p; if (m) { + // fill frame with buffer if ( !m->fill_frame(buf, MediaPlayer::SAMPLE) ) ret = GST_FLOW_ERROR; @@ -913,6 +924,7 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p) // free buffer & sample gst_buffer_unref (buf); gst_sample_unref (sample); + } else ret = GST_FLOW_FLUSHING; @@ -970,6 +982,7 @@ void MediaPlayer::callback_discoverer_process (GstDiscoverer *discoverer, GstDis m->height_ = gst_discoverer_video_info_get_height(vinfo); m->isimage_ = gst_discoverer_video_info_is_image(vinfo); m->interlaced_ = gst_discoverer_video_info_is_interlaced(vinfo); + m->bitrate_ = gst_discoverer_video_info_get_bitrate(vinfo); guint parn = gst_discoverer_video_info_get_par_num(vinfo); guint pard = gst_discoverer_video_info_get_par_denom(vinfo); m->par_width_ = (m->width_ * parn) / pard; @@ -1048,7 +1061,7 @@ void TimeCounter::tic () // Exponential moving averate with previous framerate to filter jitter (50/50) // The divition of frame/time is done on long integer GstClockTime, counting in microsecond // NB: factor 100 to get 0.01 precision - fps = 0.5f * fps + 0.005f * static_cast( ( 100 * GST_SECOND * nbFrames ) / dt ); + fps = 0.5 * fps + 0.005 * static_cast( ( 100 * GST_SECOND * nbFrames ) / dt ); // reset counter every second if ( dt >= GST_SECOND) @@ -1073,11 +1086,11 @@ void TimeCounter::reset () last_time = gst_util_get_timestamp ();; tic_time = last_time; nbFrames = 0; - fps = 0.f; + fps = 0.0; } -float TimeCounter::frameRate() const +double TimeCounter::frameRate() const { - return fps; + return fps; } diff --git a/MediaPlayer.h b/MediaPlayer.h index 8e8d9f1..7945713 100644 --- a/MediaPlayer.h +++ b/MediaPlayer.h @@ -28,13 +28,13 @@ struct TimeCounter { GstClockTime last_time; GstClockTime tic_time; int nbFrames; - float fps; + gdouble fps; public: TimeCounter(); GstClockTime dt(); void tic(); void reset(); - float frameRate() const; + gdouble frameRate() const; }; struct MediaSegment @@ -252,6 +252,7 @@ private: guint width_; guint height_; guint par_width_; // width to match pixel aspect ratio + guint bitrate_; GstClockTime position_; GstClockTime start_position_; GstClockTime duration_; diff --git a/Mixer.cpp b/Mixer.cpp index ec37246..d66d3dc 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -203,7 +203,7 @@ void Mixer::update() // compute dt if (update_time_ == GST_CLOCK_TIME_NONE) update_time_ = gst_util_get_timestamp (); - gint64 current_time = gst_util_get_timestamp (); + guint64 current_time = gst_util_get_timestamp (); // dt is in milisecond, with fractional precision (from micro seconds) dt_ = static_cast( GST_TIME_AS_USECONDS(current_time - update_time_) * 0.001f); update_time_ = current_time; diff --git a/Mixer.h b/Mixer.h index 1368ea4..3eeb723 100644 --- a/Mixer.h +++ b/Mixer.h @@ -94,7 +94,7 @@ protected: LayerView layer_; TransitionView transition_; - gint64 update_time_; + guint64 update_time_; float dt_; };