/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/common_video/include/incoming_video_stream.h"

#include "webrtc/base/timeutils.h"
#include "webrtc/common_video/video_render_frames.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
#include "webrtc/system_wrappers/include/event_wrapper.h"

namespace webrtc {

IncomingVideoStream::IncomingVideoStream(
    int32_t delay_ms,
    rtc::VideoSinkInterface<VideoFrame>* callback)
    : incoming_render_thread_(&IncomingVideoStreamThreadFun,
                              this,
                              "IncomingVideoStreamThread"),
      deliver_buffer_event_(EventTimerWrapper::Create()),
      external_callback_(callback),
      render_buffers_(new VideoRenderFrames(delay_ms)) {
  RTC_DCHECK(external_callback_);

  render_thread_checker_.DetachFromThread();
  decoder_thread_checker_.DetachFromThread();

  incoming_render_thread_.Start();
  incoming_render_thread_.SetPriority(rtc::kRealtimePriority);
  deliver_buffer_event_->StartTimer(false, kEventStartupTimeMs);
}

IncomingVideoStream::~IncomingVideoStream() {
  RTC_DCHECK(main_thread_checker_.CalledOnValidThread());

  {
    rtc::CritScope cs(&buffer_critsect_);
    render_buffers_.reset();
  }

  deliver_buffer_event_->Set();
  incoming_render_thread_.Stop();
  deliver_buffer_event_->StopTimer();
}

void IncomingVideoStream::OnFrame(const VideoFrame& video_frame) {
  RTC_DCHECK_RUN_ON(&decoder_thread_checker_);

  // Hand over or insert frame.
  rtc::CritScope csB(&buffer_critsect_);
  if (render_buffers_->AddFrame(video_frame) == 1) {
    deliver_buffer_event_->Set();
  }
}

bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) {
  return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess();
}

bool IncomingVideoStream::IncomingVideoStreamProcess() {
  RTC_DCHECK_RUN_ON(&render_thread_checker_);

  if (kEventError != deliver_buffer_event_->Wait(kEventMaxWaitTimeMs)) {
    // Get a new frame to render and the time for the frame after this one.
    rtc::Optional<VideoFrame> frame_to_render;
    uint32_t wait_time;
    {
      rtc::CritScope cs(&buffer_critsect_);
      if (!render_buffers_.get()) {
        // Terminating
        return false;
      }
      frame_to_render = render_buffers_->FrameToRender();
      wait_time = render_buffers_->TimeToNextFrameRelease();
    }

    // Set timer for next frame to render.
    if (wait_time > kEventMaxWaitTimeMs) {
      wait_time = kEventMaxWaitTimeMs;
    }

    deliver_buffer_event_->StartTimer(false, wait_time);

    if (frame_to_render) {
      external_callback_->OnFrame(*frame_to_render);
    }
  }
  return true;
}

}  // namespace webrtc