mirror of
				https://github.com/Rafostar/clapper.git
				synced 2025-10-31 02:15:36 +01:00 
			
		
		
		
	Merge pull request #438 from Rafostar/download-cache
clapper: Add media caching via download to local storage
This commit is contained in:
		
							
								
								
									
										57
									
								
								examples/clapper-gtk/download_cache/python/example.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										57
									
								
								examples/clapper-gtk/download_cache/python/example.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| import gi | ||||
| gi.require_version('Adw', '1') | ||||
| gi.require_version('Clapper', '0.0') | ||||
| gi.require_version('ClapperGtk', '0.0') | ||||
| gi.require_version('GLib', '2.0') | ||||
| gi.require_version('Gtk', '4.0') | ||||
| from gi.repository import Adw, Clapper, ClapperGtk, GLib, Gtk | ||||
| import shutil | ||||
|  | ||||
| Clapper.init(None) | ||||
|  | ||||
| download_dir = GLib.build_filenamev([GLib.get_user_cache_dir(), "example_download_dir", None]) | ||||
| print('Using ceche directory: {0}'.format(download_dir)) | ||||
|  | ||||
| def on_download_complete(player, item, location): | ||||
|     # Media downloaded. Data from this file is still used for current playback (including seeking). | ||||
|     print('Download complete: {0} => {1}'.format(item.props.uri, location)) | ||||
|  | ||||
| def on_activate(app): | ||||
|     win = Gtk.ApplicationWindow(application=app, default_width=640, default_height=396) | ||||
|     video = ClapperGtk.Video() | ||||
|     controls = ClapperGtk.SimpleControls(fullscreenable=False) | ||||
|  | ||||
|     # Enable local storage caching and monitor it | ||||
|     video.props.player.set_download_dir(download_dir) | ||||
|     video.props.player.set_download_enabled(True) | ||||
|     video.props.player.connect('download-complete', on_download_complete) | ||||
|  | ||||
|     # Configure playback | ||||
|     video.props.player.props.queue.set_progression_mode(Clapper.QueueProgressionMode.CAROUSEL) | ||||
|     video.props.player.set_autoplay(True) | ||||
|  | ||||
|     # Create and add media for playback | ||||
|     item_1 = Clapper.MediaItem(uri='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4') | ||||
|     item_2 = Clapper.MediaItem(uri='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4') | ||||
|     video.props.player.props.queue.add_item(item_1) | ||||
|     video.props.player.props.queue.add_item(item_2) | ||||
|  | ||||
|     # Assemble window | ||||
|     video.add_fading_overlay(controls) | ||||
|     win.set_child(video) | ||||
|     win.present() | ||||
|  | ||||
| # Create a new application | ||||
| app = Adw.Application(application_id='com.example.ClapperDownloadCache') | ||||
| app.connect('activate', on_activate) | ||||
|  | ||||
| # Run the application | ||||
| app.run(None) | ||||
|  | ||||
| # Finally app should cleanup before exit. Possibly moving data to | ||||
| # another dir if it wants to use it on next run and deleting what's | ||||
| # left (so any unfinished downloads will also be removed). | ||||
| print('Cleanup') | ||||
| shutil.rmtree(download_dir) | ||||
| @@ -47,6 +47,8 @@ void clapper_app_bus_post_refresh_timeline (ClapperAppBus *app_bus, GstObject *s | ||||
|  | ||||
| void clapper_app_bus_post_simple_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id); | ||||
|  | ||||
| void clapper_app_bus_post_object_desc_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GstObject *object, const gchar *desc); | ||||
|  | ||||
| void clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, const gchar *desc, const gchar *details); | ||||
|  | ||||
| void clapper_app_bus_post_error_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GError *error, const gchar *debug_info); | ||||
|   | ||||
| @@ -43,6 +43,7 @@ enum | ||||
|   CLAPPER_APP_BUS_STRUCTURE_REFRESH_STREAMS, | ||||
|   CLAPPER_APP_BUS_STRUCTURE_REFRESH_TIMELINE, | ||||
|   CLAPPER_APP_BUS_STRUCTURE_SIMPLE_SIGNAL, | ||||
|   CLAPPER_APP_BUS_STRUCTURE_OBJECT_DESC_SIGNAL, | ||||
|   CLAPPER_APP_BUS_STRUCTURE_DESC_WITH_DETAILS_SIGNAL, | ||||
|   CLAPPER_APP_BUS_STRUCTURE_ERROR_SIGNAL | ||||
| }; | ||||
| @@ -53,6 +54,7 @@ static ClapperBusQuark _structure_quarks[] = { | ||||
|   {"refresh-streams", 0}, | ||||
|   {"refresh-timeline", 0}, | ||||
|   {"simple-signal", 0}, | ||||
|   {"object-desc-signal", 0}, | ||||
|   {"desc-with-details-signal", 0}, | ||||
|   {"error-signal", 0}, | ||||
|   {NULL, 0} | ||||
| @@ -63,6 +65,7 @@ enum | ||||
|   CLAPPER_APP_BUS_FIELD_UNKNOWN = 0, | ||||
|   CLAPPER_APP_BUS_FIELD_PSPEC, | ||||
|   CLAPPER_APP_BUS_FIELD_SIGNAL_ID, | ||||
|   CLAPPER_APP_BUS_FIELD_OBJECT, | ||||
|   CLAPPER_APP_BUS_FIELD_DESC, | ||||
|   CLAPPER_APP_BUS_FIELD_DETAILS, | ||||
|   CLAPPER_APP_BUS_FIELD_ERROR, | ||||
| @@ -73,6 +76,7 @@ static ClapperBusQuark _field_quarks[] = { | ||||
|   {"unknown", 0}, | ||||
|   {"pspec", 0}, | ||||
|   {"signal-id", 0}, | ||||
|   {"object", 0}, | ||||
|   {"desc", 0}, | ||||
|   {"details", 0}, | ||||
|   {"error", 0}, | ||||
| @@ -177,6 +181,37 @@ _handle_simple_signal_msg (GstMessage *msg, const GstStructure *structure) | ||||
|   g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, 0); | ||||
| } | ||||
|  | ||||
| void | ||||
| clapper_app_bus_post_object_desc_signal (ClapperAppBus *self, | ||||
|     GstObject *src, guint signal_id, | ||||
|     GstObject *object, const gchar *desc) | ||||
| { | ||||
|   GstStructure *structure = gst_structure_new_id (_STRUCTURE_QUARK (OBJECT_DESC_SIGNAL), | ||||
|       _FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, signal_id, | ||||
|       _FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, object, | ||||
|       _FIELD_QUARK (DESC), G_TYPE_STRING, desc, | ||||
|       NULL); | ||||
|   gst_bus_post (GST_BUS_CAST (self), gst_message_new_application (src, structure)); | ||||
| } | ||||
|  | ||||
| static inline void | ||||
| _handle_object_desc_signal_msg (GstMessage *msg, const GstStructure *structure) | ||||
| { | ||||
|   guint signal_id = 0; | ||||
|   GstObject *object = NULL; | ||||
|   gchar *desc = NULL; | ||||
|  | ||||
|   gst_structure_id_get (structure, | ||||
|       _FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, &signal_id, | ||||
|       _FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, &object, | ||||
|       _FIELD_QUARK (DESC), G_TYPE_STRING, &desc, | ||||
|       NULL); | ||||
|   g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, 0, object, desc); | ||||
|  | ||||
|   gst_object_unref (object); | ||||
|   g_free (desc); | ||||
| } | ||||
|  | ||||
| void | ||||
| clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *self, | ||||
|     GstObject *src, guint signal_id, | ||||
| @@ -253,6 +288,8 @@ clapper_app_bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G | ||||
|       _handle_refresh_timeline_msg (msg, structure); | ||||
|     else if (quark == _STRUCTURE_QUARK (SIMPLE_SIGNAL)) | ||||
|       _handle_simple_signal_msg (msg, structure); | ||||
|     else if (quark == _STRUCTURE_QUARK (OBJECT_DESC_SIGNAL)) | ||||
|       _handle_object_desc_signal_msg (msg, structure); | ||||
|     else if (quark == _STRUCTURE_QUARK (ERROR_SIGNAL)) | ||||
|       _handle_error_signal_msg (msg, structure); | ||||
|     else if (quark == _STRUCTURE_QUARK (DESC_WITH_DETAILS_SIGNAL)) | ||||
|   | ||||
| @@ -37,6 +37,12 @@ void clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, Gst | ||||
| G_GNUC_INTERNAL | ||||
| gboolean clapper_media_item_set_duration (ClapperMediaItem *item, gdouble duration, ClapperAppBus *app_bus); | ||||
|  | ||||
| G_GNUC_INTERNAL | ||||
| void clapper_media_item_set_cache_location (ClapperMediaItem *item, const gchar *location); | ||||
|  | ||||
| G_GNUC_INTERNAL | ||||
| const gchar * clapper_media_item_get_playback_uri (ClapperMediaItem *item); | ||||
|  | ||||
| G_GNUC_INTERNAL | ||||
| void clapper_media_item_set_used (ClapperMediaItem *item, gboolean used); | ||||
|  | ||||
|   | ||||
| @@ -50,6 +50,8 @@ struct _ClapperMediaItem | ||||
|   gchar *container_format; | ||||
|   gdouble duration; | ||||
|  | ||||
|   gchar *cache_uri; | ||||
|  | ||||
|   /* For shuffle */ | ||||
|   gboolean used; | ||||
| }; | ||||
| @@ -450,6 +452,41 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco | ||||
|   gst_object_unref (player); | ||||
| } | ||||
|  | ||||
| /* XXX: Must be set from player thread */ | ||||
| inline void | ||||
| clapper_media_item_set_cache_location (ClapperMediaItem *self, const gchar *location) | ||||
| { | ||||
|   g_free (self->cache_uri); | ||||
|   self->cache_uri = g_filename_to_uri (location, NULL, NULL); | ||||
|   GST_DEBUG_OBJECT (self, "Set cache URI: \"%s\"", self->cache_uri); | ||||
| } | ||||
|  | ||||
| /* XXX: Can only be read from player thread. | ||||
|  * Returns cache URI if available, item URI otherwise. */ | ||||
| inline const gchar * | ||||
| clapper_media_item_get_playback_uri (ClapperMediaItem *self) | ||||
| { | ||||
|   if (self->cache_uri) { | ||||
|     GFile *file = g_file_new_for_uri (self->cache_uri); | ||||
|     gboolean exists; | ||||
|  | ||||
|     /* It is an app error if it removes files in non-stopped state, | ||||
|      * and this function is only called when starting playback */ | ||||
|     exists = g_file_query_exists (file, NULL); | ||||
|     g_object_unref (file); | ||||
|  | ||||
|     if (exists) | ||||
|       return self->cache_uri; | ||||
|  | ||||
|     /* Do not test file existence next time */ | ||||
|     GST_DEBUG_OBJECT (self, "Cleared cache URI for non-existing file: \"%s\"", | ||||
|         self->cache_uri); | ||||
|     g_clear_pointer (&self->cache_uri, g_free); | ||||
|   } | ||||
|  | ||||
|   return self->uri; | ||||
| } | ||||
|  | ||||
| void | ||||
| clapper_media_item_set_used (ClapperMediaItem *self, gboolean used) | ||||
| { | ||||
| @@ -505,6 +542,8 @@ clapper_media_item_finalize (GObject *object) | ||||
|   gst_object_unparent (GST_OBJECT_CAST (self->timeline)); | ||||
|   gst_object_unref (self->timeline); | ||||
|  | ||||
|   g_free (self->cache_uri); | ||||
|  | ||||
|   G_OBJECT_CLASS (parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -827,6 +827,24 @@ _handle_element_msg (GstMessage *msg, ClapperPlayer *player) | ||||
|  | ||||
|     g_free (name); | ||||
|     g_free (details); | ||||
|   } else if (gst_message_has_name (msg, "GstCacheDownloadComplete")) { | ||||
|     const GstStructure *structure; | ||||
|     const gchar *location; | ||||
|     guint signal_id; | ||||
|  | ||||
|     if (G_UNLIKELY (player->played_item == NULL)) | ||||
|       return; | ||||
|  | ||||
|     structure = gst_message_get_structure (msg); | ||||
|     location = gst_structure_get_string (structure, "location"); | ||||
|     signal_id = g_signal_lookup ("download-complete", CLAPPER_TYPE_PLAYER); | ||||
|  | ||||
|     GST_INFO_OBJECT (player, "Download complete: %s", location); | ||||
|     clapper_media_item_set_cache_location (player->played_item, location); | ||||
|  | ||||
|     clapper_app_bus_post_object_desc_signal (player->app_bus, | ||||
|         GST_OBJECT_CAST (player), signal_id, | ||||
|         GST_OBJECT_CAST (player->played_item), location); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -95,6 +95,8 @@ struct _ClapperPlayer | ||||
|   gboolean video_enabled; | ||||
|   gboolean audio_enabled; | ||||
|   gboolean subtitles_enabled; | ||||
|   gchar *download_dir; | ||||
|   gboolean download_enabled; | ||||
|   gdouble audio_offset; | ||||
|   gdouble subtitle_offset; | ||||
| }; | ||||
|   | ||||
| @@ -43,7 +43,7 @@ | ||||
| #include "clapper-playbin-bus-private.h" | ||||
| #include "clapper-app-bus-private.h" | ||||
| #include "clapper-queue-private.h" | ||||
| #include "clapper-media-item.h" | ||||
| #include "clapper-media-item-private.h" | ||||
| #include "clapper-stream-list-private.h" | ||||
| #include "clapper-stream-private.h" | ||||
| #include "clapper-video-stream-private.h" | ||||
| @@ -61,6 +61,7 @@ | ||||
| #define DEFAULT_VIDEO_ENABLED TRUE | ||||
| #define DEFAULT_AUDIO_ENABLED TRUE | ||||
| #define DEFAULT_SUBTITLES_ENABLED TRUE | ||||
| #define DEFAULT_DOWNLOAD_ENABLED FALSE | ||||
|  | ||||
| #define GST_CAT_DEFAULT clapper_player_debug | ||||
| GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); | ||||
| @@ -90,6 +91,8 @@ enum | ||||
|   PROP_VIDEO_ENABLED, | ||||
|   PROP_AUDIO_ENABLED, | ||||
|   PROP_SUBTITLES_ENABLED, | ||||
|   PROP_DOWNLOAD_DIR, | ||||
|   PROP_DOWNLOAD_ENABLED, | ||||
|   PROP_AUDIO_OFFSET, | ||||
|   PROP_SUBTITLE_OFFSET, | ||||
|   PROP_SUBTITLE_FONT_DESC, | ||||
| @@ -99,6 +102,7 @@ enum | ||||
| enum | ||||
| { | ||||
|   SIGNAL_SEEK_DONE, | ||||
|   SIGNAL_DOWNLOAD_COMPLETE, | ||||
|   SIGNAL_MISSING_PLUGIN, | ||||
|   SIGNAL_WARNING, | ||||
|   SIGNAL_ERROR, | ||||
| @@ -278,14 +282,15 @@ void | ||||
| clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue *value) | ||||
| { | ||||
|   gint flags; | ||||
|   gboolean video_enabled, audio_enabled, subtitles_enabled; | ||||
|   gboolean video_changed, audio_changed, subtitles_changed; | ||||
|   gboolean video_enabled, audio_enabled, subtitles_enabled, download_enabled; | ||||
|   gboolean video_changed, audio_changed, subtitles_changed, download_changed; | ||||
|  | ||||
|   flags = g_value_get_flags (value); | ||||
|  | ||||
|   video_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_VIDEO) == CLAPPER_PLAYER_PLAY_FLAG_VIDEO); | ||||
|   audio_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_AUDIO) == CLAPPER_PLAYER_PLAY_FLAG_AUDIO); | ||||
|   subtitles_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_TEXT) == CLAPPER_PLAYER_PLAY_FLAG_TEXT); | ||||
|   download_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD) == CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD); | ||||
|  | ||||
|   GST_OBJECT_LOCK (self); | ||||
|  | ||||
| @@ -295,6 +300,8 @@ clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue * | ||||
|     self->audio_enabled = audio_enabled; | ||||
|   if ((subtitles_changed = self->subtitles_enabled != subtitles_enabled)) | ||||
|     self->subtitles_enabled = subtitles_enabled; | ||||
|   if ((download_changed = self->download_enabled != download_enabled)) | ||||
|     self->download_enabled = download_enabled; | ||||
|  | ||||
|   GST_OBJECT_UNLOCK (self); | ||||
|  | ||||
| @@ -313,6 +320,11 @@ clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue * | ||||
|     clapper_app_bus_post_prop_notify (self->app_bus, | ||||
|         GST_OBJECT_CAST (self), param_specs[PROP_SUBTITLES_ENABLED]); | ||||
|   } | ||||
|   if (download_changed) { | ||||
|     GST_INFO_OBJECT (self, "Download enabled: %s", (download_enabled) ? "yes" : "no"); | ||||
|     clapper_app_bus_post_prop_notify (self->app_bus, | ||||
|         GST_OBJECT_CAST (self), param_specs[PROP_DOWNLOAD_ENABLED]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -438,7 +450,7 @@ clapper_player_set_pending_item (ClapperPlayer *self, ClapperMediaItem *pending_ | ||||
|  | ||||
|   /* Might be NULL (e.g. after queue is cleared) */ | ||||
|   if (pending_item) { | ||||
|     uri = clapper_media_item_get_uri (pending_item); | ||||
|     uri = clapper_media_item_get_playback_uri (pending_item); | ||||
|     suburi = clapper_media_item_get_suburi (pending_item); | ||||
|   } | ||||
|  | ||||
| @@ -725,6 +737,50 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose) | ||||
|   } | ||||
| } | ||||
|  | ||||
| static inline gchar * | ||||
| _make_download_template (ClapperPlayer *self) | ||||
| { | ||||
|   gchar *download_template = NULL; | ||||
|  | ||||
|   GST_OBJECT_LOCK (self); | ||||
|  | ||||
|   if (self->download_enabled && self->download_dir) { | ||||
|     if (g_mkdir_with_parents (self->download_dir, 0755) == 0) { | ||||
|       download_template = g_build_filename (self->download_dir, "XXXXXX", NULL); | ||||
|     } else { | ||||
|       GST_ERROR_OBJECT (self, "Could not create download dir: \"%s\"", self->download_dir); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   GST_OBJECT_UNLOCK (self); | ||||
|  | ||||
|   return download_template; | ||||
| } | ||||
|  | ||||
| static void | ||||
| _element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self) | ||||
| { | ||||
|   GstElementFactory *factory = gst_element_get_factory (element); | ||||
|  | ||||
|   if (G_UNLIKELY (factory == NULL)) | ||||
|     return; | ||||
|  | ||||
|   GST_INFO_OBJECT (self, "Element setup: %s", GST_OBJECT_NAME (factory)); | ||||
|  | ||||
|   if (strcmp (GST_OBJECT_NAME (factory), "downloadbuffer") == 0) { | ||||
|     gchar *download_template; | ||||
|  | ||||
|     /* Only set props if we have download template */ | ||||
|     if ((download_template = _make_download_template (self))) { | ||||
|       g_object_set (element, | ||||
|           "temp-template", download_template, | ||||
|           "temp-remove", FALSE, | ||||
|           NULL); | ||||
|       g_free (download_template); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void | ||||
| _about_to_finish_cb (GstElement *playbin, ClapperPlayer *self) | ||||
| { | ||||
| @@ -1505,6 +1561,106 @@ clapper_player_get_subtitles_enabled (ClapperPlayer *self) | ||||
|   return enabled; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * clapper_player_set_download_dir: | ||||
|  * @player: a #ClapperPlayer | ||||
|  * @path: (type filename): the path of a directory to use for media downloads | ||||
|  * | ||||
|  * Set a directory that @player will use to store downloads. | ||||
|  * | ||||
|  * See [property@Clapper.Player:download-enabled] description for more | ||||
|  * info how this works. | ||||
|  * | ||||
|  * Since: 0.8 | ||||
|  */ | ||||
| void | ||||
| clapper_player_set_download_dir (ClapperPlayer *self, const gchar *path) | ||||
| { | ||||
|   gboolean changed; | ||||
|  | ||||
|   g_return_if_fail (CLAPPER_IS_PLAYER (self)); | ||||
|   g_return_if_fail (path != NULL); | ||||
|  | ||||
|   GST_OBJECT_LOCK (self); | ||||
|   changed = g_set_str (&self->download_dir, path); | ||||
|   GST_OBJECT_UNLOCK (self); | ||||
|  | ||||
|   if (changed) { | ||||
|     GST_INFO_OBJECT (self, "Current download dir: %s", path); | ||||
|     clapper_app_bus_post_prop_notify (self->app_bus, | ||||
|         GST_OBJECT_CAST (self), param_specs[PROP_DOWNLOAD_DIR]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * clapper_player_get_download_dir: | ||||
|  * @player: a #ClapperPlayer | ||||
|  * | ||||
|  * Get path to a directory set for media downloads. | ||||
|  * | ||||
|  * Returns: (type filename) (transfer full) (nullable): the path of a directory | ||||
|  *   set for media downloads or %NULL if no directory was set yet. | ||||
|  * | ||||
|  * Since: 0.8 | ||||
|  */ | ||||
| gchar * | ||||
| clapper_player_get_download_dir (ClapperPlayer *self) | ||||
| { | ||||
|   gchar *download_dir; | ||||
|  | ||||
|   g_return_val_if_fail (CLAPPER_IS_PLAYER (self), NULL); | ||||
|  | ||||
|   GST_OBJECT_LOCK (self); | ||||
|   download_dir = g_strdup (self->download_dir); | ||||
|   GST_OBJECT_UNLOCK (self); | ||||
|  | ||||
|   return download_dir; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * clapper_player_set_download_enabled: | ||||
|  * @player: a #ClapperPlayer | ||||
|  * @enabled: whether enabled | ||||
|  * | ||||
|  * Set whether player should attempt progressive download buffering. | ||||
|  * | ||||
|  * For this to actually work a [property@Clapper.Player:download-dir] | ||||
|  * must also be set. | ||||
|  * | ||||
|  * Since: 0.8 | ||||
|  */ | ||||
| void | ||||
| clapper_player_set_download_enabled (ClapperPlayer *self, gboolean enabled) | ||||
| { | ||||
|   g_return_if_fail (CLAPPER_IS_PLAYER (self)); | ||||
|  | ||||
|   clapper_playbin_bus_post_set_play_flag (self->bus, CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD, enabled); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * clapper_player_get_download_enabled: | ||||
|  * @player: a #ClapperPlayer | ||||
|  * | ||||
|  * Get whether progressive download buffering is enabled. | ||||
|  * | ||||
|  * Returns: %TRUE if enabled, %FALSE otherwise. | ||||
|  * | ||||
|  * Since: 0.8 | ||||
|  */ | ||||
| gboolean | ||||
| clapper_player_get_download_enabled (ClapperPlayer *self) | ||||
| { | ||||
|   gboolean enabled; | ||||
|  | ||||
|   g_return_val_if_fail (CLAPPER_IS_PLAYER (self), FALSE); | ||||
|  | ||||
|   GST_OBJECT_LOCK (self); | ||||
|   enabled = self->download_enabled; | ||||
|   GST_OBJECT_UNLOCK (self); | ||||
|  | ||||
|   return enabled; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * clapper_player_set_audio_offset: | ||||
|  * @player: a #ClapperPlayer | ||||
| @@ -1793,6 +1949,7 @@ clapper_player_thread_start (ClapperThreadedObject *threaded_object) | ||||
|   for (i = 0; playbin_watchlist[i]; ++i) | ||||
|     gst_element_add_property_notify_watch (self->playbin, playbin_watchlist[i], TRUE); | ||||
|  | ||||
|   g_signal_connect (self->playbin, "element-setup", G_CALLBACK (_element_setup_cb), self); | ||||
|   g_signal_connect (self->playbin, "about-to-finish", G_CALLBACK (_about_to_finish_cb), self); | ||||
|  | ||||
|   if (!self->use_playbin3) { | ||||
| @@ -1866,6 +2023,7 @@ clapper_player_init (ClapperPlayer *self) | ||||
|   self->video_enabled = DEFAULT_VIDEO_ENABLED; | ||||
|   self->audio_enabled = DEFAULT_AUDIO_ENABLED; | ||||
|   self->subtitles_enabled = DEFAULT_SUBTITLES_ENABLED; | ||||
|   self->download_enabled = DEFAULT_DOWNLOAD_ENABLED; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1924,6 +2082,8 @@ clapper_player_finalize (GObject *object) | ||||
|   gst_clear_object (&self->pending_item); | ||||
|   gst_clear_object (&self->played_item); | ||||
|  | ||||
|   g_free (self->download_dir); | ||||
|  | ||||
|   G_OBJECT_CLASS (parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| @@ -1991,6 +2151,12 @@ clapper_player_get_property (GObject *object, guint prop_id, | ||||
|     case PROP_SUBTITLES_ENABLED: | ||||
|       g_value_set_boolean (value, clapper_player_get_subtitles_enabled (self)); | ||||
|       break; | ||||
|     case PROP_DOWNLOAD_DIR: | ||||
|       g_value_take_string (value, clapper_player_get_download_dir (self)); | ||||
|       break; | ||||
|     case PROP_DOWNLOAD_ENABLED: | ||||
|       g_value_set_boolean (value, clapper_player_get_download_enabled (self)); | ||||
|       break; | ||||
|     case PROP_AUDIO_OFFSET: | ||||
|       g_value_set_double (value, clapper_player_get_audio_offset (self)); | ||||
|       break; | ||||
| @@ -2046,6 +2212,12 @@ clapper_player_set_property (GObject *object, guint prop_id, | ||||
|     case PROP_SUBTITLES_ENABLED: | ||||
|       clapper_player_set_subtitles_enabled (self, g_value_get_boolean (value)); | ||||
|       break; | ||||
|     case PROP_DOWNLOAD_DIR: | ||||
|       clapper_player_set_download_dir (self, g_value_get_string (value)); | ||||
|       break; | ||||
|     case PROP_DOWNLOAD_ENABLED: | ||||
|       clapper_player_set_download_enabled (self, g_value_get_boolean (value)); | ||||
|       break; | ||||
|     case PROP_AUDIO_OFFSET: | ||||
|       clapper_player_set_audio_offset (self, g_value_get_double (value)); | ||||
|       break; | ||||
| @@ -2251,6 +2423,52 @@ clapper_player_class_init (ClapperPlayerClass *klass) | ||||
|       NULL, NULL, DEFAULT_SUBTITLES_ENABLED, | ||||
|       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   /** | ||||
|    * ClapperPlayer:download-dir: | ||||
|    * | ||||
|    * A directory that @player will use to download network content | ||||
|    * when [property@Clapper.Player:download-enabled] is set to %TRUE. | ||||
|    * | ||||
|    * If directory at @path does not exist, it will be automatically created. | ||||
|    * | ||||
|    * Since: 0.8 | ||||
|    */ | ||||
|   param_specs[PROP_DOWNLOAD_DIR] = g_param_spec_string ("download-dir", | ||||
|       NULL, NULL, NULL, | ||||
|       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   /** | ||||
|    * ClapperPlayer:download-enabled: | ||||
|    * | ||||
|    * Whether progressive download buffering is enabled. | ||||
|    * | ||||
|    * If progressive download is enabled and [property@Clapper.Player:download-dir] | ||||
|    * is set, streamed network content will be cached to the disk space instead | ||||
|    * of memory whenever possible. This allows for faster seeking through | ||||
|    * currently played media. | ||||
|    * | ||||
|    * Not every type of content is download applicable. Mainly applies to | ||||
|    * web content that does not use adaptive streaming. | ||||
|    * | ||||
|    * Once data that media item URI points to is fully downloaded, player | ||||
|    * will emit [signal@Clapper.Player::download-complete] signal with a | ||||
|    * location of downloaded file. | ||||
|    * | ||||
|    * Playing again the exact same [class@Clapper.MediaItem] object that was | ||||
|    * previously fully downloaded will cause player to automatically use that | ||||
|    * cached file if it still exists, avoiding any further network requests. | ||||
|    * | ||||
|    * Please note that player will not delete nor manage downloaded content. | ||||
|    * It is up to application to cleanup data in created cache directory | ||||
|    * (e.g. before app exits), in order to remove any downloads that app | ||||
|    * is not going to use next time it is run and incomplete ones. | ||||
|    * | ||||
|    * Since: 0.8 | ||||
|    */ | ||||
|   param_specs[PROP_DOWNLOAD_ENABLED] = g_param_spec_boolean ("download-enabled", | ||||
|       NULL, NULL, DEFAULT_DOWNLOAD_ENABLED, | ||||
|       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); | ||||
|  | ||||
|   /** | ||||
|    * ClapperPlayer:audio-offset: | ||||
|    * | ||||
| @@ -2288,6 +2506,22 @@ clapper_player_class_init (ClapperPlayerClass *klass) | ||||
|       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, | ||||
|       0, NULL, NULL, NULL, G_TYPE_NONE, 0); | ||||
|  | ||||
|   /** | ||||
|    * ClapperPlayer::download-complete: | ||||
|    * @player: a #ClapperPlayer | ||||
|    * @item: a #ClapperMediaItem | ||||
|    * @location: (type filename): a path to downloaded file | ||||
|    * | ||||
|    * Media was fully downloaded to local cache directory. This signal will | ||||
|    * be only emitted when progressive download buffering is enabled by | ||||
|    * setting [property@Clapper.Player:download-enabled] property to %TRUE. | ||||
|    * | ||||
|    * Since: 0.8 | ||||
|    */ | ||||
|   signals[SIGNAL_DOWNLOAD_COMPLETE] = g_signal_new ("download-complete", | ||||
|       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, | ||||
|       0, NULL, NULL, NULL, G_TYPE_NONE, 2, CLAPPER_TYPE_MEDIA_ITEM, G_TYPE_STRING); | ||||
|  | ||||
|   /** | ||||
|    * ClapperPlayer::missing-plugin: | ||||
|    * @player: a #ClapperPlayer | ||||
|   | ||||
| @@ -101,6 +101,14 @@ void clapper_player_set_subtitles_enabled (ClapperPlayer *player, gboolean enabl | ||||
|  | ||||
| gboolean clapper_player_get_subtitles_enabled (ClapperPlayer *player); | ||||
|  | ||||
| void clapper_player_set_download_dir (ClapperPlayer *player, const gchar *path); | ||||
|  | ||||
| gchar * clapper_player_get_download_dir (ClapperPlayer *player); | ||||
|  | ||||
| void clapper_player_set_download_enabled (ClapperPlayer *player, gboolean enabled); | ||||
|  | ||||
| gboolean clapper_player_get_download_enabled (ClapperPlayer *player); | ||||
|  | ||||
| void clapper_player_set_audio_offset (ClapperPlayer *player, gdouble offset); | ||||
|  | ||||
| gdouble clapper_player_get_audio_offset (ClapperPlayer *player); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user