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 "gtkffmediafileprivate.h"
23
24 #include "gtkintl.h"
25
26 #include "gdk/gdkmemorytextureprivate.h"
27
28 #include <libavcodec/avcodec.h>
29 #include <libavformat/avformat.h>
30 #include <libavutil/pixdesc.h>
31 #include <libswscale/swscale.h>
32
33 typedef struct _GtkVideoFrameFFMpeg GtkVideoFrameFFMpeg;
34
35 struct _GtkVideoFrameFFMpeg
36 {
37 GdkTexture *texture;
38 gint64 timestamp;
39 };
40
41 struct _GtkFfMediaFile
42 {
43 GtkMediaFile parent_instance;
44
45 GFile *file;
46 GInputStream *input_stream;
47
48 AVFormatContext *format_ctx;
49 AVCodecContext *codec_ctx;
50 int stream_id;
51 struct SwsContext *sws_ctx;
52 enum AVPixelFormat sws_pix_fmt;
53 GdkMemoryFormat memory_format;
54
55 GtkVideoFrameFFMpeg current_frame;
56 GtkVideoFrameFFMpeg next_frame;
57
58 gint64 start_time; /* monotonic time when we displayed the last frame */
59 guint next_frame_cb; /* Source ID of next frame callback */
60 };
61
62 struct _GtkFfMediaFileClass
63 {
64 GtkMediaFileClass parent_class;
65 };
66
67 static void
gtk_video_frame_ffmpeg_init(GtkVideoFrameFFMpeg * frame,GdkTexture * texture,gint64 timestamp)68 gtk_video_frame_ffmpeg_init (GtkVideoFrameFFMpeg *frame,
69 GdkTexture *texture,
70 gint64 timestamp)
71 {
72 frame->texture = texture;
73 frame->timestamp = timestamp;
74 }
75
76 static void
gtk_video_frame_ffmpeg_clear(GtkVideoFrameFFMpeg * frame)77 gtk_video_frame_ffmpeg_clear (GtkVideoFrameFFMpeg *frame)
78 {
79 g_clear_object (&frame->texture);
80 frame->timestamp = 0;
81 }
82
83 static gboolean
gtk_video_frame_ffmpeg_is_empty(GtkVideoFrameFFMpeg * frame)84 gtk_video_frame_ffmpeg_is_empty (GtkVideoFrameFFMpeg *frame)
85 {
86 return frame->texture == NULL;
87 }
88
89 static void
gtk_video_frame_ffmpeg_move(GtkVideoFrameFFMpeg * dest,GtkVideoFrameFFMpeg * src)90 gtk_video_frame_ffmpeg_move (GtkVideoFrameFFMpeg *dest,
91 GtkVideoFrameFFMpeg *src)
92 {
93 *dest = *src;
94 src->texture = NULL;
95 src->timestamp = 0;
96 }
97
98 static void
gtk_ff_media_file_paintable_snapshot(GdkPaintable * paintable,GdkSnapshot * snapshot,double width,double height)99 gtk_ff_media_file_paintable_snapshot (GdkPaintable *paintable,
100 GdkSnapshot *snapshot,
101 double width,
102 double height)
103 {
104 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (paintable);
105
106 if (!gtk_video_frame_ffmpeg_is_empty (&video->current_frame))
107 {
108 gdk_paintable_snapshot (GDK_PAINTABLE (video->current_frame.texture), snapshot, width, height);
109 }
110 }
111
112 static GdkPaintable *
gtk_ff_media_file_paintable_get_current_image(GdkPaintable * paintable)113 gtk_ff_media_file_paintable_get_current_image (GdkPaintable *paintable)
114 {
115 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (paintable);
116
117 if (gtk_video_frame_ffmpeg_is_empty (&video->current_frame))
118 {
119 if (video->codec_ctx)
120 return gdk_paintable_new_empty (video->codec_ctx->width, video->codec_ctx->height);
121 else
122 return gdk_paintable_new_empty (0, 0);
123 }
124
125 return GDK_PAINTABLE (g_object_ref (video->current_frame.texture));
126 }
127
128 static int
gtk_ff_media_file_paintable_get_intrinsic_width(GdkPaintable * paintable)129 gtk_ff_media_file_paintable_get_intrinsic_width (GdkPaintable *paintable)
130 {
131 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (paintable);
132
133 if (video->codec_ctx)
134 return video->codec_ctx->width;
135
136 return 0;
137 }
138
139 static int
gtk_ff_media_file_paintable_get_intrinsic_height(GdkPaintable * paintable)140 gtk_ff_media_file_paintable_get_intrinsic_height (GdkPaintable *paintable)
141 {
142 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (paintable);
143
144 if (video->codec_ctx)
145 return video->codec_ctx->height;
146
147 return 0;
148 }
149
gtk_ff_media_file_paintable_get_intrinsic_aspect_ratio(GdkPaintable * paintable)150 static double gtk_ff_media_file_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
151 {
152 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (paintable);
153
154 if (video->codec_ctx)
155 return (double) video->codec_ctx->width / video->codec_ctx->height;
156
157 return 0.0;
158 };
159
160 static void
gtk_ff_media_file_paintable_init(GdkPaintableInterface * iface)161 gtk_ff_media_file_paintable_init (GdkPaintableInterface *iface)
162 {
163 iface->snapshot = gtk_ff_media_file_paintable_snapshot;
164 iface->get_current_image = gtk_ff_media_file_paintable_get_current_image;
165 iface->get_intrinsic_width = gtk_ff_media_file_paintable_get_intrinsic_width;
166 iface->get_intrinsic_height = gtk_ff_media_file_paintable_get_intrinsic_height;
167 iface->get_intrinsic_aspect_ratio = gtk_ff_media_file_paintable_get_intrinsic_aspect_ratio;
168 }
169
170 G_DEFINE_TYPE_EXTENDED (GtkFfMediaFile, gtk_ff_media_file, GTK_TYPE_MEDIA_FILE, 0,
171 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
172 gtk_ff_media_file_paintable_init))
173
174 G_MODULE_EXPORT
175 void
g_io_module_load(GIOModule * module)176 g_io_module_load (GIOModule *module)
177 {
178 g_type_module_use (G_TYPE_MODULE (module));
179
180 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT (58, 9, 100)
181 av_register_all ();
182 #endif
183
184 g_io_extension_point_implement (GTK_MEDIA_FILE_EXTENSION_POINT_NAME,
185 GTK_TYPE_FF_MEDIA_FILE,
186 "ffmpeg",
187 0);
188 }
189
190 G_MODULE_EXPORT
191 G_GNUC_NORETURN
192 void
g_io_module_unload(GIOModule * module)193 g_io_module_unload (GIOModule *module)
194 {
195 g_assert_not_reached ();
196 }
197
198 G_MODULE_EXPORT
199 char **
g_io_module_query(void)200 g_io_module_query (void)
201 {
202 char *eps[] = {
203 (char *) GTK_MEDIA_FILE_EXTENSION_POINT_NAME,
204 NULL
205 };
206
207 return g_strdupv (eps);
208 }
209
210 static void
gtk_ff_media_file_set_ffmpeg_error(GtkFfMediaFile * video,int av_errnum)211 gtk_ff_media_file_set_ffmpeg_error (GtkFfMediaFile *video,
212 int av_errnum)
213 {
214 char s[AV_ERROR_MAX_STRING_SIZE];
215
216 if (gtk_media_stream_get_error (GTK_MEDIA_STREAM (video)))
217 return;
218
219 if (av_strerror (av_errnum, s, sizeof (s) != 0))
220 g_snprintf (s, sizeof (s), _("Unspecified error decoding video"));
221
222 gtk_media_stream_error (GTK_MEDIA_STREAM (video),
223 G_IO_ERROR,
224 G_IO_ERROR_FAILED,
225 "%s",
226 s);
227 }
228
229 static int
gtk_ff_media_file_read_packet_cb(void * data,uint8_t * buf,int buf_size)230 gtk_ff_media_file_read_packet_cb (void *data,
231 uint8_t *buf,
232 int buf_size)
233 {
234 GtkFfMediaFile *video = data;
235 GError *error = NULL;
236 gssize n_read;
237
238 n_read = g_input_stream_read (video->input_stream,
239 buf,
240 buf_size,
241 NULL,
242 &error);
243 if (n_read < 0)
244 {
245 gtk_media_stream_gerror (GTK_MEDIA_STREAM (video), error);
246 }
247 else if (n_read == 0)
248 {
249 n_read = AVERROR_EOF;
250 }
251
252 return n_read;
253 }
254
255 static GdkMemoryFormat
memory_format_from_pix_fmt(enum AVPixelFormat pix_fmt)256 memory_format_from_pix_fmt (enum AVPixelFormat pix_fmt)
257 {
258 switch ((int) pix_fmt)
259 {
260 case AV_PIX_FMT_RGBA:
261 return GDK_MEMORY_R8G8B8A8;
262 case AV_PIX_FMT_RGB24:
263 return GDK_MEMORY_R8G8B8;
264 default:
265 g_assert_not_reached ();
266 return GDK_MEMORY_R8G8B8A8;
267 }
268 }
269
270 static gboolean
gtk_ff_media_file_decode_frame(GtkFfMediaFile * video,GtkVideoFrameFFMpeg * result)271 gtk_ff_media_file_decode_frame (GtkFfMediaFile *video,
272 GtkVideoFrameFFMpeg *result)
273 {
274 GdkTexture *texture;
275 AVPacket packet;
276 AVFrame *frame;
277 int errnum;
278 GBytes *bytes;
279 guchar *data;
280
281 frame = av_frame_alloc ();
282
283 for (errnum = av_read_frame (video->format_ctx, &packet);
284 errnum >= 0;
285 errnum = av_read_frame (video->format_ctx, &packet))
286 {
287 if (packet.stream_index == video->stream_id)
288 {
289 errnum = avcodec_send_packet (video->codec_ctx, &packet);
290 if (errnum < 0)
291 G_BREAKPOINT();
292 if (errnum >= 0)
293 {
294 errnum = avcodec_receive_frame (video->codec_ctx, frame);
295 if (errnum < 0)
296 G_BREAKPOINT();
297 if (errnum >= 0)
298 {
299 av_packet_unref (&packet);
300 break;
301 }
302 }
303 }
304
305 av_packet_unref (&packet);
306 }
307
308 if (errnum < 0)
309 {
310 if (errnum != AVERROR_EOF)
311 gtk_ff_media_file_set_ffmpeg_error (video, errnum);
312 av_frame_free (&frame);
313 return FALSE;
314 }
315
316 data = g_try_malloc0 (video->codec_ctx->width * video->codec_ctx->height * 4);
317 if (data == NULL)
318 {
319 gtk_media_stream_error (GTK_MEDIA_STREAM (video),
320 G_IO_ERROR,
321 G_IO_ERROR_FAILED,
322 _("Not enough memory"));
323 av_frame_free (&frame);
324 return FALSE;
325 }
326
327 if (video->sws_ctx == NULL ||
328 video->sws_pix_fmt != frame->format)
329 {
330 const AVPixFmtDescriptor *desc;
331 enum AVPixelFormat gdk_pix_fmt;
332
333 g_clear_pointer (&video->sws_ctx, sws_freeContext);
334 video->sws_pix_fmt = frame->format;
335 desc = av_pix_fmt_desc_get (video->sws_pix_fmt);
336 /* Use gdk-pixbuf formats because ffmpeg can't premultiply */
337 if (desc != NULL && (desc->flags & AV_PIX_FMT_FLAG_ALPHA))
338 gdk_pix_fmt = AV_PIX_FMT_RGBA;
339 else
340 gdk_pix_fmt = AV_PIX_FMT_RGB24;
341
342 video->sws_ctx = sws_getContext (video->codec_ctx->width,
343 video->codec_ctx->height,
344 frame->format,
345 video->codec_ctx->width,
346 video->codec_ctx->height,
347 gdk_pix_fmt,
348 0,
349 NULL,
350 NULL,
351 NULL);
352
353 video->memory_format = memory_format_from_pix_fmt (gdk_pix_fmt);
354 }
355
356 sws_scale(video->sws_ctx,
357 (const uint8_t * const *) frame->data, frame->linesize,
358 0, video->codec_ctx->height,
359 (uint8_t *[1]) { data }, (int[1]) { video->codec_ctx->width * 4 });
360
361 bytes = g_bytes_new_take (data, video->codec_ctx->width * video->codec_ctx->height * 4);
362 texture = gdk_memory_texture_new (video->codec_ctx->width,
363 video->codec_ctx->height,
364 video->memory_format,
365 bytes,
366 video->codec_ctx->width * 4);
367
368 g_bytes_unref (bytes);
369
370 gtk_video_frame_ffmpeg_init (result,
371 texture,
372 av_rescale_q (frame->best_effort_timestamp,
373 video->format_ctx->streams[video->stream_id]->time_base,
374 (AVRational) { 1, G_USEC_PER_SEC }));
375
376 av_frame_free (&frame);
377
378 return TRUE;
379 }
380
381 static int64_t
gtk_ff_media_file_seek_cb(void * data,int64_t offset,int whence)382 gtk_ff_media_file_seek_cb (void *data,
383 int64_t offset,
384 int whence)
385 {
386 GtkFfMediaFile *video = data;
387 GSeekType seek_type;
388 gboolean result;
389
390 switch (whence)
391 {
392 case SEEK_SET:
393 seek_type = G_SEEK_SET;
394 break;
395
396 case SEEK_CUR:
397 seek_type = G_SEEK_CUR;
398 break;
399
400 case SEEK_END:
401 seek_type = G_SEEK_END;
402 break;
403
404 case AVSEEK_SIZE:
405 /* FIXME: Handle size querying */
406 return -1;
407
408 default:
409 g_assert_not_reached ();
410 return -1;
411 }
412
413 result = g_seekable_seek (G_SEEKABLE (video->input_stream),
414 offset,
415 seek_type,
416 NULL,
417 NULL);
418 if (!result)
419 return -1;
420
421 return g_seekable_tell (G_SEEKABLE (video->input_stream));
422 }
423
424 static gboolean
gtk_ff_media_file_create_input_stream(GtkFfMediaFile * video)425 gtk_ff_media_file_create_input_stream (GtkFfMediaFile *video)
426 {
427 GError *error = NULL;
428 GFile *file;
429
430 file = gtk_media_file_get_file (GTK_MEDIA_FILE (video));
431 if (file)
432 {
433 video->input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
434 if (video->input_stream == NULL)
435 {
436 gtk_media_stream_gerror (GTK_MEDIA_STREAM (video), error);
437 g_error_free (error);
438 return FALSE;
439 }
440 }
441 else
442 {
443 video->input_stream = g_object_ref (gtk_media_file_get_input_stream (GTK_MEDIA_FILE (video)));
444 }
445
446 return TRUE;
447 }
448
449 static AVIOContext *
gtk_ff_media_file_create_io_context(GtkFfMediaFile * video)450 gtk_ff_media_file_create_io_context (GtkFfMediaFile *video)
451 {
452 AVIOContext *result;
453 int buffer_size = 4096; /* it's what everybody else uses... */
454 unsigned char *buffer;
455
456 if (!gtk_ff_media_file_create_input_stream (video))
457 return NULL;
458
459 buffer = av_malloc (buffer_size);
460 if (buffer == NULL)
461 return NULL;
462
463 result = avio_alloc_context (buffer,
464 buffer_size,
465 AVIO_FLAG_READ,
466 video,
467 gtk_ff_media_file_read_packet_cb,
468 NULL,
469 G_IS_SEEKABLE (video->input_stream)
470 ? gtk_ff_media_file_seek_cb
471 : NULL);
472
473 result->buf_ptr = result->buf_end;
474 result->write_flag = 0;
475
476 return result;
477 }
478
479 static gboolean gtk_ff_media_file_play (GtkMediaStream *stream);
480
481 static void
gtk_ff_media_file_open(GtkMediaFile * file)482 gtk_ff_media_file_open (GtkMediaFile *file)
483 {
484 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (file);
485 AVStream *stream;
486 AVCodec *codec;
487 int errnum;
488
489 video->format_ctx = avformat_alloc_context ();
490 video->format_ctx->pb = gtk_ff_media_file_create_io_context (video);
491 if (video->format_ctx->pb == NULL)
492 {
493 gtk_media_stream_error (GTK_MEDIA_STREAM (video),
494 G_IO_ERROR,
495 G_IO_ERROR_FAILED,
496 _("Not enough memory"));
497 return;
498 }
499 errnum = avformat_open_input (&video->format_ctx, NULL, NULL, NULL);
500 if (errnum != 0)
501 {
502 gtk_ff_media_file_set_ffmpeg_error (video, errnum);
503 return;
504 }
505
506 errnum = avformat_find_stream_info (video->format_ctx, NULL);
507 if (errnum < 0)
508 {
509 gtk_ff_media_file_set_ffmpeg_error (video, errnum);
510 return;
511 }
512
513 video->stream_id = av_find_best_stream (video->format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
514 if (video->stream_id < 0)
515 {
516 gtk_media_stream_error (GTK_MEDIA_STREAM (video),
517 G_IO_ERROR,
518 G_IO_ERROR_INVALID_DATA,
519 _("Not a video file"));
520 return;
521 }
522
523 stream = video->format_ctx->streams[video->stream_id];
524 /* alpha transparency requires the libvpx codecs, not the ffmpeg builtin ones */
525 if (stream->codecpar->codec_id == AV_CODEC_ID_VP8)
526 codec = avcodec_find_decoder_by_name ("libvpx");
527 else if (stream->codecpar->codec_id == AV_CODEC_ID_VP9)
528 codec = avcodec_find_decoder_by_name ("libvpx-vp9");
529 else
530 codec = NULL;
531 if (codec == NULL)
532 codec = avcodec_find_decoder (stream->codecpar->codec_id);
533 if (codec == NULL)
534 {
535 gtk_media_stream_error (GTK_MEDIA_STREAM (video),
536 G_IO_ERROR,
537 G_IO_ERROR_NOT_SUPPORTED,
538 _("Unsupported video codec"));
539 return;
540 }
541
542 video->codec_ctx = avcodec_alloc_context3 (codec);
543 errnum = avcodec_parameters_to_context (video->codec_ctx, stream->codecpar);
544 if (errnum < 0)
545 {
546 gtk_ff_media_file_set_ffmpeg_error (video, errnum);
547 return;
548 }
549 errnum = avcodec_open2 (video->codec_ctx, codec, &stream->metadata);
550 if (errnum < 0)
551 {
552 gtk_ff_media_file_set_ffmpeg_error (video, errnum);
553 return;
554 }
555
556 gtk_media_stream_stream_prepared (GTK_MEDIA_STREAM (video),
557 FALSE,
558 video->codec_ctx != NULL,
559 TRUE,
560 video->format_ctx->duration != AV_NOPTS_VALUE
561 ? av_rescale (video->format_ctx->duration, G_USEC_PER_SEC, AV_TIME_BASE)
562 : 0);
563
564 gdk_paintable_invalidate_size (GDK_PAINTABLE (video));
565
566 if (gtk_ff_media_file_decode_frame (video, &video->current_frame))
567 gdk_paintable_invalidate_contents (GDK_PAINTABLE (video));
568
569 if (gtk_media_stream_get_playing (GTK_MEDIA_STREAM (video)))
570 gtk_ff_media_file_play (GTK_MEDIA_STREAM (video));
571 }
572
573 static void
gtk_ff_media_file_close(GtkMediaFile * file)574 gtk_ff_media_file_close (GtkMediaFile *file)
575 {
576 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (file);
577
578 g_clear_object (&video->input_stream);
579
580 g_clear_pointer (&video->sws_ctx, sws_freeContext);
581 g_clear_pointer (&video->codec_ctx, avcodec_close);
582 avformat_close_input (&video->format_ctx);
583 video->stream_id = -1;
584 gtk_video_frame_ffmpeg_clear (&video->next_frame);
585 gtk_video_frame_ffmpeg_clear (&video->current_frame);
586
587 gdk_paintable_invalidate_size (GDK_PAINTABLE (video));
588 gdk_paintable_invalidate_contents (GDK_PAINTABLE (video));
589 }
590
591 static gboolean
592 gtk_ff_media_file_next_frame_cb (gpointer data);
593 static void
gtk_ff_media_file_queue_frame(GtkFfMediaFile * video)594 gtk_ff_media_file_queue_frame (GtkFfMediaFile *video)
595 {
596 gint64 time, frame_time;
597 guint delay;
598
599 time = g_get_monotonic_time ();
600 frame_time = video->start_time + video->next_frame.timestamp;
601 delay = time > frame_time ? 0 : (frame_time - time) / 1000;
602
603 video->next_frame_cb = g_timeout_add (delay, gtk_ff_media_file_next_frame_cb, video);
604 }
605
606 static gboolean
gtk_ff_media_file_restart(GtkFfMediaFile * video)607 gtk_ff_media_file_restart (GtkFfMediaFile *video)
608 {
609 if (av_seek_frame (video->format_ctx,
610 video->stream_id,
611 av_rescale_q (0,
612 (AVRational) { 1, G_USEC_PER_SEC },
613 video->format_ctx->streams[video->stream_id]->time_base),
614 AVSEEK_FLAG_BACKWARD) < 0)
615 return FALSE;
616
617 if (!gtk_ff_media_file_decode_frame (video, &video->next_frame))
618 return FALSE;
619
620 return TRUE;
621 }
622
623 static gboolean
gtk_ff_media_file_next_frame_cb(gpointer data)624 gtk_ff_media_file_next_frame_cb (gpointer data)
625 {
626 GtkFfMediaFile *video = data;
627
628 video->next_frame_cb = 0;
629
630 if (gtk_video_frame_ffmpeg_is_empty (&video->next_frame))
631 {
632 if (!gtk_media_stream_get_loop (GTK_MEDIA_STREAM (video)) ||
633 !gtk_ff_media_file_restart (video))
634 {
635 gtk_media_stream_stream_ended (GTK_MEDIA_STREAM (video));
636 return G_SOURCE_REMOVE;
637 }
638
639 video->start_time += video->current_frame.timestamp - video->next_frame.timestamp;
640 }
641
642 gtk_video_frame_ffmpeg_clear (&video->current_frame);
643 gtk_video_frame_ffmpeg_move (&video->current_frame,
644 &video->next_frame);
645
646 gtk_media_stream_update (GTK_MEDIA_STREAM (video),
647 video->current_frame.timestamp);
648 gdk_paintable_invalidate_contents (GDK_PAINTABLE (video));
649
650 /* ignore failure here, we'll handle the empty frame case above
651 * the next time we're called. */
652 gtk_ff_media_file_decode_frame (video, &video->next_frame);
653 gtk_ff_media_file_queue_frame (video);
654
655 return G_SOURCE_REMOVE;
656 }
657
658 static gboolean
gtk_ff_media_file_play(GtkMediaStream * stream)659 gtk_ff_media_file_play (GtkMediaStream *stream)
660 {
661 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (stream);
662
663 if (video->format_ctx == NULL)
664 return FALSE;
665
666 if (!gtk_media_stream_is_prepared (stream))
667 return TRUE;
668
669 if (gtk_video_frame_ffmpeg_is_empty (&video->next_frame) &&
670 !gtk_ff_media_file_decode_frame (video, &video->next_frame))
671 {
672 if (gtk_ff_media_file_restart (video))
673 {
674 video->start_time = g_get_monotonic_time () - video->next_frame.timestamp;
675 }
676 else
677 {
678 return FALSE;
679 }
680 }
681 else
682 {
683 video->start_time = g_get_monotonic_time () - video->current_frame.timestamp;
684 }
685
686 gtk_ff_media_file_queue_frame (video);
687
688 return TRUE;
689 }
690
691 static void
gtk_ff_media_file_pause(GtkMediaStream * stream)692 gtk_ff_media_file_pause (GtkMediaStream *stream)
693 {
694 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (stream);
695
696 if (video->next_frame_cb)
697 {
698 g_source_remove (video->next_frame_cb);
699 video->next_frame_cb = 0;
700 }
701
702 video->start_time = 0;
703 }
704
705 static void
gtk_ff_media_file_seek(GtkMediaStream * stream,gint64 timestamp)706 gtk_ff_media_file_seek (GtkMediaStream *stream,
707 gint64 timestamp)
708 {
709 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (stream);
710 int errnum;
711
712 errnum = av_seek_frame (video->format_ctx,
713 video->stream_id,
714 av_rescale_q (timestamp,
715 (AVRational) { 1, G_USEC_PER_SEC },
716 video->format_ctx->streams[video->stream_id]->time_base),
717 AVSEEK_FLAG_BACKWARD);
718 if (errnum < 0)
719 {
720 gtk_media_stream_seek_failed (stream);
721 return;
722 }
723
724 gtk_media_stream_seek_success (stream);
725
726 gtk_video_frame_ffmpeg_clear (&video->next_frame);
727 gtk_video_frame_ffmpeg_clear (&video->current_frame);
728 if (gtk_ff_media_file_decode_frame (video, &video->current_frame))
729 gtk_media_stream_update (stream, video->current_frame.timestamp);
730 gdk_paintable_invalidate_contents (GDK_PAINTABLE (video));
731
732 if (gtk_media_stream_get_playing (stream))
733 {
734 gtk_ff_media_file_pause (stream);
735 if (!gtk_ff_media_file_play (stream))
736 gtk_media_stream_stream_ended (stream);
737 }
738 }
739
740 static void
gtk_ff_media_file_dispose(GObject * object)741 gtk_ff_media_file_dispose (GObject *object)
742 {
743 GtkFfMediaFile *video = GTK_FF_MEDIA_FILE (object);
744
745 gtk_ff_media_file_pause (GTK_MEDIA_STREAM (video));
746 gtk_ff_media_file_close (GTK_MEDIA_FILE (video));
747
748 G_OBJECT_CLASS (gtk_ff_media_file_parent_class)->dispose (object);
749 }
750
751 static void
gtk_ff_media_file_class_init(GtkFfMediaFileClass * klass)752 gtk_ff_media_file_class_init (GtkFfMediaFileClass *klass)
753 {
754 GtkMediaFileClass *file_class = GTK_MEDIA_FILE_CLASS (klass);
755 GtkMediaStreamClass *stream_class = GTK_MEDIA_STREAM_CLASS (klass);
756 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
757
758 file_class->open = gtk_ff_media_file_open;
759 file_class->close = gtk_ff_media_file_close;
760 stream_class->play = gtk_ff_media_file_play;
761 stream_class->pause = gtk_ff_media_file_pause;
762 stream_class->seek = gtk_ff_media_file_seek;
763
764 gobject_class->dispose = gtk_ff_media_file_dispose;
765 }
766
767 static void
gtk_ff_media_file_init(GtkFfMediaFile * video)768 gtk_ff_media_file_init (GtkFfMediaFile *video)
769 {
770 video->stream_id = -1;
771 }
772
773
774