text: #include "frame_buffer.h"
  
  #include <assert.h>
  #include <libavutil/avutil.h>
  #include <libavformat/avformat.h>
  
  #include "util/log.h"
  
  bool
  sc_frame_buffer_init(struct sc_frame_buffer *fb) {
      fb->pending_frame = av_frame_alloc();
      if (!fb->pending_frame) {
          LOG_OOM();
          return false;
      }
  
      fb->tmp_frame = av_frame_alloc();
      if (!fb->tmp_frame) {
          LOG_OOM();
          av_frame_free(&fb->pending_frame);
          return false;
      }
  
      bool ok = sc_mutex_init(&fb->mutex);
      if (!ok) {
          av_frame_free(&fb->pending_frame);
          av_frame_free(&fb->tmp_frame);
          return false;
      }
  
      // there is initially no frame, so consider it has already been consumed
      fb->pending_frame_consumed = true;
  
      return true;
  }
  
  void
  sc_frame_buffer_destroy(struct sc_frame_buffer *fb) {
      sc_mutex_destroy(&fb->mutex);
      av_frame_free(&fb->pending_frame);
      av_frame_free(&fb->tmp_frame);
  }
  
  static inline void
  swap_frames(AVFrame **lhs, AVFrame **rhs) {
      AVFrame *tmp = *lhs;
      *lhs = *rhs;
      *rhs = tmp;
  }
  
  bool
  sc_frame_buffer_push(struct sc_frame_buffer *fb, const AVFrame *frame,
                    bool *previous_frame_skipped) {
      sc_mutex_lock(&fb->mutex);
  
      // Use a temporary frame to preserve pending_frame in case of error.
      // tmp_frame is an empty frame, no need to call av_frame_unref() beforehand.
      int r = av_frame_ref(fb->tmp_frame, frame);
      if (r) {
          LOGE("Could not ref frame: %d", r);
          return false;
      }
  
      // Now that av_frame_ref() succeeded, we can replace the previous
      // pending_frame
      swap_frames(&fb->pending_frame, &fb->tmp_frame);
      av_frame_unref(fb->tmp_frame);
  
      if (previous_frame_skipped) {
          *previous_frame_skipped = !fb->pending_frame_consumed;
      }
      fb->pending_frame_consumed = false;
  
      sc_mutex_unlock(&fb->mutex);
  
      return true;
  }
  
  void
  sc_frame_buffer_consume(struct sc_frame_buffer *fb, AVFrame *dst) {
      sc_mutex_lock(&fb->mutex);
      assert(!fb->pending_frame_consumed);
      fb->pending_frame_consumed = true;
  
      av_frame_move_ref(dst, fb->pending_frame);
      // av_frame_move_ref() resets its source frame, so no need to call
      // av_frame_unref()
  
      sc_mutex_unlock(&fb->mutex);
  }
  