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 | |
48 | typedef struct _GtkMediaStreamPrivate GtkMediaStreamPrivate; |
49 | |
50 | struct _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 | |
68 | enum { |
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 | |
87 | static GParamSpec *properties[N_PROPS] = { NULL, }; |
88 | |
89 | static void |
90 | gtk_media_stream_paintable_snapshot (GdkPaintable *paintable, |
91 | GdkSnapshot *snapshot, |
92 | double width, |
93 | double height) |
94 | { |
95 | } |
96 | |
97 | static void |
98 | gtk_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 | |
104 | G_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 | |
112 | static gboolean |
113 | gtk_media_stream_default_play (GtkMediaStream *self) |
114 | { |
115 | GTK_MEDIA_STREAM_WARN_NOT_IMPLEMENTED_METHOD (self, play); |
116 | |
117 | return FALSE; |
118 | } |
119 | |
120 | static void |
121 | gtk_media_stream_default_pause (GtkMediaStream *self) |
122 | { |
123 | GTK_MEDIA_STREAM_WARN_NOT_IMPLEMENTED_METHOD (self, pause); |
124 | } |
125 | |
126 | static void |
127 | gtk_media_stream_default_seek (GtkMediaStream *self, |
128 | gint64 timestamp) |
129 | { |
130 | gtk_media_stream_seek_failed (self); |
131 | } |
132 | |
133 | static void |
134 | gtk_media_stream_default_update_audio (GtkMediaStream *self, |
135 | gboolean muted, |
136 | double volume) |
137 | { |
138 | } |
139 | |
140 | static void |
141 | gtk_media_stream_default_realize (GtkMediaStream *self, |
142 | GdkSurface *surface) |
143 | { |
144 | } |
145 | |
146 | static void |
147 | gtk_media_stream_default_unrealize (GtkMediaStream *self, |
148 | GdkSurface *surface) |
149 | { |
150 | } |
151 | |
152 | static void |
153 | gtk_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 | |
185 | static void |
186 | gtk_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 | |
254 | static void |
255 | gtk_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 | |
265 | static void |
266 | gtk_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 | |
276 | static void |
277 | gtk_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 | |
454 | static void |
455 | gtk_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 | */ |
472 | gboolean |
473 | gtk_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 | */ |
490 | gboolean |
491 | gtk_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 | */ |
508 | gboolean |
509 | gtk_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 | */ |
526 | void |
527 | gtk_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 | */ |
564 | void |
565 | gtk_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 | */ |
591 | gboolean |
592 | gtk_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 | */ |
608 | void |
609 | gtk_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 | */ |
628 | gboolean |
629 | gtk_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 | */ |
646 | gint64 |
647 | gtk_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 | */ |
666 | gint64 |
667 | gtk_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 | */ |
692 | gboolean |
693 | gtk_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 | */ |
710 | gboolean |
711 | gtk_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 | */ |
742 | const GError * |
743 | gtk_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 | */ |
769 | void |
770 | gtk_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 | */ |
808 | gboolean |
809 | gtk_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 | */ |
832 | void |
833 | gtk_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 | */ |
857 | gboolean |
858 | gtk_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 | */ |
881 | void |
882 | gtk_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 | */ |
909 | double |
910 | gtk_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 | */ |
936 | void |
937 | gtk_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 | */ |
978 | void |
979 | gtk_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 | */ |
1001 | void |
1002 | gtk_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 | */ |
1035 | void |
1036 | gtk_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 | */ |
1088 | void |
1089 | gtk_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 | */ |
1154 | void |
1155 | gtk_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 | */ |
1172 | void |
1173 | gtk_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 | */ |
1196 | void |
1197 | gtk_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 | */ |
1244 | void |
1245 | gtk_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 | */ |
1278 | void |
1279 | gtk_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 | */ |
1309 | void |
1310 | gtk_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 | */ |
1352 | void |
1353 | gtk_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 | */ |
1384 | void |
1385 | gtk_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 | */ |
1402 | void |
1403 | gtk_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 | */ |
1436 | void |
1437 | gtk_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 | |