blob: a5e7ba755d35023ad9ba063d37b0e6b928305077 [file] [log] [blame]
/*
* 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 <assert.h>
#include "webrtc/base/platform_thread.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(bool disable_prerenderer_smoothing)
: disable_prerenderer_smoothing_(disable_prerenderer_smoothing),
incoming_render_thread_(),
deliver_buffer_event_(EventTimerWrapper::Create()),
running_(false),
external_callback_(nullptr),
render_buffers_(new VideoRenderFrames()) {}
IncomingVideoStream::~IncomingVideoStream() {
Stop();
}
void IncomingVideoStream::OnFrame(const VideoFrame& video_frame) {
rtc::CritScope csS(&stream_critsect_);
if (!running_) {
return;
}
// Hand over or insert frame.
if (disable_prerenderer_smoothing_) {
DeliverFrame(video_frame);
} else {
rtc::CritScope csB(&buffer_critsect_);
if (render_buffers_->AddFrame(video_frame) == 1) {
deliver_buffer_event_->Set();
}
}
}
int32_t IncomingVideoStream::SetExpectedRenderDelay(
int32_t delay_ms) {
rtc::CritScope csS(&stream_critsect_);
if (running_) {
return -1;
}
rtc::CritScope cs(&buffer_critsect_);
return render_buffers_->SetRenderDelay(delay_ms);
}
void IncomingVideoStream::SetExternalCallback(
rtc::VideoSinkInterface<VideoFrame>* external_callback) {
rtc::CritScope cs(&thread_critsect_);
external_callback_ = external_callback;
}
int32_t IncomingVideoStream::Start() {
rtc::CritScope csS(&stream_critsect_);
if (running_) {
return 0;
}
if (!disable_prerenderer_smoothing_) {
rtc::CritScope csT(&thread_critsect_);
assert(incoming_render_thread_ == NULL);
incoming_render_thread_.reset(new rtc::PlatformThread(
IncomingVideoStreamThreadFun, this, "IncomingVideoStreamThread"));
if (!incoming_render_thread_) {
return -1;
}
incoming_render_thread_->Start();
incoming_render_thread_->SetPriority(rtc::kRealtimePriority);
deliver_buffer_event_->StartTimer(false, kEventStartupTimeMs);
}
running_ = true;
return 0;
}
int32_t IncomingVideoStream::Stop() {
rtc::CritScope cs_stream(&stream_critsect_);
if (!running_) {
return 0;
}
rtc::PlatformThread* thread = NULL;
{
rtc::CritScope cs_thread(&thread_critsect_);
if (incoming_render_thread_) {
// Setting the incoming render thread to NULL marks that we're performing
// a shutdown and will make IncomingVideoStreamProcess abort after wakeup.
thread = incoming_render_thread_.release();
deliver_buffer_event_->StopTimer();
// Set the event to allow the thread to wake up and shut down without
// waiting for a timeout.
deliver_buffer_event_->Set();
}
}
if (thread) {
thread->Stop();
delete thread;
}
running_ = false;
return 0;
}
bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) {
return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess();
}
bool IncomingVideoStream::IncomingVideoStreamProcess() {
if (kEventError != deliver_buffer_event_->Wait(kEventMaxWaitTimeMs)) {
rtc::CritScope cs(&thread_critsect_);
if (incoming_render_thread_ == NULL) {
// Terminating
return false;
}
// 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_);
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)
DeliverFrame(*frame_to_render);
}
return true;
}
void IncomingVideoStream::DeliverFrame(const VideoFrame& video_frame) {
rtc::CritScope cs(&thread_critsect_);
// Send frame for rendering.
if (external_callback_) {
external_callback_->OnFrame(video_frame);
}
}
} // namespace webrtc