mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 23:32:04 +02:00
clapper: Add media caching via download to local storage
The aim here is to stream an online video/audio while also at the same time download/cache it to disk (excluding adaptive content). After download is complete, further playback and seeking are done using the locally cached file. This functionality uses GStreamer "downloadbuffer" element. Player will emit a signal with a local download location after it completes, so application will know where downloaded file for media item is stored in case it wants to reuse it in the future. It is up to application to set download dir and later manage downloaded content in it, removing files its not going to use on next application run and any incomplete downloads.
This commit is contained in:
@@ -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