+++ /dev/null
-/*
- * This file is a part of MAFW
- *
- * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
- *
- * Contact: Visa Smolander <visa.smolander@nokia.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <glib.h>
-#include <X11/Xlib.h>
-#include <gst/interfaces/xoverlay.h>
-#include <gst/pbutils/missing-plugins.h>
-#include <gst/base/gstbasesink.h>
-#include <libmafw/mafw.h>
-
-#ifdef HAVE_GDKPIXBUF
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <glib/gstdio.h>
-#include <unistd.h>
-#include "gstscreenshot.h"
-#endif
-
-#include <totem-pl-parser.h>
-#include "mafw-gst-renderer.h"
-#include "mafw-gst-renderer-worker.h"
-#include "mafw-gst-renderer-utils.h"
-#include "blanking.h"
-#include "keypad.h"
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mafw-gst-renderer-worker"
-
-#define MAFW_GST_RENDERER_WORKER_SECONDS_READY 60
-#define MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY 4
-
-#define MAFW_GST_MISSING_TYPE_DECODER "decoder"
-#define MAFW_GST_MISSING_TYPE_ENCODER "encoder"
-
-#define MAFW_GST_BUFFER_TIME 600000L
-#define MAFW_GST_LATENCY_TIME (MAFW_GST_BUFFER_TIME / 2)
-
-#define NSECONDS_TO_SECONDS(ns) ((ns)%1000000000 < 500000000?\
- GST_TIME_AS_SECONDS((ns)):\
- GST_TIME_AS_SECONDS((ns))+1)
-
-#define _current_metadata_add(worker, key, type, value) \
- do { \
- if (!worker->current_metadata) \
- worker->current_metadata = mafw_metadata_new(); \
- /* At first remove old value */ \
- g_hash_table_remove(worker->current_metadata, key); \
- mafw_metadata_add_something(worker->current_metadata, \
- key, type, 1, value); \
- } while (0)
-
-/* Private variables. */
-/* Global reference to worker instance, needed for Xerror handler */
-static MafwGstRendererWorker *Global_worker = NULL;
-
-/* Forward declarations. */
-static void _do_play(MafwGstRendererWorker *worker);
-static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type,
- gint position, GError **error);
-static void _play_pl_next(MafwGstRendererWorker *worker);
-
-static void _emit_metadatas(MafwGstRendererWorker *worker);
-
-/* Playlist parsing */
-static void _on_pl_entry_parsed(TotemPlParser *parser, gchar *uri,
- gpointer metadata, GSList **plitems)
-{
- if (uri != NULL) {
- *plitems = g_slist_append(*plitems, g_strdup(uri));
- }
-}
-static GSList *_parse_playlist(const gchar *uri)
-{
- static TotemPlParser *pl_parser = NULL;
- GSList *plitems = NULL;
- gulong handler_id;
-
- /* Initialize the playlist parser */
- if (!pl_parser)
- {
- pl_parser = totem_pl_parser_new ();
- g_object_set(pl_parser, "recurse", TRUE, "disable-unsafe",
- TRUE, NULL);
- }
- handler_id = g_signal_connect(G_OBJECT(pl_parser), "entry-parsed",
- G_CALLBACK(_on_pl_entry_parsed), &plitems);
- /* Parsing */
- if (totem_pl_parser_parse(pl_parser, uri, FALSE) !=
- TOTEM_PL_PARSER_RESULT_SUCCESS) {
- /* An error happens while parsing */
-
- }
- g_signal_handler_disconnect(pl_parser, handler_id);
- return plitems;
-}
-
-/*
- * Sends @error to MafwGstRenderer. Only call this from the glib main thread, or
- * face the consequences. @err is free'd.
- */
-static void _send_error(MafwGstRendererWorker *worker, GError *err)
-{
- worker->is_error = TRUE;
- if (worker->notify_error_handler)
- worker->notify_error_handler(worker, worker->owner, err);
- g_error_free(err);
-}
-
-/*
- * Posts an @error on the gst bus. _async_bus_handler will then pick it up and
- * forward to MafwGstRenderer. @err is free'd.
- */
-static void _post_error(MafwGstRendererWorker *worker, GError *err)
-{
- gst_bus_post(worker->bus,
- gst_message_new_error(GST_OBJECT(worker->pipeline),
- err, NULL));
- g_error_free(err);
-}
-
-#ifdef HAVE_GDKPIXBUF
-typedef struct {
- MafwGstRendererWorker *worker;
- gchar *metadata_key;
- GdkPixbuf *pixbuf;
-} SaveGraphicData;
-
-static gchar *_init_tmp_file(void)
-{
- gint fd;
- gchar *path = NULL;
-
- fd = g_file_open_tmp("mafw-gst-renderer-XXXXXX.jpeg", &path, NULL);
- close(fd);
-
- return path;
-}
-
-static void _init_tmp_files_pool(MafwGstRendererWorker *worker)
-{
- guint8 i;
-
- worker->tmp_files_pool_index = 0;
-
- for (i = 0; i < MAFW_GST_RENDERER_MAX_TMP_FILES; i++) {
- worker->tmp_files_pool[i] = NULL;
- }
-}
-
-static void _destroy_tmp_files_pool(MafwGstRendererWorker *worker)
-{
- guint8 i;
-
- for (i = 0; (i < MAFW_GST_RENDERER_MAX_TMP_FILES) &&
- (worker->tmp_files_pool[i] != NULL); i++) {
- g_unlink(worker->tmp_files_pool[i]);
- g_free(worker->tmp_files_pool[i]);
- }
-}
-
-static const gchar *_get_tmp_file_from_pool(
- MafwGstRendererWorker *worker)
-{
- gchar *path = worker->tmp_files_pool[worker->tmp_files_pool_index];
-
- if (path == NULL) {
- path = _init_tmp_file();
- worker->tmp_files_pool[worker->tmp_files_pool_index] = path;
- }
-
- if (++(worker->tmp_files_pool_index) >=
- MAFW_GST_RENDERER_MAX_TMP_FILES) {
- worker->tmp_files_pool_index = 0;
- }
-
- return path;
-}
-
-static void _destroy_pixbuf (guchar *pixbuf, gpointer data)
-{
- gst_buffer_unref(GST_BUFFER(data));
-}
-
-static void _emit_gst_buffer_as_graphic_file_cb(GstBuffer *new_buffer,
- gpointer user_data)
-{
- SaveGraphicData *sgd = user_data;
- GdkPixbuf *pixbuf = NULL;
-
- if (new_buffer != NULL) {
- gint width, height;
- GstStructure *structure;
-
- structure =
- gst_caps_get_structure(GST_BUFFER_CAPS(new_buffer), 0);
-
- gst_structure_get_int(structure, "width", &width);
- gst_structure_get_int(structure, "height", &height);
-
- pixbuf = gdk_pixbuf_new_from_data(
- GST_BUFFER_DATA(new_buffer), GDK_COLORSPACE_RGB,
- FALSE, 8, width, height,
- GST_ROUND_UP_4(3 * width), _destroy_pixbuf,
- new_buffer);
-
- if (sgd->pixbuf != NULL) {
- g_object_unref(sgd->pixbuf);
- sgd->pixbuf = NULL;
- }
- } else {
- pixbuf = sgd->pixbuf;
- }
-
- if (pixbuf != NULL) {
- gboolean save_ok;
- GError *error = NULL;
- const gchar *filename;
-
- filename = _get_tmp_file_from_pool(sgd->worker);
-
- save_ok = gdk_pixbuf_save (pixbuf, filename, "jpeg", &error,
- NULL);
-
- g_object_unref (pixbuf);
-
- if (save_ok) {
- /* Add the info to the current metadata. */
- _current_metadata_add(sgd->worker, sgd->metadata_key,
- G_TYPE_STRING,
- (gchar*)filename);
-
- /* Emit the metadata. */
- mafw_renderer_emit_metadata_string(sgd->worker->owner,
- sgd->metadata_key,
- (gchar *) filename);
- } else {
- if (error != NULL) {
- g_warning ("%s\n", error->message);
- g_error_free (error);
- } else {
- g_critical("Unknown error when saving pixbuf "
- "with GStreamer data");
- }
- }
- } else {
- g_warning("Could not create pixbuf from GstBuffer");
- }
-
- g_free(sgd->metadata_key);
- g_free(sgd);
-}
-
-static void _pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
- gint width, gint height,
- gpointer user_data)
-{
- /* Be sure the image size is reasonable */
- if (width > 512 || height > 512) {
- g_debug ("pixbuf: image is too big: %dx%d", width, height);
- gdouble ar;
- ar = (gdouble) width / height;
- if (width > height) {
- width = 512;
- height = width / ar;
- } else {
- height = 512;
- width = height * ar;
- }
- g_debug ("pixbuf: scaled image to %dx%d", width, height);
- gdk_pixbuf_loader_set_size (loader, width, height);
- }
-}
-
-static void _emit_gst_buffer_as_graphic_file(MafwGstRendererWorker *worker,
- GstBuffer *buffer,
- const gchar *metadata_key)
-{
- GdkPixbufLoader *loader;
- GstStructure *structure;
- const gchar *mime = NULL;
- GError *error = NULL;
-
- g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
-
- structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0);
- mime = gst_structure_get_name(structure);
-
- if (g_str_has_prefix(mime, "video/x-raw")) {
- gint framerate_d, framerate_n;
- GstCaps *to_caps;
- SaveGraphicData *sgd;
-
- gst_structure_get_fraction (structure, "framerate",
- &framerate_n, &framerate_d);
-
- to_caps = gst_caps_new_simple ("video/x-raw-rgb",
- "bpp", G_TYPE_INT, 24,
- "depth", G_TYPE_INT, 24,
- "framerate", GST_TYPE_FRACTION,
- framerate_n, framerate_d,
- "pixel-aspect-ratio",
- GST_TYPE_FRACTION, 1, 1,
- "endianness",
- G_TYPE_INT, G_BIG_ENDIAN,
- "red_mask", G_TYPE_INT,
- 0xff0000,
- "green_mask",
- G_TYPE_INT, 0x00ff00,
- "blue_mask",
- G_TYPE_INT, 0x0000ff,
- NULL);
-
- sgd = g_new0(SaveGraphicData, 1);
- sgd->worker = worker;
- sgd->metadata_key = g_strdup(metadata_key);
-
- g_debug("pixbuf: using bvw to convert image format");
- bvw_frame_conv_convert (buffer, to_caps,
- _emit_gst_buffer_as_graphic_file_cb,
- sgd);
- } else {
- GdkPixbuf *pixbuf = NULL;
- loader = gdk_pixbuf_loader_new_with_mime_type(mime, &error);
- g_signal_connect (G_OBJECT (loader), "size-prepared",
- (GCallback)_pixbuf_size_prepared_cb, NULL);
-
- if (loader == NULL) {
- g_warning ("%s\n", error->message);
- g_error_free (error);
- } else {
- if (!gdk_pixbuf_loader_write (loader,
- GST_BUFFER_DATA(buffer),
- GST_BUFFER_SIZE(buffer),
- &error)) {
- g_warning ("%s\n", error->message);
- g_error_free (error);
-
- gdk_pixbuf_loader_close (loader, NULL);
- } else {
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-
- if (!gdk_pixbuf_loader_close (loader, &error)) {
- g_warning ("%s\n", error->message);
- g_error_free (error);
-
- g_object_unref(pixbuf);
- } else {
- SaveGraphicData *sgd;
-
- sgd = g_new0(SaveGraphicData, 1);
-
- sgd->worker = worker;
- sgd->metadata_key =
- g_strdup(metadata_key);
- sgd->pixbuf = pixbuf;
-
- _emit_gst_buffer_as_graphic_file_cb(
- NULL, sgd);
- }
- }
- g_object_unref(loader);
- }
- }
-}
-#endif
-
-static gboolean _go_to_gst_ready(gpointer user_data)
-{
- MafwGstRendererWorker *worker = user_data;
-
- g_return_val_if_fail(worker->state == GST_STATE_PAUSED ||
- worker->prerolling, FALSE);
-
- worker->seek_position =
- mafw_gst_renderer_worker_get_position(worker);
-
- g_debug("going to GST_STATE_READY");
- gst_element_set_state(worker->pipeline, GST_STATE_READY);
- worker->in_ready = TRUE;
- worker->ready_timeout = 0;
-
- return FALSE;
-}
-
-static void _add_ready_timeout(MafwGstRendererWorker *worker)
-{
- if (worker->media.seekable) {
- if (!worker->ready_timeout)
- {
- g_debug("Adding timeout to go to GST_STATE_READY");
- worker->ready_timeout =
- g_timeout_add_seconds(
- MAFW_GST_RENDERER_WORKER_SECONDS_READY,
- _go_to_gst_ready,
- worker);
- }
- } else {
- g_debug("Not adding timeout to go to GST_STATE_READY as media "
- "is not seekable");
- worker->ready_timeout = 0;
- }
-}
-
-static void _remove_ready_timeout(MafwGstRendererWorker *worker)
-{
- if (worker->ready_timeout != 0) {
- g_debug("removing timeout for READY");
- g_source_remove(worker->ready_timeout);
- worker->ready_timeout = 0;
- }
- worker->in_ready = FALSE;
-}
-
-static gboolean _emit_video_info(MafwGstRendererWorker *worker)
-{
- mafw_renderer_emit_metadata_int(worker->owner,
- MAFW_METADATA_KEY_RES_X,
- worker->media.video_width);
- mafw_renderer_emit_metadata_int(worker->owner,
- MAFW_METADATA_KEY_RES_Y,
- worker->media.video_height);
- mafw_renderer_emit_metadata_double(worker->owner,
- MAFW_METADATA_KEY_VIDEO_FRAMERATE,
- worker->media.fps);
- return FALSE;
-}
-
-/*
- * Checks if the video details are supported. It also extracts other useful
- * information (such as PAR and framerate) from the caps, if available. NOTE:
- * this will be called from a different thread than glib's mainloop (when
- * invoked via _stream_info_cb); don't call MafwGstRenderer directly.
- *
- * Returns: TRUE if video details are acceptable.
- */
-static gboolean _handle_video_info(MafwGstRendererWorker *worker,
- const GstStructure *structure)
-{
- gint width, height;
- gdouble fps;
-
- width = height = 0;
- gst_structure_get_int(structure, "width", &width);
- gst_structure_get_int(structure, "height", &height);
- g_debug("video size: %d x %d", width, height);
- if (gst_structure_has_field(structure, "pixel-aspect-ratio"))
- {
- gst_structure_get_fraction(structure, "pixel-aspect-ratio",
- &worker->media.par_n,
- &worker->media.par_d);
- g_debug("video PAR: %d:%d", worker->media.par_n,
- worker->media.par_d);
- width = width * worker->media.par_n / worker->media.par_d;
- }
-
- fps = 1.0;
- if (gst_structure_has_field(structure, "framerate"))
- {
- gint fps_n, fps_d;
-
- gst_structure_get_fraction(structure, "framerate",
- &fps_n, &fps_d);
- if (fps_d > 0)
- fps = (gdouble)fps_n / (gdouble)fps_d;
- g_debug("video fps: %f", fps);
- }
-
- worker->media.video_width = width;
- worker->media.video_height = height;
- worker->media.fps = fps;
-
- /* Add the info to the current metadata. */
- gint p_width, p_height, p_fps;
-
- p_width = width;
- p_height = height;
- p_fps = fps;
-
- _current_metadata_add(worker, MAFW_METADATA_KEY_RES_X, G_TYPE_INT,
- p_width);
- _current_metadata_add(worker, MAFW_METADATA_KEY_RES_Y, G_TYPE_INT,
- p_height);
- _current_metadata_add(worker, MAFW_METADATA_KEY_VIDEO_FRAMERATE,
- G_TYPE_DOUBLE,
- p_fps);
-
- /* Emit the metadata.*/
- g_idle_add((GSourceFunc)_emit_video_info, worker);
-
- return TRUE;
-}
-
-static void _parse_stream_info_item(MafwGstRendererWorker *worker, GObject *obj)
-{
- GParamSpec *pspec;
- GEnumValue *val;
- gint type;
-
- g_object_get(obj, "type", &type, NULL);
- pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), "type");
- val = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class, type);
- if (!val)
- return;
- if (!g_ascii_strcasecmp(val->value_nick, "video") ||
- !g_ascii_strcasecmp(val->value_name, "video"))
- {
- GstCaps *vcaps;
- GstObject *object;
-
- object = NULL;
- g_object_get(obj, "object", &object, NULL);
- vcaps = NULL;
- if (object) {
- vcaps = gst_pad_get_caps(GST_PAD_CAST(object));
- } else {
- g_object_get(obj, "caps", &vcaps, NULL);
- gst_caps_ref(vcaps);
- }
- if (vcaps) {
- if (gst_caps_is_fixed(vcaps))
- {
- _handle_video_info(
- worker,
- gst_caps_get_structure(vcaps, 0));
- }
- gst_caps_unref(vcaps);
- }
- }
-}
-
-/* It always returns FALSE, because it is used as an idle callback as well. */
-static gboolean _parse_stream_info(MafwGstRendererWorker *worker)
-{
- GList *stream_info, *s;
-
- stream_info = NULL;
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(worker->pipeline),
- "stream-info"))
- {
- g_object_get(worker->pipeline,
- "stream-info", &stream_info, NULL);
- }
- for (s = stream_info; s; s = g_list_next(s))
- _parse_stream_info_item(worker, G_OBJECT(s->data));
- return FALSE;
-}
-
-static void mafw_gst_renderer_worker_apply_xid(MafwGstRendererWorker *worker)
-{
- /* Set sink to render on the provided XID if we have do have
- a XID a valid video sink and we are rendeing video content */
- if (worker->xid &&
- worker->vsink &&
- worker->media.has_visual_content)
- {
- g_debug ("Setting overlay, window id: %x",
- (gint) worker->xid);
- gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(worker->vsink),
- worker->xid);
- /* Ask the gst to redraw the frame if we are paused */
- /* TODO: in MTG this works only in non-fs -> fs way. */
- if (worker->state == GST_STATE_PAUSED)
- {
- gst_x_overlay_expose(GST_X_OVERLAY(worker->vsink));
- }
- } else {
- g_debug("Not setting overlay for window id: %x",
- (gint) worker->xid);
- }
-}
-
-/*
- * GstBus synchronous message handler. NOTE that this handler is NOT invoked
- * from the glib thread, so be careful what you do here.
- */
-static GstBusSyncReply _sync_bus_handler(GstBus *bus, GstMessage *msg,
- MafwGstRendererWorker *worker)
-{
- if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
- gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
- {
- g_debug("got prepare-xwindow-id");
- worker->media.has_visual_content = TRUE;
- /* The user has to preset the XID, we don't create windows by
- * ourselves. */
- if (!worker->xid) {
- /* We must post an error message to the bus that will
- * be picked up by _async_bus_handler. Calling the
- * notification function directly from here (different
- * thread) is not healthy. */
- g_warning("No video window set!");
- _post_error(worker,
- g_error_new_literal(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_PLAYBACK,
- "No video window XID set"));
- gst_message_unref (msg);
- return GST_BUS_DROP;
- } else {
- g_debug ("Video window to use is: %x",
- (gint) worker->xid);
- }
-
- /* Instruct vsink to use the client-provided window */
- mafw_gst_renderer_worker_apply_xid(worker);
-
- /* Handle colorkey and autopaint */
- mafw_gst_renderer_worker_set_autopaint(
- worker,
- worker->autopaint);
- if (worker->colorkey == -1)
- g_object_get(worker->vsink,
- "colorkey", &worker->colorkey, NULL);
- else
- mafw_gst_renderer_worker_set_colorkey(
- worker,
- worker->colorkey);
- /* Defer the signal emission to the thread running the
- * mainloop. */
- if (worker->colorkey != -1) {
- gst_bus_post(worker->bus,
- gst_message_new_application(
- GST_OBJECT(worker->vsink),
- gst_structure_empty_new("ckey")));
- }
- gst_message_unref (msg);
- return GST_BUS_DROP;
- }
- /* do not unref message when returning PASS */
- return GST_BUS_PASS;
-}
-
-static void _free_taglist_item(GstMessage *msg, gpointer data)
-{
- gst_message_unref(msg);
-}
-
-static void _free_taglist(MafwGstRendererWorker *worker)
-{
- if (worker->tag_list != NULL)
- {
- g_ptr_array_foreach(worker->tag_list, (GFunc)_free_taglist_item,
- NULL);
- g_ptr_array_free(worker->tag_list, TRUE);
- worker->tag_list = NULL;
- }
-}
-
-static gboolean _seconds_duration_equal(gint64 duration1, gint64 duration2)
-{
- gint64 duration1_seconds, duration2_seconds;
-
- duration1_seconds = NSECONDS_TO_SECONDS(duration1);
- duration2_seconds = NSECONDS_TO_SECONDS(duration2);
-
- return duration1_seconds == duration2_seconds;
-}
-
-static void _check_duration(MafwGstRendererWorker *worker, gint64 value)
-{
- MafwGstRenderer *renderer = worker->owner;
- gboolean right_query = TRUE;
-
- if (value == -1) {
- GstFormat format = GST_FORMAT_TIME;
- right_query =
- gst_element_query_duration(worker->pipeline, &format,
- &value);
- }
-
- if (right_query && value > 0) {
- gint duration_seconds = NSECONDS_TO_SECONDS(value);
-
- if (!_seconds_duration_equal(worker->media.length_nanos,
- value)) {
- /* Add the duration to the current metadata. */
- _current_metadata_add(worker, MAFW_METADATA_KEY_DURATION,
- G_TYPE_INT64,
- (gint64)duration_seconds);
- /* Emit the duration. */
- mafw_renderer_emit_metadata_int64(
- worker->owner, MAFW_METADATA_KEY_DURATION,
- (gint64)duration_seconds);
- }
-
- /* We compare this duration we just got with the
- * source one and update it in the source if needed */
- if (duration_seconds > 0 &&
- duration_seconds != renderer->media->duration) {
- mafw_gst_renderer_update_source_duration(
- renderer,
- duration_seconds);
- }
- }
-
- worker->media.length_nanos = value;
- g_debug("media duration: %lld", worker->media.length_nanos);
-}
-
-static void _check_seekability(MafwGstRendererWorker *worker)
-{
- MafwGstRenderer *renderer = worker->owner;
- SeekabilityType seekable = SEEKABILITY_NO_SEEKABLE;
-
- if (worker->media.length_nanos != -1)
- {
- g_debug("source seekability %d", renderer->media->seekability);
-
- if (renderer->media->seekability != SEEKABILITY_NO_SEEKABLE) {
- g_debug("Quering GStreamer for seekability");
- GstQuery *seek_query;
- GstFormat format = GST_FORMAT_TIME;
- /* Query the seekability of the stream */
- seek_query = gst_query_new_seeking(format);
- if (gst_element_query(worker->pipeline, seek_query)) {
- gboolean renderer_seekable = FALSE;
- gst_query_parse_seeking(seek_query, NULL,
- &renderer_seekable,
- NULL, NULL);
- g_debug("GStreamer seekability %d",
- renderer_seekable);
- seekable = renderer_seekable ?
- SEEKABILITY_SEEKABLE :
- SEEKABILITY_NO_SEEKABLE;
- }
- gst_query_unref(seek_query);
- }
- }
-
- if (worker->media.seekable != seekable) {
- gboolean is_seekable = (seekable == SEEKABILITY_SEEKABLE);
-
- /* Add the seekability to the current metadata. */
- _current_metadata_add(worker, MAFW_METADATA_KEY_IS_SEEKABLE,
- G_TYPE_BOOLEAN, is_seekable);
-
- /* Emit. */
- mafw_renderer_emit_metadata_boolean(
- worker->owner, MAFW_METADATA_KEY_IS_SEEKABLE,
- is_seekable);
- }
-
- g_debug("media seekable: %d", seekable);
- worker->media.seekable = seekable;
-}
-
-static gboolean _query_duration_and_seekability_timeout(gpointer data)
-{
- MafwGstRendererWorker *worker = data;
-
- _check_duration(worker, -1);
- _check_seekability(worker);
-
- worker->duration_seek_timeout = 0;
-
- return FALSE;
-}
-
-/*
- * Called when the pipeline transitions into PAUSED state. It extracts more
- * information from Gst.
- */
-static void _finalize_startup(MafwGstRendererWorker *worker)
-{
- /* Check video caps */
- if (worker->media.has_visual_content) {
- GstPad *pad = GST_BASE_SINK_PAD(worker->vsink);
- GstCaps *caps = GST_PAD_CAPS(pad);
- if (caps && gst_caps_is_fixed(caps)) {
- GstStructure *structure;
- structure = gst_caps_get_structure(caps, 0);
- if (!_handle_video_info(worker, structure))
- return;
- }
- }
-
- /* Something might have gone wrong at this point already. */
- if (worker->is_error) {
- g_debug("Error occured during preroll");
- return;
- }
-
- /* Streaminfo might reveal the media to be unsupported. Therefore we
- * need to check the error again. */
- _parse_stream_info(worker);
- if (worker->is_error) {
- g_debug("Error occured. Leaving");
- return;
- }
-
- /* Check duration and seekability */
- if (worker->duration_seek_timeout != 0) {
- g_source_remove(worker->duration_seek_timeout);
- worker->duration_seek_timeout = 0;
- }
- _check_duration(worker, -1);
- _check_seekability(worker);
-}
-
-static void _add_duration_seek_query_timeout(MafwGstRendererWorker *worker)
-{
- if (worker->duration_seek_timeout != 0) {
- g_source_remove(worker->duration_seek_timeout);
- }
- worker->duration_seek_timeout = g_timeout_add_seconds(
- MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY,
- _query_duration_and_seekability_timeout,
- worker);
-}
-
-static void _do_pause_postprocessing(MafwGstRendererWorker *worker)
-{
- if (worker->notify_pause_handler) {
- worker->notify_pause_handler(worker, worker->owner);
- }
-
-#ifdef HAVE_GDKPIXBUF
- if (worker->media.has_visual_content &&
- worker->current_frame_on_pause) {
- GstBuffer *buffer = NULL;
-
- g_object_get(worker->pipeline, "frame", &buffer, NULL);
-
- if (buffer != NULL) {
- _emit_gst_buffer_as_graphic_file(
- worker, buffer,
- MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI);
- }
- }
-#endif
-
- _add_ready_timeout(worker);
-}
-
-static void _report_playing_state(MafwGstRendererWorker * worker)
-{
- if (worker->report_statechanges) {
- switch (worker->mode) {
- case WORKER_MODE_SINGLE_PLAY:
- /* Notify play if we are playing in
- * single mode */
- if (worker->notify_play_handler)
- worker->notify_play_handler(
- worker,
- worker->owner);
- break;
- case WORKER_MODE_PLAYLIST:
- case WORKER_MODE_REDUNDANT:
- /* Only notify play when the "playlist"
- playback starts, don't notify play for each
- individual element of the playlist. */
- if (worker->pl.notify_play_pending) {
- if (worker->notify_play_handler)
- worker->notify_play_handler(
- worker,
- worker->owner);
- worker->pl.notify_play_pending = FALSE;
- }
- break;
- default: break;
- }
- }
-}
-
-static void _handle_state_changed(GstMessage *msg, MafwGstRendererWorker *worker)
-{
- GstState newstate, oldstate;
- GstStateChange statetrans;
- MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner;
-
- gst_message_parse_state_changed(msg, &oldstate, &newstate, NULL);
- statetrans = GST_STATE_TRANSITION(oldstate, newstate);
- g_debug ("State changed: %d: %d -> %d", worker->state, oldstate, newstate);
-
- /* If the state is the same we do nothing, otherwise, we keep
- * it */
- if (worker->state == newstate) {
- return;
- } else {
- worker->state = newstate;
- }
-
- if (statetrans == GST_STATE_CHANGE_READY_TO_PAUSED &&
- worker->in_ready) {
- /* Woken up from READY, resume stream position and playback */
- g_debug("State changed to pause after ready");
- if (worker->seek_position > 0) {
- _check_seekability(worker);
- if (worker->media.seekable) {
- g_debug("performing a seek");
- _do_seek(worker, GST_SEEK_TYPE_SET,
- worker->seek_position, NULL);
- } else {
- g_critical("media is not seekable (and should)");
- }
- }
-
- /* If playing a stream wait for buffering to finish before
- starting to play */
- if (!worker->is_stream || worker->is_live) {
- _do_play(worker);
- }
- return;
- }
-
- /* While buffering, we have to wait in PAUSED
- until we reach 100% before doing anything */
- if (worker->buffering) {
- if (statetrans == GST_STATE_CHANGE_PAUSED_TO_PLAYING) {
- /* Mmm... probably the client issued a seek on the
- * stream and then a play/resume command right away,
- * so the stream got into PLAYING state while
- * buffering. When the next buffering signal arrives,
- * the stream will be PAUSED silently and resumed when
- * buffering is done (silently too), so let's signal
- * the state change to PLAYING here. */
- _report_playing_state(worker);
- }
- return;
- }
-
- switch (statetrans) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- if (worker->prerolling && worker->report_statechanges) {
- /* PAUSED after pipeline has been
- * constructed. We check caps, seek and
- * duration and if staying in pause is needed,
- * we perform operations for pausing, such as
- * current frame on pause and signalling state
- * change and adding the timeout to go to ready */
- g_debug ("Prerolling done, finalizaing startup");
- _finalize_startup(worker);
- _do_play(worker);
- renderer->play_failed_count = 0;
-
- if (worker->stay_paused) {
- _do_pause_postprocessing(worker);
- }
- worker->prerolling = FALSE;
- }
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- /* When pausing we do the stuff, like signalling
- * state, current frame on pause and timeout to go to
- * ready */
- if (worker->report_statechanges) {
- _do_pause_postprocessing(worker);
- }
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- /* if seek was called, at this point it is really ended */
- worker->seek_position = -1;
- worker->eos = FALSE;
-
- /* Signal state change if needed */
- _report_playing_state(worker);
-
- /* Prevent blanking if we are playing video */
- if (worker->media.has_visual_content) {
- blanking_prohibit();
- }
- keypadlocking_prohibit();
- /* Remove the ready timeout if we are playing [again] */
- _remove_ready_timeout(worker);
- /* If mode is redundant we are trying to play one of several
- * candidates, so when we get a successful playback, we notify
- * the real URI that we are playing */
- if (worker->mode == WORKER_MODE_REDUNDANT) {
- mafw_renderer_emit_metadata_string(
- worker->owner,
- MAFW_METADATA_KEY_URI,
- worker->media.location);
- }
-
- /* Emit metadata. We wait until we reach the playing
- state because this speeds up playback start time */
- _emit_metadatas(worker);
- /* Query duration and seekability. Useful for vbr
- * clips or streams. */
- _add_duration_seek_query_timeout(worker);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- /* If we went to READY, we free the taglist and
- * deassign the timout it */
- if (worker->in_ready) {
- g_debug("changed to GST_STATE_READY");
- _free_taglist(worker);
- }
- break;
- default:
- break;
- }
-}
-
-static void _handle_duration(MafwGstRendererWorker *worker, GstMessage *msg)
-{
- GstFormat fmt;
- gint64 duration;
-
- gst_message_parse_duration(msg, &fmt, &duration);
-
- if (worker->duration_seek_timeout != 0) {
- g_source_remove(worker->duration_seek_timeout);
- worker->duration_seek_timeout = 0;
- }
-
- _check_duration(worker,
- duration != GST_CLOCK_TIME_NONE ? duration : -1);
- _check_seekability(worker);
-}
-
-#ifdef HAVE_GDKPIXBUF
-static void _emit_renderer_art(MafwGstRendererWorker *worker,
- const GstTagList *list)
-{
- GstBuffer *buffer = NULL;
- const GValue *value = NULL;
-
- g_return_if_fail(gst_tag_list_get_tag_size(list, GST_TAG_IMAGE) > 0);
-
- value = gst_tag_list_get_value_index(list, GST_TAG_IMAGE, 0);
-
- g_return_if_fail((value != NULL) && G_VALUE_HOLDS(value, GST_TYPE_BUFFER));
-
- buffer = g_value_peek_pointer(value);
-
- g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
-
- _emit_gst_buffer_as_graphic_file(worker, buffer,
- MAFW_METADATA_KEY_RENDERER_ART_URI);
-}
-#endif
-
-static GHashTable* _build_tagmap(void)
-{
- GHashTable *hash_table = NULL;
-
- hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
- g_free);
-
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_TITLE),
- g_strdup(MAFW_METADATA_KEY_TITLE));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_ARTIST),
- g_strdup(MAFW_METADATA_KEY_ARTIST));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_AUDIO_CODEC),
- g_strdup(MAFW_METADATA_KEY_AUDIO_CODEC));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_VIDEO_CODEC),
- g_strdup(MAFW_METADATA_KEY_VIDEO_CODEC));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_BITRATE),
- g_strdup(MAFW_METADATA_KEY_BITRATE));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_LANGUAGE_CODE),
- g_strdup(MAFW_METADATA_KEY_ENCODING));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_ALBUM),
- g_strdup(MAFW_METADATA_KEY_ALBUM));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_GENRE),
- g_strdup(MAFW_METADATA_KEY_GENRE));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_TRACK_NUMBER),
- g_strdup(MAFW_METADATA_KEY_TRACK));
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_ORGANIZATION),
- g_strdup(MAFW_METADATA_KEY_ORGANIZATION));
-#ifdef HAVE_GDKPIXBUF
- g_hash_table_insert(hash_table, g_strdup(GST_TAG_IMAGE),
- g_strdup(MAFW_METADATA_KEY_RENDERER_ART_URI));
-#endif
-
- return hash_table;
-}
-
-/*
- * Emits metadata-changed signals for gst tags.
- */
-static void _emit_tag(const GstTagList *list, const gchar *tag,
- MafwGstRendererWorker *worker)
-{
- /* Mapping between Gst <-> MAFW metadata tags
- * NOTE: This assumes that GTypes matches between GST and MAFW. */
- static GHashTable *tagmap = NULL;
- gint i, count;
- const gchar *mafwtag;
- GType type;
- GValueArray *values;
-
- if (tagmap == NULL) {
- tagmap = _build_tagmap();
- }
-
- g_debug("tag: '%s' (type: %s)", tag,
- g_type_name(gst_tag_get_type(tag)));
- /* Is there a mapping for this tag? */
- mafwtag = g_hash_table_lookup(tagmap, tag);
- if (!mafwtag)
- return;
-
-#ifdef HAVE_GDKPIXBUF
- if (strcmp (mafwtag, MAFW_METADATA_KEY_RENDERER_ART_URI) == 0) {
- _emit_renderer_art(worker, list);
- return;
- }
-#endif
-
- /* Build a value array of this tag. We need to make sure that strings
- * are UTF-8. GstTagList API says that the value is always UTF8, but it
- * looks like the ID3 demuxer still might sometimes produce non-UTF-8
- * strings. */
- count = gst_tag_list_get_tag_size(list, tag);
- type = gst_tag_get_type(tag);
- values = g_value_array_new(count);
- for (i = 0; i < count; ++i) {
- GValue *v = (GValue *)
- gst_tag_list_get_value_index(list, tag, i);
- if (type == G_TYPE_STRING) {
- gchar *orig, *utf8;
-
- gst_tag_list_get_string_index(list, tag, i, &orig);
- if (convert_utf8(orig, &utf8)) {
- GValue utf8gval = {0};
-
- g_value_init(&utf8gval, G_TYPE_STRING);
- g_value_take_string(&utf8gval, utf8);
- _current_metadata_add(worker, mafwtag, G_TYPE_STRING,
- utf8);
- g_value_array_append(values, &utf8gval);
- g_value_unset(&utf8gval);
- }
- g_free(orig);
- } else if (type == G_TYPE_UINT) {
- GValue intgval = {0};
- gint intval;
-
- g_value_init(&intgval, G_TYPE_INT);
- g_value_transform(v, &intgval);
- intval = g_value_get_int(&intgval);
- _current_metadata_add(worker, mafwtag, G_TYPE_INT,
- intval);
- g_value_array_append(values, &intgval);
- g_value_unset(&intgval);
- } else {
- _current_metadata_add(worker, mafwtag, G_TYPE_VALUE,
- v);
- g_value_array_append(values, v);
- }
- }
-
- /* Emit the metadata. */
- g_signal_emit_by_name(worker->owner, "metadata-changed", mafwtag,
- values);
-
- g_value_array_free(values);
-}
-
-/**
- * Collect tag-messages, parse it later, when playing is ongoing
- */
-static void _handle_tag(MafwGstRendererWorker *worker, GstMessage *msg)
-{
- /* Do not emit metadata until we get to PLAYING state to speed up
- playback start */
- if (worker->tag_list == NULL)
- worker->tag_list = g_ptr_array_new();
- g_ptr_array_add(worker->tag_list, gst_message_ref(msg));
-
- /* Some tags come in playing state, so in this case we have
- to emit them right away (example: radio stations) */
- if (worker->state == GST_STATE_PLAYING) {
- _emit_metadatas(worker);
- }
-}
-
-/**
- * Parses the list of tag-messages
- */
-static void _parse_tagmsg(GstMessage *msg, MafwGstRendererWorker *worker)
-{
- GstTagList *new_tags;
-
- gst_message_parse_tag(msg, &new_tags);
- gst_tag_list_foreach(new_tags, (gpointer)_emit_tag, worker);
- gst_tag_list_free(new_tags);
- gst_message_unref(msg);
-}
-
-/**
- * Parses the collected tag messages, and emits the metadatas
- */
-static void _emit_metadatas(MafwGstRendererWorker *worker)
-{
- if (worker->tag_list != NULL)
- {
- g_ptr_array_foreach(worker->tag_list, (GFunc)_parse_tagmsg,
- worker);
- g_ptr_array_free(worker->tag_list, TRUE);
- worker->tag_list = NULL;
- }
-}
-
-static void _reset_volume_and_mute_to_pipeline(MafwGstRendererWorker *worker)
-{
-#ifdef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
- g_debug("resetting volume and mute to pipeline");
-
- if (worker->pipeline != NULL) {
- g_object_set(
- G_OBJECT(worker->pipeline), "volume",
- mafw_gst_renderer_worker_volume_get(worker->wvolume),
- "mute",
- mafw_gst_renderer_worker_volume_is_muted(worker->wvolume),
- NULL);
- }
-#endif
-}
-
-static void _handle_buffering(MafwGstRendererWorker *worker, GstMessage *msg)
-{
- gint percent;
- MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner;
-
- gst_message_parse_buffering(msg, &percent);
- g_debug("buffering: %d", percent);
-
- /* No state management needed for live pipelines */
- if (!worker->is_live) {
- worker->buffering = TRUE;
- if (percent < 100 && worker->state == GST_STATE_PLAYING) {
- g_debug("setting pipeline to PAUSED not to wolf the "
- "buffer down");
- worker->report_statechanges = FALSE;
- /* We can't call _pause() here, since it sets
- * the "report_statechanges" to TRUE. We don't
- * want that, application doesn't need to know
- * that internally the state changed to
- * PAUSED. */
- if (gst_element_set_state(worker->pipeline,
- GST_STATE_PAUSED) ==
- GST_STATE_CHANGE_ASYNC)
- {
- /* XXX this blocks at most 2 seconds. */
- gst_element_get_state(worker->pipeline, NULL,
- NULL,
- 2 * GST_SECOND);
- }
- }
-
- if (percent >= 100) {
- /* On buffering we go to PAUSED, so here we move back to
- PLAYING */
- worker->buffering = FALSE;
- if (worker->state == GST_STATE_PAUSED) {
- /* If buffering more than once, do this only the
- first time we are done with buffering */
- if (worker->prerolling) {
- g_debug("buffering concluded during "
- "prerolling");
- _finalize_startup(worker);
- _do_play(worker);
- renderer->play_failed_count = 0;
- /* Send the paused notification */
- if (worker->stay_paused &&
- worker->notify_pause_handler) {
- worker->notify_pause_handler(
- worker,
- worker->owner);
- }
- worker->prerolling = FALSE;
- } else if (worker->in_ready) {
- /* If we had been woken up from READY
- and we have finish our buffering,
- check if we have to play or stay
- paused and if we have to play,
- signal the state change. */
- g_debug("buffering concluded, "
- "continuing playing");
- _do_play(worker);
- } else if (!worker->stay_paused) {
- /* This means, that we were playing but
- ran out of buffer, so we silently
- paused waited for buffering to
- finish and now we continue silently
- (silently meaning we do not expose
- state changes) */
- g_debug("buffering concluded, setting "
- "pipeline to PLAYING again");
- _reset_volume_and_mute_to_pipeline(
- worker);
- if (gst_element_set_state(
- worker->pipeline,
- GST_STATE_PLAYING) ==
- GST_STATE_CHANGE_ASYNC)
- {
- /* XXX this blocks at most 2 seconds. */
- gst_element_get_state(
- worker->pipeline, NULL, NULL,
- 2 * GST_SECOND);
- }
- }
- } else if (worker->state == GST_STATE_PLAYING) {
- g_debug("buffering concluded, signalling "
- "state change");
- /* In this case we got a PLAY command while
- buffering, likely because it was issued
- before we got the first buffering signal.
- The UI should not do this, but if it does,
- we have to signal that we have executed
- the state change, since in
- _handle_state_changed we do not do anything
- if we are buffering */
-
- /* Set the pipeline to playing. This is an async
- handler, it could be, that the reported state
- is not the real-current state */
- if (gst_element_set_state(
- worker->pipeline,
- GST_STATE_PLAYING) ==
- GST_STATE_CHANGE_ASYNC)
- {
- /* XXX this blocks at most 2 seconds. */
- gst_element_get_state(
- worker->pipeline, NULL, NULL,
- 2 * GST_SECOND);
- }
- if (worker->report_statechanges &&
- worker->notify_play_handler) {
- worker->notify_play_handler(
- worker,
- worker->owner);
- }
- _add_duration_seek_query_timeout(worker);
- }
- }
- }
-
- /* Send buffer percentage */
- if (worker->notify_buffer_status_handler)
- worker->notify_buffer_status_handler(worker, worker->owner,
- percent);
-}
-
-static void _handle_element_msg(MafwGstRendererWorker *worker, GstMessage *msg)
-{
- /* Only HelixBin sends "resolution" messages. */
- if (gst_structure_has_name(msg->structure, "resolution") &&
- _handle_video_info(worker, msg->structure))
- {
- worker->media.has_visual_content = TRUE;
- }
-}
-
-static void _reset_pl_info(MafwGstRendererWorker *worker)
-{
- if (worker->pl.items) {
- g_slist_foreach(worker->pl.items, (GFunc) g_free, NULL);
- g_slist_free(worker->pl.items);
- worker->pl.items = NULL;
- }
-
- worker->pl.current = 0;
- worker->pl.notify_play_pending = TRUE;
-}
-
-static GError * _get_specific_missing_plugin_error(GstMessage *msg)
-{
- const GstStructure *gst_struct;
- const gchar *type;
-
- GError *error;
- gchar *desc;
-
- desc = gst_missing_plugin_message_get_description(msg);
-
- gst_struct = gst_message_get_structure(msg);
- type = gst_structure_get_string(gst_struct, "type");
-
- if ((type) && ((strcmp(type, MAFW_GST_MISSING_TYPE_DECODER) == 0) ||
- (strcmp(type, MAFW_GST_MISSING_TYPE_ENCODER) == 0))) {
-
- /* Missing codec error. */
- const GValue *val;
- const GstCaps *caps;
- GstStructure *caps_struct;
- const gchar *mime;
-
- val = gst_structure_get_value(gst_struct, "detail");
- caps = gst_value_get_caps(val);
- caps_struct = gst_caps_get_structure(caps, 0);
- mime = gst_structure_get_name(caps_struct);
-
- if (g_strrstr(mime, "video")) {
- error = g_error_new_literal(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_VIDEO_CODEC_NOT_FOUND,
- desc);
- } else if (g_strrstr(mime, "audio")) {
- error = g_error_new_literal(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_AUDIO_CODEC_NOT_FOUND,
- desc);
- } else {
- error = g_error_new_literal(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_CODEC_NOT_FOUND,
- desc);
- }
- } else {
- /* Unsupported type error. */
- error = g_error_new(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE,
- "missing plugin: %s", desc);
- }
-
- g_free(desc);
-
- return error;
-}
-
-/*
- * Asynchronous message handler. It gets removed from if it returns FALSE.
- */
-static gboolean _async_bus_handler(GstBus *bus, GstMessage *msg,
- MafwGstRendererWorker *worker)
-{
- /* No need to handle message if error has already occured. */
- if (worker->is_error)
- return TRUE;
-
- /* Handle missing-plugin (element) messages separately, relaying more
- * details. */
- if (gst_is_missing_plugin_message(msg)) {
- GError *err = _get_specific_missing_plugin_error(msg);
- /* FIXME?: for some reason, calling the error handler directly
- * (_send_error) causes problems. On the other hand, turning
- * the error into a new GstMessage and letting the next
- * iteration handle it seems to work. */
- _post_error(worker, err);
- return TRUE;
- }
-
- switch (GST_MESSAGE_TYPE(msg)) {
- case GST_MESSAGE_ERROR:
- if (!worker->is_error) {
- gchar *debug;
- GError *err;
-
- debug = NULL;
- gst_message_parse_error(msg, &err, &debug);
- g_debug("gst error: domain = %d, code = %d, "
- "message = '%s', debug = '%s'",
- err->domain, err->code, err->message, debug);
- if (debug)
- g_free(debug);
-
- /* If we are in playlist/radio mode, we silently
- ignore the error and continue with the next
- item until we end the playlist. If no
- playable elements we raise the error and
- after finishing we go to normal mode */
-
- if (worker->mode == WORKER_MODE_PLAYLIST ||
- worker->mode == WORKER_MODE_REDUNDANT) {
- if (worker->pl.current <
- (g_slist_length(worker->pl.items) - 1)) {
- /* If the error is "no space left"
- notify, otherwise try to play the
- next item */
- if (err->code ==
- GST_RESOURCE_ERROR_NO_SPACE_LEFT) {
- _send_error(worker, err);
-
- } else {
- _play_pl_next(worker);
- }
- } else {
- /* Playlist EOS. We cannot try another
- * URI, so we have to go back to normal
- * mode and signal the error (done
- * below) */
- worker->mode = WORKER_MODE_SINGLE_PLAY;
- _reset_pl_info(worker);
- }
- }
-
- if (worker->mode == WORKER_MODE_SINGLE_PLAY) {
- if (err->domain == GST_STREAM_ERROR &&
- err->code == GST_STREAM_ERROR_WRONG_TYPE)
- {/* Maybe it is a playlist? */
- GSList *plitems = _parse_playlist(worker->media.location);
-
- if (plitems)
- {/* Yes, it is a plitem */
- g_error_free(err);
- mafw_gst_renderer_worker_play(worker, NULL, plitems);
- break;
- }
-
-
- }
- _send_error(worker, err);
- }
- }
- break;
- case GST_MESSAGE_EOS:
- if (!worker->is_error) {
- worker->eos = TRUE;
-
- if (worker->mode == WORKER_MODE_PLAYLIST) {
- if (worker->pl.current <
- (g_slist_length(worker->pl.items) - 1)) {
- /* If the playlist EOS is not reached
- continue playing */
- _play_pl_next(worker);
- } else {
- /* Playlist EOS, go back to normal
- mode */
- worker->mode = WORKER_MODE_SINGLE_PLAY;
- _reset_pl_info(worker);
- }
- }
-
- if (worker->mode == WORKER_MODE_SINGLE_PLAY ||
- worker->mode == WORKER_MODE_REDUNDANT) {
- if (worker->notify_eos_handler)
- worker->notify_eos_handler(
- worker,
- worker->owner);
-
- /* We can remove the message handlers now, we
- are not interested in bus messages
- anymore. */
- if (worker->bus) {
- gst_bus_set_sync_handler(worker->bus,
- NULL,
- NULL);
- }
- if (worker->async_bus_id) {
- g_source_remove(worker->async_bus_id);
- worker->async_bus_id = 0;
- }
-
- if (worker->mode == WORKER_MODE_REDUNDANT) {
- /* Go to normal mode */
- worker->mode = WORKER_MODE_SINGLE_PLAY;
- _reset_pl_info(worker);
- }
- }
- }
- break;
- case GST_MESSAGE_TAG:
- _handle_tag(worker, msg);
- break;
- case GST_MESSAGE_BUFFERING:
- _handle_buffering(worker, msg);
- break;
- case GST_MESSAGE_DURATION:
- _handle_duration(worker, msg);
- break;
- case GST_MESSAGE_ELEMENT:
- _handle_element_msg(worker, msg);
- break;
- case GST_MESSAGE_STATE_CHANGED:
- if ((GstElement *)GST_MESSAGE_SRC(msg) == worker->pipeline)
- _handle_state_changed(msg, worker);
- break;
- case GST_MESSAGE_APPLICATION:
- if (gst_structure_has_name(gst_message_get_structure(msg),
- "ckey"))
- {
- GValue v = {0};
- g_value_init(&v, G_TYPE_INT);
- g_value_set_int(&v, worker->colorkey);
- mafw_extension_emit_property_changed(
- MAFW_EXTENSION(worker->owner),
- MAFW_PROPERTY_RENDERER_COLORKEY,
- &v);
- }
- default: break;
- }
- return TRUE;
-}
-
-/* NOTE this function will possibly be called from a different thread than the
- * glib main thread. */
-static void _stream_info_cb(GstObject *pipeline, GParamSpec *unused,
- MafwGstRendererWorker *worker)
-{
- g_debug("stream-info changed");
- _parse_stream_info(worker);
-}
-
-static void _volume_cb(MafwGstRendererWorkerVolume *wvolume, gdouble volume,
- gpointer data)
-{
- MafwGstRendererWorker *worker = data;
- GValue value = {0, };
-
- _reset_volume_and_mute_to_pipeline(worker);
-
- g_value_init(&value, G_TYPE_UINT);
- g_value_set_uint(&value, (guint) (volume * 100.0));
- mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner),
- MAFW_PROPERTY_RENDERER_VOLUME,
- &value);
-}
-
-#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
-
-static void _mute_cb(MafwGstRendererWorkerVolume *wvolume, gboolean mute,
- gpointer data)
-{
- MafwGstRendererWorker *worker = data;
- GValue value = {0, };
-
- _reset_volume_and_mute_to_pipeline(worker);
-
- g_value_init(&value, G_TYPE_BOOLEAN);
- g_value_set_boolean(&value, mute);
- mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner),
- MAFW_PROPERTY_RENDERER_MUTE,
- &value);
-}
-
-#endif
-
-/* TODO: I think it's not enought to act on error, we need to handle
- * DestroyNotify on the given window ourselves, because for example helixbin
- * does it and silently stops the decoder thread. But it doesn't notify
- * us... */
-static int xerror(Display *dpy, XErrorEvent *xev)
-{
- MafwGstRendererWorker *worker;
-
- if (Global_worker == NULL) {
- return -1;
- } else {
- worker = Global_worker;
- }
-
- /* Swallow BadWindow and stop pipeline when the error is about the
- * currently set xid. */
- if (worker->xid &&
- xev->resourceid == worker->xid &&
- xev->error_code == BadWindow)
- {
- g_warning("BadWindow received for current xid (%x).",
- (gint)xev->resourceid);
- worker->xid = 0;
- /* We must post a message to the bus, because this function is
- * invoked from a different thread (xvimagerenderer's queue). */
- _post_error(worker, g_error_new_literal(
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_PLAYBACK,
- "Video window gone"));
- }
- return 0;
-}
-
-/*
- * Resets the media information.
- */
-static void _reset_media_info(MafwGstRendererWorker *worker)
-{
- if (worker->media.location) {
- g_free(worker->media.location);
- worker->media.location = NULL;
- }
- worker->media.length_nanos = -1;
- worker->media.has_visual_content = FALSE;
- worker->media.seekable = SEEKABILITY_UNKNOWN;
- worker->media.video_width = 0;
- worker->media.video_height = 0;
- worker->media.fps = 0.0;
-}
-
-static void _set_volume_and_mute(MafwGstRendererWorker *worker, gdouble vol,
- gboolean mute)
-{
- g_return_if_fail(worker->wvolume != NULL);
-
- mafw_gst_renderer_worker_volume_set(worker->wvolume, vol, mute);
-}
-
-static void _set_volume(MafwGstRendererWorker *worker, gdouble new_vol)
-{
- g_return_if_fail(worker->wvolume != NULL);
-
- _set_volume_and_mute(
- worker, new_vol,
- mafw_gst_renderer_worker_volume_is_muted(worker->wvolume));
-}
-
-static void _set_mute(MafwGstRendererWorker *worker, gboolean mute)
-{
- g_return_if_fail(worker->wvolume != NULL);
-
- _set_volume_and_mute(
- worker, mafw_gst_renderer_worker_volume_get(worker->wvolume),
- mute);
-}
-
-/*
- * Start to play the media
- */
-static void _start_play(MafwGstRendererWorker *worker)
-{
- MafwGstRenderer *renderer = (MafwGstRenderer*) worker->owner;
- GstStateChangeReturn state_change_info;
- char *autoload_sub = NULL;
-
- g_assert(worker->pipeline);
- g_object_set(G_OBJECT(worker->pipeline),
- "uri", worker->media.location, NULL);
-
- if (worker->subtitles.enabled) {
- autoload_sub = uri_get_subtitle_uri(worker->media.location);
- if (autoload_sub) {
- g_debug("SUBURI: %s", autoload_sub);
- g_object_set(G_OBJECT(worker->pipeline),
- "suburi", autoload_sub,
- "subtitle-font-desc", worker->subtitles.font,
- "subtitle-encoding", worker->subtitles.encoding,
- NULL);
-
- gst_element_set_state(worker->pipeline, GST_STATE_READY);
- g_free(autoload_sub);
- }
- } else {
- g_object_set(G_OBJECT(worker->pipeline), "suburi", NULL, NULL);
- }
-
- g_debug("URI: %s", worker->media.location);
- g_debug("setting pipeline to PAUSED");
-
- worker->report_statechanges = TRUE;
- state_change_info = gst_element_set_state(worker->pipeline,
- GST_STATE_PAUSED);
- if (state_change_info == GST_STATE_CHANGE_NO_PREROLL) {
- /* FIXME: for live sources we may have to handle
- buffering and prerolling differently */
- g_debug ("Source is live!");
- worker->is_live = TRUE;
- }
- worker->prerolling = TRUE;
-
- worker->is_stream = uri_is_stream(worker->media.location);
-
- if (renderer->update_playcount_id > 0) {
- g_source_remove(renderer->update_playcount_id);
- renderer->update_playcount_id = 0;
- }
-
-}
-
-/*
- * Constructs gst pipeline
- *
- * FIXME: Could the same pipeline be used for playing all media instead of
- * constantly deleting and reconstructing it again?
- */
-static void _construct_pipeline(MafwGstRendererWorker *worker)
-{
- g_debug("constructing pipeline");
- g_assert(worker != NULL);
-
- /* Return if we have already one */
- if (worker->pipeline)
- return;
-
- _free_taglist(worker);
-
- g_debug("Creating a new instance of playbin2");
- worker->pipeline = gst_element_factory_make("playbin2",
- "playbin");
- if (worker->pipeline == NULL)
- {
- /* Let's try with playbin */
- g_warning ("playbin2 failed, falling back to playbin");
- worker->pipeline = gst_element_factory_make("playbin",
- "playbin");
-
- if (worker->pipeline) {
- /* Use nwqueue only for non-rtsp and non-mms(h)
- streams. */
- gboolean use_nw;
- use_nw = worker->media.location &&
- !g_str_has_prefix(worker->media.location,
- "rtsp://") &&
- !g_str_has_prefix(worker->media.location,
- "mms://") &&
- !g_str_has_prefix(worker->media.location,
- "mmsh://");
-
- g_debug("playbin using network queue: %d", use_nw);
-
- /* These need a modified version of playbin. */
- g_object_set(G_OBJECT(worker->pipeline),
- "nw-queue", use_nw,
- "no-video-transform", TRUE,
- NULL);
- }
- }
-
- if (!worker->pipeline) {
- g_critical("failed to create playback pipeline");
- g_signal_emit_by_name(MAFW_EXTENSION (worker->owner),
- "error",
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
- "Could not create pipeline");
- g_assert_not_reached();
- }
-
-
- worker->bus = gst_pipeline_get_bus(GST_PIPELINE(worker->pipeline));
- gst_bus_set_sync_handler(worker->bus,
- (GstBusSyncHandler)_sync_bus_handler, worker);
- worker->async_bus_id = gst_bus_add_watch_full(worker->bus,G_PRIORITY_HIGH,
- (GstBusFunc)_async_bus_handler,
- worker, NULL);
-
- /* Listen for changes in stream-info object to find out whether the
- * media contains video and throw error if application has not provided
- * video window. */
- g_signal_connect(worker->pipeline, "notify::stream-info",
- G_CALLBACK(_stream_info_cb), worker);
-
-#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
-
-
- /* Set audio and video sinks ourselves. We create and configure
- them only once. */
- if (!worker->asink) {
- worker->asink = gst_element_factory_make("pulsesink", NULL);
- if (!worker->asink) {
- g_critical("Failed to create pipeline audio sink");
- g_signal_emit_by_name(MAFW_EXTENSION (worker->owner),
- "error",
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
- "Could not create audio sink");
- g_assert_not_reached();
- }
- gst_object_ref(worker->asink);
- g_object_set(worker->asink,
- "buffer-time", (gint64) MAFW_GST_BUFFER_TIME,
- "latency-time", (gint64) MAFW_GST_LATENCY_TIME,
- NULL);
- }
- g_object_set(worker->pipeline, "audio-sink", worker->asink, NULL);
-#endif
-
- if (!worker->vsink) {
- worker->vsink = gst_element_factory_make("xvimagesink", NULL);
- if (!worker->vsink) {
- g_critical("Failed to create pipeline video sink");
- g_signal_emit_by_name(MAFW_EXTENSION (worker->owner),
- "error",
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
- "Could not create video sink");
- g_assert_not_reached();
- }
- gst_object_ref(worker->vsink);
- g_object_set(G_OBJECT(worker->vsink),
- "handle-events", TRUE,
- "force-aspect-ratio", TRUE,
- NULL);
- }
- g_object_set(worker->pipeline,
- "video-sink", worker->vsink,
- "flags", 103,
- NULL);
-
- if (!worker->tsink) {
- worker->tsink = gst_element_factory_make("textoverlay", NULL);
- if (!worker->tsink) {
- g_critical("Failed to create pipeline text sink");
- g_signal_emit_by_name(MAFW_EXTENSION (worker->owner),
- "error",
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
- "Could not create text sink");
- g_assert_not_reached();
- }
- gst_object_ref(worker->tsink);
- }
- g_object_set(worker->pipeline, "text-sink", worker->tsink, NULL);
-}
-
-/*
- * @seek_type: GstSeekType
- * @position: Time in seconds where to seek
- */
-static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type,
- gint position, GError **error)
-{
- gboolean ret;
- gint64 spos;
-
- g_assert(worker != NULL);
-
- if (worker->eos || !worker->media.seekable)
- goto err;
-
- /* According to the docs, relative seeking is not so easy:
- GST_SEEK_TYPE_CUR - change relative to currently configured segment.
- This can't be used to seek relative to the current playback position -
- do a position query, calculate the desired position and then do an
- absolute position seek instead if that's what you want to do. */
- if (seek_type == GST_SEEK_TYPE_CUR)
- {
- gint curpos = mafw_gst_renderer_worker_get_position(worker);
- position = curpos + position;
- seek_type = GST_SEEK_TYPE_SET;
- }
-
- if (position < 0) {
- position = 0;
- }
-
- worker->seek_position = position;
- worker->report_statechanges = FALSE;
- spos = (gint64)position * GST_SECOND;
- g_debug("seek: type = %d, offset = %lld", seek_type, spos);
-
- /* If the pipeline has been set to READY by us, then wake it up by
- setting it to PAUSED (when we get the READY->PAUSED transition
- we will execute the seek). This way when we seek we disable the
- READY state (logical, since the player is not idle anymore)
- allowing the sink to render the destination frame in case of
- video playback */
- if (worker->in_ready && worker->state == GST_STATE_READY) {
- gst_element_set_state(worker->pipeline, GST_STATE_PAUSED);
- } else {
- ret = gst_element_seek(worker->pipeline, 1.0, GST_FORMAT_TIME,
- GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_KEY_UNIT,
- seek_type, spos,
- GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
- if (!ret) {
- /* Seeking is async, so seek_position should not be
- invalidated here */
- goto err;
- }
- }
- return;
-
-err: g_set_error(error,
- MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_CANNOT_SET_POSITION,
- "Seeking to %d failed", position);
-}
-
-/* @vol should be between [0 .. 100], higher values (up to 1000) are allowed,
- * but probably cause distortion. */
-void mafw_gst_renderer_worker_set_volume(
- MafwGstRendererWorker *worker, guint volume)
-{
- _set_volume(worker, CLAMP((gdouble)volume / 100.0, 0.0, 1.0));
-}
-
-guint mafw_gst_renderer_worker_get_volume(
- MafwGstRendererWorker *worker)
-{
- return (guint)
- (mafw_gst_renderer_worker_volume_get(worker->wvolume) * 100);
-}
-
-void mafw_gst_renderer_worker_set_mute(MafwGstRendererWorker *worker,
- gboolean mute)
-{
- _set_mute(worker, mute);
-}
-
-gboolean mafw_gst_renderer_worker_get_mute(MafwGstRendererWorker *worker)
-{
- return mafw_gst_renderer_worker_volume_is_muted(worker->wvolume);
-}
-
-#ifdef HAVE_GDKPIXBUF
-void mafw_gst_renderer_worker_set_current_frame_on_pause(MafwGstRendererWorker *worker,
- gboolean current_frame_on_pause)
-{
- worker->current_frame_on_pause = current_frame_on_pause;
-}
-
-gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(MafwGstRendererWorker *worker)
-{
- return worker->current_frame_on_pause;
-}
-#endif
-
-void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker,
- GstSeekType seek_type,
- gint position, GError **error)
-{
- /* If player is paused and we have a timeout for going to ready
- * restart it. This is logical, since the user is seeking and
- * thus, the player is not idle anymore. Also this prevents that
- * when seeking streams we enter buffering and in the middle of
- * the buffering process we set the pipeline to ready (which stops
- * the buffering before it reaches 100%, making the client think
- * buffering is still going on).
- */
- if (worker->ready_timeout) {
- _remove_ready_timeout(worker);
- _add_ready_timeout(worker);
- }
-
- _do_seek(worker, seek_type, position, error);
- if (worker->notify_seek_handler)
- worker->notify_seek_handler(worker, worker->owner);
-}
-
-/*
- * Gets current position, rounded down into precision of one second. If a seek
- * is pending, returns the position we are going to seek. Returns -1 on
- * failure.
- */
-gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker)
-{
- GstFormat format;
- gint64 time = 0;
- g_assert(worker != NULL);
-
- /* If seek is ongoing, return the position where we are seeking. */
- if (worker->seek_position != -1)
- {
- return worker->seek_position;
- }
- /* Otherwise query position from pipeline. */
- format = GST_FORMAT_TIME;
- if (worker->pipeline &&
- gst_element_query_position(worker->pipeline, &format, &time))
- {
- return (gint)(NSECONDS_TO_SECONDS(time));
- }
- return -1;
-}
-
-GHashTable *mafw_gst_renderer_worker_get_current_metadata(
- MafwGstRendererWorker *worker)
-{
- return worker->current_metadata;
-}
-
-void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid)
-{
- /* Check for errors on the target window */
- XSetErrorHandler(xerror);
-
- /* Store the target window id */
- g_debug("Setting xid: %x", (guint)xid);
- worker->xid = xid;
-
- /* Check if we should use it right away */
- mafw_gst_renderer_worker_apply_xid(worker);
-}
-
-XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker)
-{
- return worker->xid;
-}
-
-gboolean mafw_gst_renderer_worker_get_autopaint(
- MafwGstRendererWorker *worker)
-{
- return worker->autopaint;
-}
-void mafw_gst_renderer_worker_set_autopaint(
- MafwGstRendererWorker *worker, gboolean autopaint)
-{
- worker->autopaint = autopaint;
- if (worker->vsink)
- g_object_set(worker->vsink, "autopaint-colorkey",
- worker->autopaint, NULL);
-}
-
-gint mafw_gst_renderer_worker_get_colorkey(
- MafwGstRendererWorker *worker)
-{
- return worker->colorkey;
-}
-
-void mafw_gst_renderer_worker_set_colorkey(
- MafwGstRendererWorker *worker, gint colorkey)
-{
- worker->colorkey = colorkey;
- if (worker->vsink)
- g_object_set(worker->vsink, "colorkey",
- worker->colorkey, NULL);
-}
-
-gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker)
-{
- return worker->media.seekable;
-}
-
-static void _play_pl_next(MafwGstRendererWorker *worker) {
- gchar *next;
-
- g_assert(worker != NULL);
- g_return_if_fail(worker->pl.items != NULL);
-
- next = (gchar *) g_slist_nth_data(worker->pl.items,
- ++worker->pl.current);
- mafw_gst_renderer_worker_stop(worker);
- _reset_media_info(worker);
-
- worker->media.location = g_strdup(next);
- _construct_pipeline(worker);
- _start_play(worker);
-}
-
-static void _do_play(MafwGstRendererWorker *worker)
-{
- g_assert(worker != NULL);
-
- if (worker->pipeline == NULL) {
- g_debug("play without a pipeline!");
- return;
- }
- worker->report_statechanges = TRUE;
-
- /* If we have to stay paused, we do and add the ready
- * timeout. Otherwise, we move the pipeline */
- if (!worker->stay_paused) {
- /* If pipeline is READY, we move it to PAUSED,
- * otherwise, to PLAYING */
- if (worker->state == GST_STATE_READY) {
- gst_element_set_state(worker->pipeline,
- GST_STATE_PAUSED);
- g_debug("setting pipeline to PAUSED");
- } else {
- _reset_volume_and_mute_to_pipeline(worker);
- gst_element_set_state(worker->pipeline,
- GST_STATE_PLAYING);
- g_debug("setting pipeline to PLAYING");
- }
- }
- else {
- g_debug("staying in PAUSED state");
- _add_ready_timeout(worker);
- }
-}
-
-void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker,
- const gchar *uri, GSList *plitems)
-{
- g_assert(uri || plitems);
-
- mafw_gst_renderer_worker_stop(worker);
- _reset_media_info(worker);
- _reset_pl_info(worker);
- /* Check if the item to play is a single item or a playlist. */
- if (plitems || uri_is_playlist(uri)){
- gchar *item;
- /* In case of a playlist we parse it and start playing the first
- item of the playlist. */
- if (plitems)
- {
- worker->pl.items = plitems;
- }
- else
- {
- worker->pl.items = _parse_playlist(uri);
- }
- if (!worker->pl.items)
- {
- _send_error(worker,
- g_error_new(MAFW_RENDERER_ERROR,
- MAFW_RENDERER_ERROR_PLAYLIST_PARSING,
- "Playlist parsing failed: %s",
- uri));
- return;
- }
-
- /* Set the playback mode */
- worker->mode = WORKER_MODE_PLAYLIST;
- worker->pl.notify_play_pending = TRUE;
-
- /* Set the item to be played */
- worker->pl.current = 0;
- item = (gchar *) g_slist_nth_data(worker->pl.items, 0);
- worker->media.location = g_strdup(item);
- } else {
- /* Single item. Set the playback mode according to that */
- worker->mode = WORKER_MODE_SINGLE_PLAY;
-
- /* Set the item to be played */
- worker->media.location = g_strdup(uri);
- }
- _construct_pipeline(worker);
- _start_play(worker);
-}
-
-void mafw_gst_renderer_worker_play_alternatives(MafwGstRendererWorker *worker,
- gchar **uris)
-{
- gint i;
- gchar *item;
-
- g_assert(uris && uris[0]);
-
- mafw_gst_renderer_worker_stop(worker);
- _reset_media_info(worker);
- _reset_pl_info(worker);
-
- /* Add the uris to playlist */
- i = 0;
- while (uris[i]) {
- worker->pl.items =
- g_slist_append(worker->pl.items, g_strdup(uris[i]));
- i++;
- }
-
- /* Set the playback mode */
- worker->mode = WORKER_MODE_REDUNDANT;
- worker->pl.notify_play_pending = TRUE;
-
- /* Set the item to be played */
- worker->pl.current = 0;
- item = (gchar *) g_slist_nth_data(worker->pl.items, 0);
- worker->media.location = g_strdup(item);
-
- /* Start playing */
- _construct_pipeline(worker);
- _start_play(worker);
-}
-
-/*
- * Currently, stop destroys the Gst pipeline and resets the worker into
- * default startup configuration.
- */
-void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker)
-{
- g_debug("worker stop");
- g_assert(worker != NULL);
-
- /* If location is NULL, this is a pre-created pipeline */
- if (worker->async_bus_id && worker->pipeline && !worker->media.location)
- return;
-
- if (worker->pipeline) {
- g_debug("destroying pipeline");
- if (worker->async_bus_id) {
- g_source_remove(worker->async_bus_id);
- worker->async_bus_id = 0;
- }
- gst_bus_set_sync_handler(worker->bus, NULL, NULL);
- gst_element_set_state(worker->pipeline, GST_STATE_NULL);
- if (worker->bus) {
- gst_object_unref(GST_OBJECT_CAST(worker->bus));
- worker->bus = NULL;
- }
- gst_object_unref(GST_OBJECT(worker->pipeline));
- worker->pipeline = NULL;
- }
-
- /* Reset worker */
- worker->report_statechanges = TRUE;
- worker->state = GST_STATE_NULL;
- worker->prerolling = FALSE;
- worker->is_live = FALSE;
- worker->buffering = FALSE;
- worker->is_stream = FALSE;
- worker->is_error = FALSE;
- worker->eos = FALSE;
- worker->seek_position = -1;
- _remove_ready_timeout(worker);
- _free_taglist(worker);
- if (worker->current_metadata) {
- g_hash_table_destroy(worker->current_metadata);
- worker->current_metadata = NULL;
- }
-
- if (worker->duration_seek_timeout != 0) {
- g_source_remove(worker->duration_seek_timeout);
- worker->duration_seek_timeout = 0;
- }
-
- /* Reset media iformation */
- _reset_media_info(worker);
-
- /* We are not playing, so we can let the screen blank */
- blanking_allow();
- keypadlocking_allow();
-
- /* And now get a fresh pipeline ready */
- _construct_pipeline(worker);
-}
-
-void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker)
-{
- g_assert(worker != NULL);
-
- if (worker->buffering && worker->state == GST_STATE_PAUSED &&
- !worker->prerolling) {
- /* If we are buffering and get a pause, we have to
- * signal state change and stay_paused */
- g_debug("Pausing while buffering, signalling state change");
- worker->stay_paused = TRUE;
- if (worker->notify_pause_handler) {
- worker->notify_pause_handler(
- worker,
- worker->owner);
- }
- } else {
- worker->report_statechanges = TRUE;
-
- if (gst_element_set_state(worker->pipeline, GST_STATE_PAUSED) ==
- GST_STATE_CHANGE_ASYNC)
- {
- /* XXX this blocks at most 2 seconds. */
- gst_element_get_state(worker->pipeline, NULL, NULL,
- 2 * GST_SECOND);
- }
- blanking_allow();
- keypadlocking_allow();
- }
-}
-
-void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker)
-{
- if (worker->mode == WORKER_MODE_PLAYLIST ||
- worker->mode == WORKER_MODE_REDUNDANT) {
- /* We must notify play if the "playlist" playback
- is resumed */
- worker->pl.notify_play_pending = TRUE;
- }
- if (worker->buffering && worker->state == GST_STATE_PAUSED &&
- !worker->prerolling) {
- /* If we are buffering we cannot resume, but we know
- * that the pipeline will be moved to PLAYING as
- * stay_paused is FALSE, so we just activate the state
- * change report, this way as soon as buffering is finished
- * the pipeline will be set to PLAYING and the state
- * change will be reported */
- worker->report_statechanges = TRUE;
- g_debug("Resumed while buffering, activating pipeline state "
- "changes");
- /* Notice though that we can receive the Resume before
- we get any buffering information. In that case
- we go with the "else" branch and set the pipeline to
- to PLAYING. However, it is possible that in this case
- we get the fist buffering signal before the
- PAUSED -> PLAYING state change. In that case, since we
- ignore state changes while buffering we never signal
- the state change to PLAYING. We can only fix this by
- checking, when we receive a PAUSED -> PLAYING transition
- if we are buffering, and in that case signal the state
- change (if we get that transition while buffering
- is on, it can only mean that the client resumed playback
- while buffering, and we must notify the state change) */
- } else {
- _do_play(worker);
- }
-}
-
-static void _volume_init_cb(MafwGstRendererWorkerVolume *wvolume,
- gpointer data)
-{
- MafwGstRendererWorker *worker = data;
- gdouble volume;
- gboolean mute;
-
- worker->wvolume = wvolume;
-
- g_debug("volume manager initialized");
-
- volume = mafw_gst_renderer_worker_volume_get(wvolume);
- mute = mafw_gst_renderer_worker_volume_is_muted(wvolume);
- _volume_cb(wvolume, volume, worker);
-#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
- _mute_cb(wvolume, mute, worker);
-#endif
-}
-
-MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner)
-{
- MafwGstRendererWorker *worker;
- GMainContext *main_context;
-
- worker = g_new0(MafwGstRendererWorker, 1);
- worker->mode = WORKER_MODE_SINGLE_PLAY;
- worker->pl.items = NULL;
- worker->pl.current = 0;
- worker->pl.notify_play_pending = TRUE;
- worker->owner = owner;
- worker->report_statechanges = TRUE;
- worker->state = GST_STATE_NULL;
- worker->seek_position = -1;
- worker->ready_timeout = 0;
- worker->in_ready = FALSE;
- worker->xid = 0;
- worker->autopaint = TRUE;
- worker->colorkey = -1;
- worker->vsink = NULL;
- worker->asink = NULL;
- worker->tsink = NULL;
- worker->tag_list = NULL;
- worker->current_metadata = NULL;
- worker->subtitles.enabled = FALSE;
- worker->subtitles.font = NULL;
- worker->subtitles.encoding = NULL;
-
-#ifdef HAVE_GDKPIXBUF
- worker->current_frame_on_pause = FALSE;
- _init_tmp_files_pool(worker);
-#endif
- worker->notify_seek_handler = NULL;
- worker->notify_pause_handler = NULL;
- worker->notify_play_handler = NULL;
- worker->notify_buffer_status_handler = NULL;
- worker->notify_eos_handler = NULL;
- worker->notify_error_handler = NULL;
- Global_worker = worker;
- main_context = g_main_context_default();
- worker->wvolume = NULL;
- mafw_gst_renderer_worker_volume_init(main_context,
- _volume_init_cb, worker,
- _volume_cb, worker,
-#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
- _mute_cb,
-#else
- NULL,
-#endif
- worker);
- blanking_init();
- _construct_pipeline(worker);
-
- return worker;
-}
-
-void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker)
-{
- blanking_deinit();
-#ifdef HAVE_GDKPIXBUF
- _destroy_tmp_files_pool(worker);
-#endif
- mafw_gst_renderer_worker_volume_destroy(worker->wvolume);
- mafw_gst_renderer_worker_stop(worker);
-}
-/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */