From e9b3c602a00a01f2b009f2976e60c2c402ac1e80 Mon Sep 17 00:00:00 2001 From: Alan Guedes Date: Thu, 5 Jan 2023 17:07:27 -0500 Subject: [PATCH] use lua branch PlayerVideo::cb_Bus --- lib/PlayerVideo.cpp | 561 ++++++++++++++++++++------------------------ lib/PlayerVideo.h | 12 +- 2 files changed, 263 insertions(+), 310 deletions(-) diff --git a/lib/PlayerVideo.cpp b/lib/PlayerVideo.cpp index 806dbfa02..82e7bf8c9 100644 --- a/lib/PlayerVideo.cpp +++ b/lib/PlayerVideo.cpp @@ -19,26 +19,66 @@ along with Ginga. If not, see . */ #include "aux-gl.h" #include "PlayerVideo.h" +// clang-format off +GINGA_PRAGMA_DIAG_IGNORE (-Wfloat-equal) +// clang-format on + +#define _ERROR(fmt, ...)\ + ERROR ("PlayerGStreamer error: " fmt, ## __VA_ARGS__) + +#define _WARNING(fmt, ...)\ + WARNING ("PlayerGStreamer warning: " fmt, ## __VA_ARGS__) + +#define _TRACE(fmt, ...)\ + __ginga_log (g_print, fmt "\n", ## __VA_ARGS__) + +#define _TRACE_MESSAGE(msg, fmt, ...) \ + _TRACE ("%s: %s: " fmt, \ + GST_MESSAGE_SRC_NAME (msg), \ + GST_MESSAGE_TYPE_NAME (msg), \ + ## __VA_ARGS__) + +#define gstx_element_get_state(elt, state, pending, timeout) \ + G_STMT_START \ + { \ + if (unlikely (gst_element_get_state \ + ((elt), (state), (pending), (timeout)) \ + == GST_STATE_CHANGE_FAILURE)) \ + { \ + _ERROR ("failed to get %s state", gst_element_get_name (elt)); \ + } \ + } \ + G_STMT_END -#define gstx_element_get_state(elt, st, pend, tout) \ - g_assert (gst_element_get_state ((elt), (st), (pend), (tout)) \ - != GST_STATE_CHANGE_FAILURE) - -#define gstx_element_get_state_sync(elt, st, pend) \ - gstx_element_get_state ((elt), (st), (pend), GST_CLOCK_TIME_NONE) +#define gstx_element_get_state_sync(elt, state, pending) \ + G_STMT_START \ + { \ + gstx_element_get_state ((elt), (state), (pending), \ + GST_CLOCK_TIME_NONE); \ + } \ + G_STMT_END -#define gstx_element_set_state(elt, st) \ - g_assert (gst_element_set_state ((elt), (st)) != GST_STATE_CHANGE_FAILURE) +#define gstx_element_set_state(elt, state) \ + G_STMT_START \ + { \ + if (unlikely (gst_element_set_state ((elt), (state)) \ + == GST_STATE_CHANGE_FAILURE)) \ + { \ + _ERROR ("failed to set %s state", gst_element_get_name (elt)); \ + } \ + } \ + G_STMT_END -#define gstx_element_set_state_sync(elt, st) \ - G_STMT_START \ - { \ - gstx_element_set_state ((elt), (st)); \ - gstx_element_get_state_sync ((elt), nullptr, nullptr); \ - } \ +#define gstx_element_set_state_sync(elt, state) \ + G_STMT_START \ + { \ + gstx_element_set_state ((elt), (state)); \ + gstx_element_get_state_sync ((elt), NULL, NULL); \ + } \ G_STMT_END namespace ginga { + // Public. PlayerVideo::PlayerVideo (Formatter *formatter, Media *media) @@ -46,100 +86,61 @@ PlayerVideo::PlayerVideo (Formatter *formatter, Media *media) { GstBus *bus; gulong ret; + + GstCaps *caps; + GstStructure *st; + GstPad *pad; GstPad *ghost; - _playbin = nullptr; - _audio.bin = nullptr; - _audio.volume = nullptr; - _audio.pan = nullptr; - _audio.equalizer = nullptr; - _audio.convert = nullptr; - _audio.sink = nullptr; - _video.bin = nullptr; - _video.caps = nullptr; - _video.sink = nullptr; + _TRACE (""); + // Initialize GStreamer. if (!gst_is_initialized ()) { - GError *error = nullptr; - if (unlikely (!gst_init_check (nullptr, nullptr, &error))) + GError *err = NULL; + + if (unlikely (!gst_init_check (NULL, NULL, &err))) { - g_assert_nonnull (error); - ERROR ("%s", error->message); - g_error_free (error); + g_assert_nonnull (err); + _ERROR ("%s", err->message); + g_error_free (err); } + g_assert_null (err); } + // Create and initialize playbin. _playbin = gst_element_factory_make ("playbin", "playbin"); + if (unlikely (_playbin == NULL)) + { + _ERROR ("cannot create playbin"); + } g_assert_nonnull (_playbin); - //g_object_set (G_OBJECT (_playbin), "connection-speed", 56, nullptr); - bus = gst_pipeline_get_bus (GST_PIPELINE (_playbin)); g_assert_nonnull (bus); ret = gst_bus_add_watch (bus, (GstBusFunc) cb_Bus, this); g_assert (ret > 0); gst_object_unref (bus); - // Setup audio pipeline. - _audio.bin = gst_bin_new ("audio.bin"); - g_assert_nonnull (_audio.bin); - - _audio.volume = gst_element_factory_make ("volume", "audio.volume"); - g_assert_nonnull (_audio.volume); - - _audio.pan = gst_element_factory_make ("audiopanorama", "audio.pan"); - g_assert_nonnull (_audio.pan); - - _audio.equalizer - = gst_element_factory_make ("equalizer-3bands", "audio.equalizer"); - g_assert_nonnull (_audio.equalizer); - - _audio.convert - = gst_element_factory_make ("audioconvert", "audio.convert"); - g_assert_nonnull (_audio.convert); - - // Try to use ALSA if available. - _audio.sink = gst_element_factory_make ("alsasink", "audio.sink"); - if (_audio.sink == nullptr) - _audio.sink = gst_element_factory_make ("autoaudiosink", "audio.sink"); - g_assert_nonnull (_audio.sink); - - g_assert (gst_bin_add (GST_BIN (_audio.bin), _audio.volume)); - g_assert (gst_bin_add (GST_BIN (_audio.bin), _audio.pan)); - g_assert (gst_bin_add (GST_BIN (_audio.bin), _audio.equalizer)); - g_assert (gst_bin_add (GST_BIN (_audio.bin), _audio.convert)); - g_assert (gst_bin_add (GST_BIN (_audio.bin), _audio.sink)); - g_assert (gst_element_link (_audio.volume, _audio.pan)); - g_assert (gst_element_link (_audio.pan, _audio.equalizer)); - g_assert (gst_element_link (_audio.equalizer, _audio.convert)); - g_assert (gst_element_link (_audio.convert, _audio.sink)); - - pad = gst_element_get_static_pad (_audio.volume, "sink"); - g_assert_nonnull (pad); - ghost = gst_ghost_pad_new ("sink", pad); - g_assert_nonnull (ghost); - g_assert (gst_element_add_pad (_audio.bin, ghost)); - gst_object_unref (pad); - g_object_set (G_OBJECT (_playbin), "audio-sink", _audio.bin, nullptr); - - // Setup video pipeline. + // Setup video the processing bin. _video.bin = gst_bin_new ("video.bin"); g_assert_nonnull (_video.bin); - _video.caps = gst_element_factory_make ("capsfilter", "video.filter"); + _video.caps = gst_element_factory_make ("capsfilter", "video.caps"); g_assert_nonnull (_video.caps); + st = gst_structure_new_empty ("video/x-raw"); + gst_structure_set (st, "format", G_TYPE_STRING, "BGRA", nullptr); + + caps = gst_caps_new_full (st, nullptr); + g_assert_nonnull (caps); + g_object_set (_video.caps, "caps", caps, nullptr); + gst_caps_unref (caps); + _video.sink = gst_element_factory_make ("appsink", "video.sink"); g_assert_nonnull (_video.sink); -#ifdef __APPLE__ - g_object_set (_video.sink, "max-buffers", 1, "drop", true, nullptr); -#else - g_object_set (_video.sink, "max-buffers", 100, "drop", true, nullptr); -#endif - g_assert (gst_bin_add (GST_BIN (_video.bin), _video.caps)); g_assert (gst_bin_add (GST_BIN (_video.bin), _video.sink)); g_assert (gst_element_link (_video.caps, _video.sink)); @@ -150,86 +151,44 @@ PlayerVideo::PlayerVideo (Formatter *formatter, Media *media) g_assert_nonnull (ghost); g_assert (gst_element_add_pad (_video.bin, ghost)); gst_object_unref (pad); - g_object_set (G_OBJECT (_playbin), "video-sink", _video.bin, nullptr); - - // Callbacks. - _callbacks.eos = nullptr; //Seldom the EOS event is triggered by appsink. - _callbacks.new_preroll = nullptr; - _callbacks.new_sample = cb_NewSample; - gst_app_sink_set_callbacks (GST_APP_SINK (_video.sink), &_callbacks, this, - nullptr); - - g_signal_connect (G_OBJECT (_playbin), "about-to-finish", (GCallback) cb_EOS, - this); - - // Initialize some handled properties. - static set handled = { "balance", "bass", "freeze", "mute", - "speed", "treble", "volume" }; - this->initProperties (&handled); - // stackAction (Player::PROP_TIME, "time", "76s"); + g_object_set (G_OBJECT (_playbin), "video-sink", _video.bin, nullptr); } PlayerVideo::~PlayerVideo () { + _TRACE (""); + gstx_element_set_state_sync (_playbin, GST_STATE_NULL); + gst_object_unref (_playbin); } void PlayerVideo::start () { - GstCaps *caps; - GstStructure *st; - GstStateChangeReturn ret; - - g_assert (_state != OCCURRING); - TRACE ("starting %s", _id.c_str ()); - - st = gst_structure_new_empty ("video/x-raw"); - gst_structure_set (st, "format", G_TYPE_STRING, "BGRA", nullptr); - - caps = gst_caps_new_full (st, nullptr); - g_assert_nonnull (caps); - g_object_set (_video.caps, "caps", caps, nullptr); - gst_caps_unref (caps); - - Player::setEOS (false); - g_atomic_int_set (&_sample_flag, 0); - - g_object_set (_audio.volume, "volume", _prop.volume, "mute", _prop.mute, - nullptr); - - g_object_set (_audio.pan, "panorama", _prop.balance, nullptr); - - g_object_set (_audio.equalizer, "band0", _prop.bass, "band1", - _prop.treble, "band2", _prop.treble, nullptr); - - ret = gst_element_set_state (_playbin, GST_STATE_PLAYING); - if (unlikely (ret == GST_STATE_CHANGE_FAILURE)) - Player::setEOS (true); + g_assert_nonnull (_playbin); + _TRACE (""); + gstx_element_set_state (_playbin, GST_STATE_PLAYING); Player::start (); } void PlayerVideo::stop () { - g_assert (_state != SLEEPING); - TRACE ("stopping %s", _id.c_str ()); + g_assert_nonnull (_playbin); - gstx_element_set_state_sync (_playbin, GST_STATE_NULL); - gst_object_unref (_playbin); - _playbin = nullptr; - _stack_actions.clear (); + _TRACE (""); + gstx_element_set_state (_playbin, GST_STATE_NULL); Player::stop (); } void PlayerVideo::pause () { - g_assert (_state != PAUSED && _state != SLEEPING); - TRACE ("pausing %s", _id.c_str ()); + g_assert_nonnull (_playbin); - gstx_element_set_state_sync (_playbin, GST_STATE_PAUSED); + _TRACE (""); + gstx_element_set_state (_playbin, GST_STATE_PAUSED); Player::pause (); } @@ -311,16 +270,11 @@ PlayerVideo::redraw (cairo_t *cr) static cairo_user_data_key_t key; cairo_status_t status; - g_assert (_state != SLEEPING); - - if (Player::getEOS ()) - goto done; - - if (!g_atomic_int_compare_and_exchange (&_sample_flag, 1, 0)) - goto done; + // if (Player::getEOS ()) + // goto done; - sample = gst_app_sink_pull_sample (GST_APP_SINK (_video.sink)); - if (sample == nullptr) + sample = gst_app_sink_try_pull_sample (GST_APP_SINK (_video.sink), 0); + if (sample == NULL) goto done; buf = gst_sample_get_buffer (sample); @@ -337,29 +291,17 @@ PlayerVideo::redraw (cairo_t *cr) height = GST_VIDEO_FRAME_HEIGHT (&v_frame); stride = (int) GST_VIDEO_FRAME_PLANE_STRIDE (&v_frame, 0); - if (_opengl) - { - if (_gltexture) - GL::delete_texture (&_gltexture); - // FIXME: Do not create a new texture for each frame. - GL::create_texture (&_gltexture, width, height, pixels); - gst_video_frame_unmap (&v_frame); - gst_sample_unref (sample); - } - else - { - if (_surface != nullptr) - cairo_surface_destroy (_surface); - - _surface = cairo_image_surface_create_for_data ( - pixels, CAIRO_FORMAT_ARGB32, width, height, stride); - g_assert_nonnull (_surface); - gst_video_frame_unmap (&v_frame); - status = cairo_surface_set_user_data ( - _surface, &key, (void *) sample, - (cairo_destroy_func_t) gst_sample_unref); - g_assert (status == CAIRO_STATUS_SUCCESS); - } + if (_surface != NULL) + cairo_surface_destroy (_surface); + + _surface = cairo_image_surface_create_for_data + (pixels, CAIRO_FORMAT_ARGB32, width, height, stride); + g_assert_nonnull (_surface); + gst_video_frame_unmap (&v_frame); + status = cairo_surface_set_user_data + (_surface, &key, (void *) sample, + (cairo_destroy_func_t) gst_sample_unref); + g_assert (status == CAIRO_STATUS_SUCCESS); done: Player::redraw (cr); @@ -391,8 +333,154 @@ PlayerVideo::getStreamMediaDuration () TRACE ("Get %s duration failed", _id.c_str ()); return dur; } +gboolean +PlayerVideo::cb_Bus (GstBus *bus, GstMessage *msg, PlayerVideo *player) +{ + g_assert_nonnull (bus); + g_assert_nonnull (msg); + g_assert_nonnull (player); -// Protected. + switch (GST_MESSAGE_TYPE (msg)) + { + case GST_MESSAGE_ASYNC_DONE: + { + GstClockTime running_time; + + gst_message_parse_async_done (msg, &running_time); + _TRACE_MESSAGE (msg, "%" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time)); + break; + } + case GST_MESSAGE_BUFFERING: + { + gint percent = 0; + gst_message_parse_buffering (msg, &percent); + _TRACE_MESSAGE (msg, "%d percent", percent); + break; + } + case GST_MESSAGE_DURATION_CHANGED: + { + GstFormat fmt; + const char *name; + gint64 dur; + + gst_message_parse_duration (msg, &fmt, &dur); + name = gst_format_get_name (fmt); + switch (fmt) + { + case GST_FORMAT_UNDEFINED: + _TRACE_MESSAGE (msg, "%s %" G_GINT64_FORMAT " undefined", name, + dur); + break; + case GST_FORMAT_DEFAULT: + _TRACE_MESSAGE (msg, "%s %" G_GINT64_FORMAT " default", name, + dur); + break; + case GST_FORMAT_BYTES: + _TRACE_MESSAGE (msg, "%s %" G_GINT64_FORMAT " bytes", name, + dur); + break; + case GST_FORMAT_TIME: + _TRACE_MESSAGE (msg, "%s %" GST_TIME_FORMAT, name, + GST_TIME_ARGS (dur)); + break; + case GST_FORMAT_BUFFERS: + _TRACE_MESSAGE (msg, "%s %" G_GINT64_FORMAT " buffers", name, + dur); + break; + case GST_FORMAT_PERCENT: + _TRACE_MESSAGE (msg, "%s %" G_GINT64_FORMAT " percent", name, + dur); + break; + } + + break; + } + case GST_MESSAGE_EOS: + { + _TRACE_MESSAGE (msg, ""); + // player->stop (); // set eos or use player state to signal eos + break; + } + case GST_MESSAGE_NEW_CLOCK: + { + GstClock *clock = NULL; + + gst_message_parse_new_clock (msg, &clock); + _TRACE_MESSAGE (msg, "%s", + (clock ? GST_OBJECT_NAME (clock) : "NULL")); + break; + } + case GST_MESSAGE_STATE_CHANGED: + { + GstState old_state; + GstState new_state; + GstState pending_state; + + if (GST_ELEMENT (msg->src) != player->_playbin) + { + break; + } + + gst_message_parse_state_changed (msg, &old_state, &new_state, + &pending_state); + + if (pending_state != GST_STATE_VOID_PENDING) + { + _TRACE_MESSAGE (msg, "%s->%s (pending %s)", + gst_element_state_get_name (old_state), + gst_element_state_get_name (new_state), + gst_element_state_get_name (pending_state)); + } + else + { + _TRACE_MESSAGE (msg, "%s->%s", + gst_element_state_get_name (old_state), + gst_element_state_get_name (new_state)); + } + break; + } + case GST_MESSAGE_ERROR: + case GST_MESSAGE_WARNING: + { + GstObject *obj = nullptr; + GError *error = nullptr; + + obj = GST_MESSAGE_SRC (msg); + g_assert_nonnull (obj); + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) + { + gst_message_parse_error (msg, &error, nullptr); + g_assert_nonnull (error); + _ERROR ("%s", error->message); + } + else + { + gst_message_parse_warning (msg, &error, nullptr); + g_assert_nonnull (error); + _WARNING ("%s", error->message); + } + g_error_free (error); + gst_object_unref (obj); + break; + } + default: + { + _TRACE_MESSAGE (msg, ""); + break; + } + } + return TRUE; // keep callback installed +} + +void +PlayerVideo::setURI (const string &uri) +{ + Player::_prop.uri = uri; + TRACE ("uri = %s\n", Player::_prop.uri.c_str ()); + g_object_set (G_OBJECT (_playbin), "uri", Player::_prop.uri.c_str (), NULL); +} bool PlayerVideo::doSetProperty (Property code, unused (const string &name), @@ -511,47 +599,6 @@ PlayerVideo::doSetProperty (Property code, unused (const string &name), return true; } -// Private. - -void -PlayerVideo::initProperties (set *props) -{ - Property code; - string defval; - for (auto name : *props) - { - code = Player::getPlayerProperty (name, &defval); - if (code == Player::PROP_UNKNOWN) - continue; - - switch (code) - { - case PROP_BALANCE: - _prop.balance = xstrtodorpercent (defval, nullptr); - break; - case PROP_BASS: - _prop.bass = xstrtodorpercent (defval, nullptr); - break; - case PROP_FREEZE: - _prop.freeze = ginga::parse_bool (defval); - break; - case PROP_MUTE: - _prop.mute = ginga::parse_bool (defval); - break; - case PROP_SPEED: - _prop.speed = xstrtod (defval); - break; - case PROP_TREBLE: - _prop.treble = xstrtodorpercent (defval, nullptr); - break; - case PROP_VOLUME: - _prop.volume = xstrtodorpercent (defval, nullptr); - break; - default: - break; - } - } -} void PlayerVideo::stackAction (Property code, unused (const string &name), @@ -598,99 +645,5 @@ PlayerVideo::getPipelineState () return gst_element_state_get_name (curr); } -// Private: Static (GStreamer callbacks). - -gboolean -PlayerVideo::cb_Bus (GstBus *bus, GstMessage *msg, PlayerVideo *player) -{ - g_assert_nonnull (bus); - g_assert_nonnull (msg); - g_assert_nonnull (player); - - switch (GST_MESSAGE_TYPE (msg)) - { - case GST_MESSAGE_ERROR: - case GST_MESSAGE_WARNING: - { - GstObject *obj = nullptr; - GError *error = nullptr; - - obj = GST_MESSAGE_SRC (msg); - g_assert_nonnull (obj); - - if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) - { - gst_message_parse_error (msg, &error, nullptr); - g_assert_nonnull (error); - - string advice = ""; - switch (error->code) - { - case GST_CORE_ERROR_MISSING_PLUGIN: - { - advice = "Check if the following plugins are installed: gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad and gstreamer1.0-libav."; - break; - } - default: - break; - } - - ERROR ("%s %s", error->message, advice.c_str ()); - } - else - { - gst_message_parse_warning (msg, &error, nullptr); - g_assert_nonnull (error); - - string advice = ""; - switch (error->code) - { - case GST_LIBRARY_ERROR_ENCODE: - { - advice = "You should verify if there is some decoder installed to run this file type."; - break; - } - default: - break; - } - - WARNING ("%s %s", error->message, advice.c_str ()); - } - g_error_free (error); - gst_object_unref (obj); - break; - } - case GST_MESSAGE_STATE_CHANGED: - { - if (player->getPipelineState () == "PAUSED" - || player->getPipelineState () == "PLAYING") - { - player->doStackedActions (); - } - break; - } - default: - break; - } - return TRUE; -} - -GstFlowReturn -PlayerVideo::cb_NewSample (unused (GstAppSink *appsink), gpointer data) -{ - PlayerVideo *player = (PlayerVideo *) data; - g_assert_nonnull (player); - g_atomic_int_compare_and_exchange (&player->_sample_flag, 0, 1); - return GST_FLOW_OK; -} - -void -PlayerVideo::cb_EOS (unused (GstElement *playbin), gpointer data) -{ - PlayerVideo *player = (PlayerVideo *) data; - g_assert_nonnull (player); - TRACE ("EOS of %s", player->_id.c_str ()); - if (unlikely (!player->getFreeze ())) - player->setEOS (true); } diff --git a/lib/PlayerVideo.h b/lib/PlayerVideo.h index 6ec4119f3..7e1a6a636 100644 --- a/lib/PlayerVideo.h +++ b/lib/PlayerVideo.h @@ -19,6 +19,7 @@ along with Ginga. If not, see . */ #define PLAYER_VIDEO_H #include "Player.h" +#include namespace ginga { class Media; @@ -32,9 +33,13 @@ class PlayerVideo : public Player void pause () override; void resume () override; void redraw (cairo_t *) override; - + // GStreamer callback. + static gboolean cb_Bus (GstBus *, GstMessage *, PlayerVideo *); + protected: bool doSetProperty (Property, const string &, const string &) override; + void setURI (const string &uri); + void seek (gint64); void speed (double); gint64 getPipelineTime (); @@ -83,11 +88,6 @@ class PlayerVideo : public Player void doStackedActions (); bool getFreeze (); string getPipelineState (); - - // GStreamer callbacks. - static gboolean cb_Bus (GstBus *, GstMessage *, PlayerVideo *); - static GstFlowReturn cb_NewSample (GstAppSink *, gpointer); - static void cb_EOS (GstElement *, gpointer); }; }