mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 15:22:11 +02: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