1 #include "video_buffer.h"
2 
3 #include <SDL2/SDL_assert.h>
4 #include <SDL2/SDL_mutex.h>
5 #include <libavutil/avutil.h>
6 #include <libavformat/avformat.h>
7 
8 #include "config.h"
9 #include "lock_util.h"
10 #include "log.h"
11 
12 bool
video_buffer_init(struct video_buffer * vb,struct fps_counter * fps_counter,bool render_expired_frames)13 video_buffer_init(struct video_buffer *vb, struct fps_counter *fps_counter,
14                   bool render_expired_frames) {
15     vb->fps_counter = fps_counter;
16 
17     if (!(vb->decoding_frame = av_frame_alloc())) {
18         goto error_0;
19     }
20 
21     if (!(vb->rendering_frame = av_frame_alloc())) {
22         goto error_1;
23     }
24 
25     if (!(vb->mutex = SDL_CreateMutex())) {
26         goto error_2;
27     }
28 
29     vb->render_expired_frames = render_expired_frames;
30     if (render_expired_frames) {
31         if (!(vb->rendering_frame_consumed_cond = SDL_CreateCond())) {
32             SDL_DestroyMutex(vb->mutex);
33             goto error_2;
34         }
35         // interrupted is not used if expired frames are not rendered
36         // since offering a frame will never block
37         vb->interrupted = false;
38     }
39 
40     // there is initially no rendering frame, so consider it has already been
41     // consumed
42     vb->rendering_frame_consumed = true;
43 
44     return true;
45 
46 error_2:
47     av_frame_free(&vb->rendering_frame);
48 error_1:
49     av_frame_free(&vb->decoding_frame);
50 error_0:
51     return false;
52 }
53 
54 void
video_buffer_destroy(struct video_buffer * vb)55 video_buffer_destroy(struct video_buffer *vb) {
56     if (vb->render_expired_frames) {
57         SDL_DestroyCond(vb->rendering_frame_consumed_cond);
58     }
59     SDL_DestroyMutex(vb->mutex);
60     av_frame_free(&vb->rendering_frame);
61     av_frame_free(&vb->decoding_frame);
62 }
63 
64 static void
video_buffer_swap_frames(struct video_buffer * vb)65 video_buffer_swap_frames(struct video_buffer *vb) {
66     AVFrame *tmp = vb->decoding_frame;
67     vb->decoding_frame = vb->rendering_frame;
68     vb->rendering_frame = tmp;
69 }
70 
71 void
video_buffer_offer_decoded_frame(struct video_buffer * vb,bool * previous_frame_skipped)72 video_buffer_offer_decoded_frame(struct video_buffer *vb,
73                                  bool *previous_frame_skipped) {
74     mutex_lock(vb->mutex);
75     if (vb->render_expired_frames) {
76         // wait for the current (expired) frame to be consumed
77         while (!vb->rendering_frame_consumed && !vb->interrupted) {
78             cond_wait(vb->rendering_frame_consumed_cond, vb->mutex);
79         }
80     } else if (!vb->rendering_frame_consumed) {
81         fps_counter_add_skipped_frame(vb->fps_counter);
82     }
83 
84     video_buffer_swap_frames(vb);
85 
86     *previous_frame_skipped = !vb->rendering_frame_consumed;
87     vb->rendering_frame_consumed = false;
88 
89     mutex_unlock(vb->mutex);
90 }
91 
92 const AVFrame *
video_buffer_consume_rendered_frame(struct video_buffer * vb)93 video_buffer_consume_rendered_frame(struct video_buffer *vb) {
94     SDL_assert(!vb->rendering_frame_consumed);
95     vb->rendering_frame_consumed = true;
96     fps_counter_add_rendered_frame(vb->fps_counter);
97     if (vb->render_expired_frames) {
98         // unblock video_buffer_offer_decoded_frame()
99         cond_signal(vb->rendering_frame_consumed_cond);
100     }
101     return vb->rendering_frame;
102 }
103 
104 void
video_buffer_interrupt(struct video_buffer * vb)105 video_buffer_interrupt(struct video_buffer *vb) {
106     if (vb->render_expired_frames) {
107         mutex_lock(vb->mutex);
108         vb->interrupted = true;
109         mutex_unlock(vb->mutex);
110         // wake up blocking wait
111         cond_signal(vb->rendering_frame_consumed_cond);
112     }
113 }
114