| /* |
| * 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 |