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)13video_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)55video_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)65video_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)72video_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)93video_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)105video_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