1/*
2 * Copyright © 2018 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtkmediastream.h"
23
24#include "gtkintl.h"
25
26/**
27 * GtkMediaStream:
28 *
29 * `GtkMediaStream` is the integration point for media playback inside GTK.
30 *
31 * GTK provides an implementation of the `GtkMediaStream` interface that
32 * is called [class@Gtk.MediaFile].
33 *
34 * Apart from application-facing API for stream playback, `GtkMediaStream`
35 * has a number of APIs that are only useful for implementations and should
36 * not be used in applications:
37 * [method@Gtk.MediaStream.prepared],
38 * [method@Gtk.MediaStream.unprepared],
39 * [method@Gtk.MediaStream.update],
40 * [method@Gtk.MediaStream.ended],
41 * [method@Gtk.MediaStream.seek_success],
42 * [method@Gtk.MediaStream.seek_failed],
43 * [method@Gtk.MediaStream.gerror],
44 * [method@Gtk.MediaStream.error],
45 * [method@Gtk.MediaStream.error_valist].
46 */
47
48typedef struct _GtkMediaStreamPrivate GtkMediaStreamPrivate;
49
50struct _GtkMediaStreamPrivate
51{
52 gint64 timestamp;
53 gint64 duration;
54 GError *error;
55 double volume;
56
57 guint has_audio : 1;
58 guint has_video : 1;
59 guint playing : 1;
60 guint ended : 1;
61 guint seekable : 1;
62 guint seeking : 1;
63 guint loop : 1;
64 guint prepared : 1;
65 guint muted : 1;
66};
67
68enum {
69 PROP_0,
70 PROP_PREPARED,
71 PROP_ERROR,
72 PROP_HAS_AUDIO,
73 PROP_HAS_VIDEO,
74 PROP_PLAYING,
75 PROP_ENDED,
76 PROP_TIMESTAMP,
77 PROP_DURATION,
78 PROP_SEEKABLE,
79 PROP_SEEKING,
80 PROP_LOOP,
81 PROP_MUTED,
82 PROP_VOLUME,
83
84 N_PROPS,
85};
86
87static GParamSpec *properties[N_PROPS] = { NULL, };
88
89static void
90gtk_media_stream_paintable_snapshot (GdkPaintable *paintable,
91 GdkSnapshot *snapshot,
92 double width,
93 double height)
94{
95}
96
97static void
98gtk_media_stream_paintable_init (GdkPaintableInterface *iface)
99{
100 /* We implement the behavior for "no video stream" here */
101 iface->snapshot = gtk_media_stream_paintable_snapshot;
102}
103
104G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkMediaStream, gtk_media_stream, G_TYPE_OBJECT,
105 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
106 gtk_media_stream_paintable_init)
107 G_ADD_PRIVATE (GtkMediaStream))
108
109#define GTK_MEDIA_STREAM_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
110 g_critical ("Media stream of type '%s' does not implement GtkMediaStream::" # method, G_OBJECT_TYPE_NAME (obj))
111
112static gboolean
113gtk_media_stream_default_play (GtkMediaStream *self)
114{
115 GTK_MEDIA_STREAM_WARN_NOT_IMPLEMENTED_METHOD (self, play);
116
117 return FALSE;
118}
119
120static void
121gtk_media_stream_default_pause (GtkMediaStream *self)
122{
123 GTK_MEDIA_STREAM_WARN_NOT_IMPLEMENTED_METHOD (self, pause);
124}
125
126static void
127gtk_media_stream_default_seek (GtkMediaStream *self,
128 gint64 timestamp)
129{
130 gtk_media_stream_seek_failed (self);
131}
132
133static void
134gtk_media_stream_default_update_audio (GtkMediaStream *self,
135 gboolean muted,
136 double volume)
137{
138}
139
140static void
141gtk_media_stream_default_realize (GtkMediaStream *self,
142 GdkSurface *surface)
143{
144}
145
146static void
147gtk_media_stream_default_unrealize (GtkMediaStream *self,
148 GdkSurface *surface)
149{
150}
151
152static void
153gtk_media_stream_set_property (GObject *object,
154 guint prop_id,
155 const GValue *value,
156 GParamSpec *pspec)
157
158{
159 GtkMediaStream *self = GTK_MEDIA_STREAM (ptr: object);
160
161 switch (prop_id)
162 {
163 case PROP_PLAYING:
164 gtk_media_stream_set_playing (self, playing: g_value_get_boolean (value));
165 break;
166
167 case PROP_LOOP:
168 gtk_media_stream_set_loop (self, loop: g_value_get_boolean (value));
169 break;
170
171 case PROP_MUTED:
172 gtk_media_stream_set_muted (self, muted: g_value_get_boolean (value));
173 break;
174
175 case PROP_VOLUME:
176 gtk_media_stream_set_volume (self, volume: g_value_get_double (value));
177 break;
178
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181 break;
182 }
183}
184
185static void
186gtk_media_stream_get_property (GObject *object,
187 guint prop_id,
188 GValue *value,
189 GParamSpec *pspec)
190{
191 GtkMediaStream *self = GTK_MEDIA_STREAM (ptr: object);
192 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
193
194 switch (prop_id)
195 {
196 case PROP_PREPARED:
197 g_value_set_boolean (value, v_boolean: priv->prepared);
198 break;
199
200 case PROP_ERROR:
201 g_value_set_boxed (value, v_boxed: priv->error);
202 break;
203
204 case PROP_HAS_AUDIO:
205 g_value_set_boolean (value, v_boolean: priv->has_audio);
206 break;
207
208 case PROP_HAS_VIDEO:
209 g_value_set_boolean (value, v_boolean: priv->has_video);
210 break;
211
212 case PROP_PLAYING:
213 g_value_set_boolean (value, v_boolean: priv->playing);
214 break;
215
216 case PROP_ENDED:
217 g_value_set_boolean (value, v_boolean: priv->ended);
218 break;
219
220 case PROP_TIMESTAMP:
221 g_value_set_int64 (value, v_int64: priv->timestamp);
222 break;
223
224 case PROP_DURATION:
225 g_value_set_int64 (value, v_int64: priv->duration);
226 break;
227
228 case PROP_SEEKABLE:
229 g_value_set_boolean (value, v_boolean: priv->seekable);
230 break;
231
232 case PROP_SEEKING:
233 g_value_set_boolean (value, v_boolean: priv->seeking);
234 break;
235
236 case PROP_LOOP:
237 g_value_set_boolean (value, v_boolean: priv->loop);
238 break;
239
240 case PROP_MUTED:
241 g_value_set_boolean (value, v_boolean: priv->muted);
242 break;
243
244 case PROP_VOLUME:
245 g_value_set_double (value, v_double: priv->volume);
246 break;
247
248 default:
249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250 break;
251 }
252}
253
254static void
255gtk_media_stream_dispose (GObject *object)
256{
257 GtkMediaStream *self = GTK_MEDIA_STREAM (ptr: object);
258 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
259
260 g_clear_error (err: &priv->error);
261
262 G_OBJECT_CLASS (gtk_media_stream_parent_class)->dispose (object);
263}
264
265static void
266gtk_media_stream_finalize (GObject *object)
267{
268#if 0
269 GtkMediaStream *self = GTK_MEDIA_STREAM (object);
270 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
271#endif
272
273 G_OBJECT_CLASS (gtk_media_stream_parent_class)->finalize (object);
274}
275
276static void
277gtk_media_stream_class_init (GtkMediaStreamClass *class)
278{
279 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
280
281 class->play = gtk_media_stream_default_play;
282 class->pause = gtk_media_stream_default_pause;
283 class->seek = gtk_media_stream_default_seek;
284 class->update_audio = gtk_media_stream_default_update_audio;
285 class->realize = gtk_media_stream_default_realize;
286 class->unrealize = gtk_media_stream_default_unrealize;
287
288 gobject_class->set_property = gtk_media_stream_set_property;
289 gobject_class->get_property = gtk_media_stream_get_property;
290 gobject_class->finalize = gtk_media_stream_finalize;
291 gobject_class->dispose = gtk_media_stream_dispose;
292
293 /**
294 * GtkMediaStream:prepared: (attributes org.gtk.Property.get=gtk_media_stream_is_prepared)
295 *
296 * Whether the stream has finished initializing and existence of
297 * audio and video is known.
298 */
299 properties[PROP_PREPARED] =
300 g_param_spec_boolean (name: "prepared",
301 P_("Prepared"),
302 P_("Whether the stream has finished initializing"),
303 FALSE,
304 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
305
306 /**
307 * GtkMediaStream:error: (attributes org.gtk.Property.get=gtk_media_stream_get_error)
308 *
309 * %NULL for a properly working stream or the `GError`
310 * that the stream is in.
311 */
312 properties[PROP_ERROR] =
313 g_param_spec_boxed (name: "error",
314 P_("Error"),
315 P_("Error the stream is in"),
316 G_TYPE_ERROR,
317 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
318
319 /**
320 * GtkMediaStream:has-audio: (attributes org.gtk.Property.get=gtk_media_stream_has_audio)
321 *
322 * Whether the stream contains audio.
323 */
324 properties[PROP_HAS_AUDIO] =
325 g_param_spec_boolean (name: "has-audio",
326 P_("Has audio"),
327 P_("Whether the stream contains audio"),
328 FALSE,
329 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
330
331 /**
332 * GtkMediaStream:has-video: (attributes org.gtk.Property.get=gtk_media_stream_has_video)
333 *
334 * Whether the stream contains video.
335 */
336 properties[PROP_HAS_VIDEO] =
337 g_param_spec_boolean (name: "has-video",
338 P_("Has video"),
339 P_("Whether the stream contains video"),
340 FALSE,
341 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
342
343 /**
344 * GtkMediaStream:playing: (attributes org.gtk.Property.get=gtk_media_stream_get_playing org.gtk.Property.set=gtk_media_stream_set_playing)
345 *
346 * Whether the stream is currently playing.
347 */
348 properties[PROP_PLAYING] =
349 g_param_spec_boolean (name: "playing",
350 P_("Playing"),
351 P_("Whether the stream is playing"),
352 FALSE,
353 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
354
355 /**
356 * GtkMediaStream:ended: (attributes org.gtk.Property.get=gtk_media_stream_get_ended)
357 *
358 * Set when playback has finished.
359 */
360 properties[PROP_ENDED] =
361 g_param_spec_boolean (name: "ended",
362 P_("Ended"),
363 P_("Set when playback has finished"),
364 FALSE,
365 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
366
367 /**
368 * GtkMediaStream:timestamp: (attributes org.gtk.Property.get=gtk_media_stream_get_timestamp)
369 *
370 * The current presentation timestamp in microseconds.
371 */
372 properties[PROP_TIMESTAMP] =
373 g_param_spec_int64 (name: "timestamp",
374 P_("Timestamp"),
375 P_("Timestamp in microseconds"),
376 minimum: 0, G_MAXINT64, default_value: 0,
377 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
378
379 /**
380 * GtkMediaStream:duration: (attributes org.gtk.Property.get=gtk_media_stream_get_duration)
381 *
382 * The stream's duration in microseconds or 0 if unknown.
383 */
384 properties[PROP_DURATION] =
385 g_param_spec_int64 (name: "duration",
386 P_("Duration"),
387 P_("Timestamp in microseconds"),
388 minimum: 0, G_MAXINT64, default_value: 0,
389 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
390
391 /**
392 * GtkMediaStream:seekable: (attributes org.gtk.Property.get=gtk_media_stream_is_seekable)
393 *
394 * Set unless the stream is known to not support seeking.
395 */
396 properties[PROP_SEEKABLE] =
397 g_param_spec_boolean (name: "seekable",
398 P_("Seekable"),
399 P_("Set unless seeking is not supported"),
400 TRUE,
401 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
402
403 /**
404 * GtkMediaStream:seeking: (attributes org.gtk.Property.get=gtk_media_stream_is_seeking)
405 *
406 * Set while a seek is in progress.
407 */
408 properties[PROP_SEEKING] =
409 g_param_spec_boolean (name: "seeking",
410 P_("Seeking"),
411 P_("Set while a seek is in progress"),
412 FALSE,
413 flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
414
415 /**
416 * GtkMediaStream:loop: (attributes org.gtk.Property.get=gtk_media_stream_get_loop org.gtk.Property.set=gtk_media_stream_set_loop)
417 *
418 * Try to restart the media from the beginning once it ended.
419 */
420 properties[PROP_LOOP] =
421 g_param_spec_boolean (name: "loop",
422 P_("Loop"),
423 P_("Try to restart the media from the beginning once it ended."),
424 FALSE,
425 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
426
427 /**
428 * GtkMediaStream:muted: (attributes org.gtk.Property.get=gtk_media_stream_get_muted org.gtk.Property.set=gtk_media_stream_set_muted)
429 *
430 * Whether the audio stream should be muted.
431 */
432 properties[PROP_MUTED] =
433 g_param_spec_boolean (name: "muted",
434 P_("Muted"),
435 P_("Whether the audio stream should be muted."),
436 FALSE,
437 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
438
439 /**
440 * GtkMediaStream:volume: (attributes org.gtk.Property.get=gtk_media_stream_get_volume org.gtk.Property.set=gtk_media_stream_set_volume)
441 *
442 * Volume of the audio stream.
443 */
444 properties[PROP_VOLUME] =
445 g_param_spec_double (name: "volume",
446 P_("Volume"),
447 P_("Volume of the audio stream."),
448 minimum: 0.0, maximum: 1.0, default_value: 1.0,
449 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
450
451 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties);
452}
453
454static void
455gtk_media_stream_init (GtkMediaStream *self)
456{
457 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
458
459 priv->volume = 1.0;
460}
461
462/**
463 * gtk_media_stream_is_prepared: (attributes org.gtk.Method.get_property=prepared)
464 * @self: a `GtkMediaStream`
465 *
466 * Returns whether the stream has finished initializing.
467 *
468 * At this point the existence of audio and video is known.
469 *
470 * Returns: %TRUE if the stream is prepared
471 */
472gboolean
473gtk_media_stream_is_prepared (GtkMediaStream *self)
474{
475 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
476
477 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
478
479 return priv->prepared;
480}
481
482/**
483 * gtk_media_stream_has_audio: (attributes org.gtk.Method.get_property=has-audio)
484 * @self: a `GtkMediaStream`
485 *
486 * Returns whether the stream has audio.
487 *
488 * Returns: %TRUE if the stream has audio
489 */
490gboolean
491gtk_media_stream_has_audio (GtkMediaStream *self)
492{
493 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
494
495 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
496
497 return priv->has_audio;
498}
499
500/**
501 * gtk_media_stream_has_video: (attributes org.gtk.Method.get_property=has-video)
502 * @self: a `GtkMediaStream`
503 *
504 * Returns whether the stream has video.
505 *
506 * Returns: %TRUE if the stream has video
507 */
508gboolean
509gtk_media_stream_has_video (GtkMediaStream *self)
510{
511 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
512
513 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
514
515 return priv->has_video;
516}
517
518/**
519 * gtk_media_stream_play:
520 * @self: a `GtkMediaStream`
521 *
522 * Starts playing the stream.
523 *
524 * If the stream is in error or already playing, do nothing.
525 */
526void
527gtk_media_stream_play (GtkMediaStream *self)
528{
529 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
530
531 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
532
533 if (priv->error)
534 return;
535
536 if (priv->playing)
537 return;
538
539 if (GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->play (self))
540 {
541 g_object_freeze_notify (G_OBJECT (self));
542
543 priv->playing = TRUE;
544 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PLAYING]);
545
546 if (priv->ended)
547 {
548 priv->ended = FALSE;
549 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ENDED]);
550 }
551
552 g_object_thaw_notify (G_OBJECT (self));
553 }
554}
555
556/**
557 * gtk_media_stream_pause:
558 * @self: a `GtkMediaStream`
559 *
560 * Pauses playback of the stream.
561 *
562 * If the stream is not playing, do nothing.
563 */
564void
565gtk_media_stream_pause (GtkMediaStream *self)
566{
567 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
568
569 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
570
571 /* Don't check for error here because we call this function right
572 * after setting the error to pause the stream */
573
574 if (!priv->playing)
575 return;
576
577 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->pause (self);
578
579 priv->playing = FALSE;
580 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PLAYING]);
581}
582
583/**
584 * gtk_media_stream_get_playing: (attributes org.gtk.Method.get_property=playing)
585 * @self: a `GtkMediaStream`
586 *
587 * Return whether the stream is currently playing.
588 *
589 * Returns: %TRUE if the stream is playing
590 */
591gboolean
592gtk_media_stream_get_playing (GtkMediaStream *self)
593{
594 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
595
596 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
597
598 return priv->playing;
599}
600
601/**
602 * gtk_media_stream_set_playing: (attributes org.gtk.Method.set_property=playing)
603 * @self: a `GtkMediaStream`
604 * @playing: whether to start or pause playback
605 *
606 * Starts or pauses playback of the stream.
607 */
608void
609gtk_media_stream_set_playing (GtkMediaStream *self,
610 gboolean playing)
611{
612 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
613
614 if (playing)
615 gtk_media_stream_play (self);
616 else
617 gtk_media_stream_pause (self);
618}
619
620/**
621 * gtk_media_stream_get_ended: (attributes org.gtk.Method.get_property=ended)
622 * @self: a `GtkMediaStream`
623 *
624 * Returns whether the streams playback is finished.
625 *
626 * Returns: %TRUE if playback is finished
627 */
628gboolean
629gtk_media_stream_get_ended (GtkMediaStream *self)
630{
631 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
632
633 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
634
635 return priv->ended;
636}
637
638/**
639 * gtk_media_stream_get_timestamp: (attributes org.gtk.Method.get_property=timestamp)
640 * @self: a `GtkMediaStream`
641 *
642 * Returns the current presentation timestamp in microseconds.
643 *
644 * Returns: the timestamp in microseconds
645 */
646gint64
647gtk_media_stream_get_timestamp (GtkMediaStream *self)
648{
649 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
650
651 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
652
653 return priv->timestamp;
654}
655
656/**
657 * gtk_media_stream_get_duration: (attributes org.gtk.Method.get_property=duration)
658 * @self: a `GtkMediaStream`
659 *
660 * Gets the duration of the stream.
661 *
662 * If the duration is not known, 0 will be returned.
663 *
664 * Returns: the duration of the stream or 0 if not known.
665 */
666gint64
667gtk_media_stream_get_duration (GtkMediaStream *self)
668{
669 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
670
671 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
672
673 return priv->duration;
674}
675
676/**
677 * gtk_media_stream_is_seekable: (attributes org.gtk.Method.get_property=seekable)
678 * @self: a `GtkMediaStream`
679 *
680 * Checks if a stream may be seekable.
681 *
682 * This is meant to be a hint. Streams may not allow seeking even if
683 * this function returns %TRUE. However, if this function returns
684 * %FALSE, streams are guaranteed to not be seekable and user interfaces
685 * may hide controls that allow seeking.
686 *
687 * It is allowed to call [method@Gtk.MediaStream.seek] on a non-seekable
688 * stream, though it will not do anything.
689 *
690 * Returns: %TRUE if the stream may support seeking
691 */
692gboolean
693gtk_media_stream_is_seekable (GtkMediaStream *self)
694{
695 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
696
697 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
698
699 return priv->seekable;
700}
701
702/**
703 * gtk_media_stream_is_seeking: (attributes org.gtk.Method.get_property=seeking)
704 * @self: a `GtkMediaStream`
705 *
706 * Checks if there is currently a seek operation going on.
707 *
708 * Returns: %TRUE if a seek operation is ongoing.
709 */
710gboolean
711gtk_media_stream_is_seeking (GtkMediaStream *self)
712{
713 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
714
715 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
716
717 return priv->seeking;
718}
719
720/**
721 * gtk_media_stream_get_error: (attributes org.gtk.Method.get_property=error)
722 * @self: a `GtkMediaStream`
723 *
724 * If the stream is in an error state, returns the `GError`
725 * explaining that state.
726 *
727 * Any type of error can be reported here depending on the
728 * implementation of the media stream.
729 *
730 * A media stream in an error cannot be operated on, calls
731 * like [method@Gtk.MediaStream.play] or
732 * [method@Gtk.MediaStream.seek] will not have any effect.
733 *
734 * `GtkMediaStream` itself does not provide a way to unset
735 * an error, but implementations may provide options. For example,
736 * a [class@Gtk.MediaFile] will unset errors when a new source is
737 * set, e.g. with [method@Gtk.MediaFile.set_file].
738 *
739 * Returns: (nullable) (transfer none): %NULL if not in an
740 * error state or the `GError` of the stream
741 */
742const GError *
743gtk_media_stream_get_error (GtkMediaStream *self)
744{
745 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
746
747 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
748
749 return priv->error;
750}
751
752/**
753 * gtk_media_stream_seek:
754 * @self: a `GtkMediaStream`
755 * @timestamp: timestamp to seek to.
756 *
757 * Start a seek operation on @self to @timestamp.
758 *
759 * If @timestamp is out of range, it will be clamped.
760 *
761 * Seek operations may not finish instantly. While a
762 * seek operation is in process, the [property@Gtk.MediaStream:seeking]
763 * property will be set.
764 *
765 * When calling gtk_media_stream_seek() during an
766 * ongoing seek operation, the new seek will override
767 * any pending seek.
768 */
769void
770gtk_media_stream_seek (GtkMediaStream *self,
771 gint64 timestamp)
772{
773 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
774 gboolean was_seeking;
775
776 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
777 g_return_if_fail (timestamp >= 0);
778
779 if (priv->error)
780 return;
781
782 if (!priv->seekable)
783 return;
784
785 g_object_freeze_notify (G_OBJECT (self));
786
787 was_seeking = priv->seeking;
788 priv->seeking = TRUE;
789
790 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->seek (self, timestamp);
791
792 if (was_seeking != priv->seeking)
793 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKING]);
794
795 g_object_thaw_notify (G_OBJECT (self));
796}
797
798/**
799 * gtk_media_stream_get_loop: (attributes org.gtk.Method.get_property=loop)
800 * @self: a `GtkMediaStream`
801 *
802 * Returns whether the stream is set to loop.
803 *
804 * See [method@Gtk.MediaStream.set_loop] for details.
805 *
806 * Returns: %TRUE if the stream should loop
807 */
808gboolean
809gtk_media_stream_get_loop (GtkMediaStream *self)
810{
811 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
812
813 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
814
815 return priv->loop;
816}
817
818/**
819 * gtk_media_stream_set_loop: (attributes org.gtk.Method.set_property=loop)
820 * @self: a `GtkMediaStream`
821 * @loop: %TRUE if the stream should loop
822 *
823 * Sets whether the stream should loop.
824 *
825 * In this case, it will attempt to restart playback
826 * from the beginning instead of stopping at the end.
827 *
828 * Not all streams may support looping, in particular
829 * non-seekable streams. Those streams will ignore the
830 * loop setting and just end.
831 */
832void
833gtk_media_stream_set_loop (GtkMediaStream *self,
834 gboolean loop)
835{
836 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
837
838 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
839
840 if (priv->loop == loop)
841 return;
842
843 priv->loop = loop;
844 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_LOOP]);
845}
846
847/**
848 * gtk_media_stream_get_muted: (attributes org.gtk.Method.get_property=muted)
849 * @self: a `GtkMediaStream`
850 *
851 * Returns whether the audio for the stream is muted.
852 *
853 * See [method@Gtk.MediaStream.set_muted] for details.
854 *
855 * Returns: %TRUE if the stream is muted
856 */
857gboolean
858gtk_media_stream_get_muted (GtkMediaStream *self)
859{
860 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
861
862 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
863
864 return priv->muted;
865}
866
867/**
868 * gtk_media_stream_set_muted: (attributes org.gtk.Method.set_property=muted)
869 * @self: a `GtkMediaStream`
870 * @muted: %TRUE if the stream should be muted
871 *
872 * Sets whether the audio stream should be muted.
873 *
874 * Muting a stream will cause no audio to be played, but it
875 * does not modify the volume. This means that muting and
876 * then unmuting the stream will restore the volume settings.
877 *
878 * If the stream has no audio, calling this function will
879 * still work but it will not have an audible effect.
880 */
881void
882gtk_media_stream_set_muted (GtkMediaStream *self,
883 gboolean muted)
884{
885 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
886
887 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
888
889 if (priv->muted == muted)
890 return;
891
892 priv->muted = muted;
893
894 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->update_audio (self, priv->muted, priv->volume);
895
896 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_MUTED]);
897}
898
899/**
900 * gtk_media_stream_get_volume: (attributes org.gtk.Method.get_property=volume)
901 * @self: a `GtkMediaStream`
902 *
903 * Returns the volume of the audio for the stream.
904 *
905 * See [method@Gtk.MediaStream.set_volume] for details.
906 *
907 * Returns: volume of the stream from 0.0 to 1.0
908 */
909double
910gtk_media_stream_get_volume (GtkMediaStream *self)
911{
912 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
913
914 g_return_val_if_fail (GTK_IS_MEDIA_STREAM (self), FALSE);
915
916 return priv->volume;
917}
918
919/**
920 * gtk_media_stream_set_volume: (attributes org.gtk.Method.set_property=volume)
921 * @self: a `GtkMediaStream`
922 * @volume: New volume of the stream from 0.0 to 1.0
923 *
924 * Sets the volume of the audio stream.
925 *
926 * This function call will work even if the stream is muted.
927 *
928 * The given @volume should range from 0.0 for silence to 1.0
929 * for as loud as possible. Values outside of this range will
930 * be clamped to the nearest value.
931 *
932 * If the stream has no audio or is muted, calling this function
933 * will still work but it will not have an immediate audible effect.
934 * When the stream is unmuted, the new volume setting will take effect.
935 */
936void
937gtk_media_stream_set_volume (GtkMediaStream *self,
938 double volume)
939{
940 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
941
942 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
943
944 volume = CLAMP (volume, 0.0, 1.0);
945
946 if (priv->volume == volume)
947 return;
948
949 priv->volume = volume;
950
951 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->update_audio (self, priv->muted, priv->volume);
952
953 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_VOLUME]);
954}
955
956/**
957 * gtk_media_stream_realize:
958 * @self: a `GtkMediaStream`
959 * @surface: a `GdkSurface`
960 *
961 * Called by users to attach the media stream to a `GdkSurface` they manage.
962 *
963 * The stream can then access the resources of @surface for its
964 * rendering purposes. In particular, media streams might want to
965 * create a `GdkGLContext` or sync to the `GdkFrameClock`.
966 *
967 * Whoever calls this function is responsible for calling
968 * [method@Gtk.MediaStream.unrealize] before either the stream
969 * or @surface get destroyed.
970 *
971 * Multiple calls to this function may happen from different
972 * users of the video, even with the same @surface. Each of these
973 * calls must be followed by its own call to
974 * [method@Gtk.MediaStream.unrealize].
975 *
976 * It is not required to call this function to make a media stream work.
977 */
978void
979gtk_media_stream_realize (GtkMediaStream *self,
980 GdkSurface *surface)
981{
982 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
983 g_return_if_fail (GDK_IS_SURFACE (surface));
984
985 g_object_ref (self);
986 g_object_ref (surface);
987
988 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->realize (self, surface);
989}
990
991/**
992 * gtk_media_stream_unrealize:
993 * @self: a `GtkMediaStream` previously realized
994 * @surface: the `GdkSurface` the stream was realized with
995 *
996 * Undoes a previous call to gtk_media_stream_realize().
997 *
998 * This causes the stream to release all resources it had
999 * allocated from @surface.
1000 */
1001void
1002gtk_media_stream_unrealize (GtkMediaStream *self,
1003 GdkSurface *surface)
1004{
1005 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1006 g_return_if_fail (GDK_IS_SURFACE (surface));
1007
1008 GTK_MEDIA_STREAM_GET_CLASS (ptr: self)->unrealize (self, surface);
1009
1010 g_object_unref (object: surface);
1011 g_object_unref (object: self);
1012}
1013
1014/**
1015 * gtk_media_stream_stream_prepared:
1016 * @self: a `GtkMediaStream`
1017 * @has_audio: %TRUE if the stream should advertise audio support
1018 * @has_video: %TRUE if the stream should advertise video support
1019 * @seekable: %TRUE if the stream should advertise seekability
1020 * @duration: The duration of the stream or 0 if unknown
1021 *
1022 * Called by `GtkMediaStream` implementations to advertise the stream
1023 * being ready to play and providing details about the stream.
1024 *
1025 * Note that the arguments are hints. If the stream implementation
1026 * cannot determine the correct values, it is better to err on the
1027 * side of caution and return %TRUE. User interfaces will use those
1028 * values to determine what controls to show.
1029 *
1030 * This function may not be called again until the stream has been
1031 * reset via [method@Gtk.MediaStream.stream_unprepared].
1032 *
1033 * Since: 4.4
1034 */
1035void
1036gtk_media_stream_stream_prepared (GtkMediaStream *self,
1037 gboolean has_audio,
1038 gboolean has_video,
1039 gboolean seekable,
1040 gint64 duration)
1041{
1042 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1043
1044 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1045 g_return_if_fail (!gtk_media_stream_is_prepared (self));
1046
1047 g_object_freeze_notify (G_OBJECT (self));
1048
1049 if (priv->has_audio != has_audio)
1050 {
1051 priv->has_audio = has_audio;
1052 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_HAS_AUDIO]);
1053 }
1054 if (priv->has_video != has_video)
1055 {
1056 priv->has_video = has_video;
1057 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_HAS_VIDEO]);
1058 }
1059 if (priv->seekable != seekable)
1060 {
1061 priv->seekable = seekable;
1062 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKABLE]);
1063 }
1064 if (priv->duration != duration)
1065 {
1066 priv->duration = duration;
1067 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_DURATION]);
1068 }
1069
1070 priv->prepared = TRUE;
1071 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PREPARED]);
1072
1073 g_object_thaw_notify (G_OBJECT (self));
1074}
1075
1076/**
1077 * gtk_media_stream_stream_unprepared:
1078 * @self: a `GtkMediaStream`
1079 *
1080 * Resets a given media stream implementation.
1081 *
1082 * [method@Gtk.MediaStream.stream_prepared] can then be called again.
1083 *
1084 * This function will also reset any error state the stream was in.
1085 *
1086 * Since: 4.4
1087 */
1088void
1089gtk_media_stream_stream_unprepared (GtkMediaStream *self)
1090{
1091 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1092
1093 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1094 g_return_if_fail (gtk_media_stream_is_prepared (self));
1095
1096 g_object_freeze_notify (G_OBJECT (self));
1097
1098 gtk_media_stream_pause (self);
1099
1100 if (priv->has_audio)
1101 {
1102 priv->has_audio = FALSE;
1103 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_HAS_AUDIO]);
1104 }
1105 if (priv->has_video)
1106 {
1107 priv->has_video = FALSE;
1108 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_HAS_VIDEO]);
1109 }
1110 if (priv->seekable)
1111 {
1112 priv->seekable = FALSE;
1113 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKABLE]);
1114 }
1115 if (priv->seeking)
1116 {
1117 priv->seeking = FALSE;
1118 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKING]);
1119 }
1120 if (priv->duration != 0)
1121 {
1122 priv->duration = 0;
1123 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_DURATION]);
1124 }
1125 if (priv->timestamp != 0)
1126 {
1127 priv->timestamp = 0;
1128 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_TIMESTAMP]);
1129 }
1130 if (priv->error)
1131 {
1132 g_clear_error (err: &priv->error);
1133 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ERROR]);
1134 }
1135
1136 priv->prepared = FALSE;
1137 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PREPARED]);
1138
1139 g_object_thaw_notify (G_OBJECT (self));
1140}
1141
1142/**
1143 * gtk_media_stream_prepared: (skip)
1144 * @self: a `GtkMediaStream`
1145 * @has_audio: %TRUE if the stream should advertise audio support
1146 * @has_video: %TRUE if the stream should advertise video support
1147 * @seekable: %TRUE if the stream should advertise seekability
1148 * @duration: The duration of the stream or 0 if unknown
1149 *
1150 * Same as gtk_media_stream_stream_prepared().
1151 *
1152 * Deprecated: 4.4: Use [method@Gtk.MediaStream.stream_prepared] instead.
1153 */
1154void
1155gtk_media_stream_prepared (GtkMediaStream *self,
1156 gboolean has_audio,
1157 gboolean has_video,
1158 gboolean seekable,
1159 gint64 duration)
1160{
1161 gtk_media_stream_stream_prepared (self, has_audio, has_video, seekable, duration);
1162}
1163
1164/**
1165 * gtk_media_stream_unprepared: (skip)
1166 * @self: a `GtkMediaStream`
1167 *
1168 * Same as gtk_media_stream_stream_unprepared().
1169 *
1170 * Deprecated: 4.4: Use [method@Gtk.MediaStream.stream_unprepared] instead.
1171 */
1172void
1173gtk_media_stream_unprepared (GtkMediaStream *self)
1174{
1175 gtk_media_stream_stream_unprepared (self);
1176}
1177
1178/**
1179 * gtk_media_stream_gerror:
1180 * @self: a `GtkMediaStream`
1181 * @error: (transfer full): the `GError` to set
1182 *
1183 * Sets @self into an error state.
1184 *
1185 * This will pause the stream (you can check for an error
1186 * via [method@Gtk.MediaStream.get_error] in your
1187 * GtkMediaStream.pause() implementation), abort pending
1188 * seeks and mark the stream as prepared.
1189 *
1190 * if the stream is already in an error state, this call
1191 * will be ignored and the existing error will be retained.
1192 *
1193 * To unset an error, the stream must be reset via a call to
1194 * [method@Gtk.MediaStream.unprepared].
1195 */
1196void
1197gtk_media_stream_gerror (GtkMediaStream *self,
1198 GError *error)
1199{
1200 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1201
1202 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1203 g_return_if_fail (error != NULL);
1204
1205 if (priv->error)
1206 {
1207 g_error_free (error);
1208 return;
1209 }
1210
1211 g_object_freeze_notify (G_OBJECT (self));
1212
1213 priv->error = error;
1214
1215 gtk_media_stream_pause (self);
1216
1217 if (!priv->prepared)
1218 {
1219 priv->prepared = TRUE;
1220 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_PREPARED]);
1221 }
1222
1223 if (priv->seeking)
1224 gtk_media_stream_seek_failed (self);
1225
1226 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ERROR]);
1227
1228 g_object_thaw_notify (G_OBJECT (self));
1229}
1230
1231/**
1232 * gtk_media_stream_error:
1233 * @self: a `GtkMediaStream`
1234 * @domain: error domain
1235 * @code: error code
1236 * @format: printf()-style format for error message
1237 * @...: parameters for message format
1238 *
1239 * Sets @self into an error state using a printf()-style format string.
1240 *
1241 * This is a utility function that calls [method@Gtk.MediaStream.gerror].
1242 * See that function for details.
1243 */
1244void
1245gtk_media_stream_error (GtkMediaStream *self,
1246 GQuark domain,
1247 int code,
1248 const char *format,
1249 ...)
1250{
1251 GError *error;
1252 va_list args;
1253
1254 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1255 g_return_if_fail (domain != 0);
1256 g_return_if_fail (format != NULL);
1257
1258 va_start (args, format);
1259 error = g_error_new_valist (domain, code, format, args);
1260 va_end (args);
1261
1262 gtk_media_stream_gerror (self, error);
1263}
1264
1265/**
1266 * gtk_media_stream_error_valist:
1267 * @self: a `GtkMediaStream`
1268 * @domain: error domain
1269 * @code: error code
1270 * @format: printf()-style format for error message
1271 * @args: `va_list` of parameters for the message format
1272 *
1273 * Sets @self into an error state using a printf()-style format string.
1274 *
1275 * This is a utility function that calls [method@Gtk.MediaStream.gerror].
1276 * See that function for details.
1277 */
1278void
1279gtk_media_stream_error_valist (GtkMediaStream *self,
1280 GQuark domain,
1281 int code,
1282 const char *format,
1283 va_list args)
1284{
1285 GError *error;
1286
1287 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1288 g_return_if_fail (domain != 0);
1289 g_return_if_fail (format != NULL);
1290
1291 error = g_error_new_valist (domain, code, format, args);
1292
1293 gtk_media_stream_gerror (self, error);
1294}
1295
1296/**
1297 * gtk_media_stream_update:
1298 * @self: a `GtkMediaStream`
1299 * @timestamp: the new timestamp
1300 *
1301 * Media stream implementations should regularly call this
1302 * function to update the timestamp reported by the stream.
1303 *
1304 * It is up to implementations to call this at the frequency
1305 * they deem appropriate.
1306 *
1307 * The media stream must be prepared when this function is called.
1308 */
1309void
1310gtk_media_stream_update (GtkMediaStream *self,
1311 gint64 timestamp)
1312{
1313 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1314
1315 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1316 g_return_if_fail (gtk_media_stream_is_prepared (self));
1317
1318 g_object_freeze_notify (G_OBJECT (self));
1319
1320 /* Timestamp before duration is important here.
1321 * This way the duration notify will be emitted first which will
1322 * make GtkMediaControls update adjustment->upper so that the
1323 * timestamp notify will cause the timestamp to not be clamped.
1324 */
1325 if (priv->timestamp != timestamp)
1326 {
1327 priv->timestamp = timestamp;
1328 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_TIMESTAMP]);
1329 }
1330 if (priv->duration > 0 && timestamp > priv->duration)
1331 {
1332 priv->duration = priv->timestamp;
1333 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_DURATION]);
1334 }
1335
1336 g_object_thaw_notify (G_OBJECT (self));
1337}
1338
1339/**
1340 * gtk_media_stream_stream_ended:
1341 * @self: a `GtkMediaStream`
1342 *
1343 * Pauses the media stream and marks it as ended.
1344 *
1345 * This is a hint only, calls to [method@Gtk.MediaStream.play]
1346 * may still happen.
1347 *
1348 * The media stream must be prepared when this function is called.
1349 *
1350 * Since: 4.4
1351 */
1352void
1353gtk_media_stream_stream_ended (GtkMediaStream *self)
1354{
1355 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1356
1357 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1358 g_return_if_fail (gtk_media_stream_is_prepared (self));
1359 g_return_if_fail (!gtk_media_stream_get_ended (self));
1360
1361 g_object_freeze_notify (G_OBJECT (self));
1362
1363 gtk_media_stream_pause (self);
1364
1365 priv->ended = TRUE;
1366 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ENDED]);
1367
1368 g_object_thaw_notify (G_OBJECT (self));
1369}
1370
1371/**
1372 * gtk_media_stream_ended: (skip)
1373 * @self: a `GtkMediaStream`
1374 *
1375 * Pauses the media stream and marks it as ended.
1376 *
1377 * This is a hint only, calls to [method@Gtk.MediaStream.play]
1378 * may still happen.
1379 *
1380 * The media stream must be prepared when this function is called.
1381 *
1382 * Deprecated: 4.4: Use [method@Gtk.MediaStream.stream_ended] instead
1383 */
1384void
1385gtk_media_stream_ended (GtkMediaStream *self)
1386{
1387 gtk_media_stream_stream_ended (self);
1388}
1389
1390/**
1391 * gtk_media_stream_seek_success:
1392 * @self: a `GtkMediaStream`
1393 *
1394 * Ends a seek operation started via GtkMediaStream.seek() successfully.
1395 *
1396 * This function will unset the GtkMediaStream:ended property
1397 * if it was set.
1398 *
1399 * See [method@Gtk.MediaStream.seek_failed] for the other way of
1400 * ending a seek.
1401 */
1402void
1403gtk_media_stream_seek_success (GtkMediaStream *self)
1404{
1405 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1406
1407 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1408 g_return_if_fail (gtk_media_stream_is_seeking (self));
1409
1410 g_object_freeze_notify (G_OBJECT (self));
1411
1412 priv->seeking = FALSE;
1413 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKING]);
1414
1415 if (priv->ended)
1416 {
1417 priv->ended = FALSE;
1418 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ENDED]);
1419 }
1420
1421 g_object_thaw_notify (G_OBJECT (self));
1422}
1423
1424/**
1425 * gtk_media_stream_seek_failed:
1426 * @self: a `GtkMediaStream`
1427 *
1428 * Ends a seek operation started via GtkMediaStream.seek() as a failure.
1429 *
1430 * This will not cause an error on the stream and will assume that
1431 * playback continues as if no seek had happened.
1432 *
1433 * See [method@Gtk.MediaStream.seek_success] for the other way of
1434 * ending a seek.
1435 */
1436void
1437gtk_media_stream_seek_failed (GtkMediaStream *self)
1438{
1439 GtkMediaStreamPrivate *priv = gtk_media_stream_get_instance_private (self);
1440
1441 g_return_if_fail (GTK_IS_MEDIA_STREAM (self));
1442 g_return_if_fail (gtk_media_stream_is_seeking (self));
1443
1444 priv->seeking = FALSE;
1445
1446 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SEEKING]);
1447}
1448

source code of gtk/gtk/gtkmediastream.c