1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/common_video/include/incoming_video_stream.h" 12 13 #include <assert.h> 14 15 #if defined(_WIN32) 16 #include <windows.h> 17 #elif defined(WEBRTC_LINUX) 18 #include <sys/time.h> 19 #include <time.h> 20 #else 21 #include <sys/time.h> 22 #endif 23 24 #include "webrtc/base/platform_thread.h" 25 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 26 #include "webrtc/common_video/video_render_frames.h" 27 #include "webrtc/system_wrappers/include/critical_section_wrapper.h" 28 #include "webrtc/system_wrappers/include/event_wrapper.h" 29 #include "webrtc/system_wrappers/include/tick_util.h" 30 #include "webrtc/system_wrappers/include/trace.h" 31 #include "webrtc/video_renderer.h" 32 33 namespace webrtc { 34 35 IncomingVideoStream::IncomingVideoStream(uint32_t stream_id, 36 bool disable_prerenderer_smoothing) 37 : stream_id_(stream_id), 38 disable_prerenderer_smoothing_(disable_prerenderer_smoothing), 39 stream_critsect_(CriticalSectionWrapper::CreateCriticalSection()), 40 thread_critsect_(CriticalSectionWrapper::CreateCriticalSection()), 41 buffer_critsect_(CriticalSectionWrapper::CreateCriticalSection()), 42 incoming_render_thread_(), 43 deliver_buffer_event_(EventTimerWrapper::Create()), 44 running_(false), 45 external_callback_(nullptr), 46 render_callback_(nullptr), 47 render_buffers_(new VideoRenderFrames()), 48 incoming_rate_(0), 49 last_rate_calculation_time_ms_(0), 50 num_frames_since_last_calculation_(0), 51 last_render_time_ms_(0), 52 temp_frame_(), 53 start_image_(), 54 timeout_image_(), 55 timeout_time_() {} 56 57 IncomingVideoStream::~IncomingVideoStream() { 58 Stop(); 59 } 60 61 VideoRenderCallback* IncomingVideoStream::ModuleCallback() { 62 CriticalSectionScoped cs(stream_critsect_.get()); 63 return this; 64 } 65 66 int32_t IncomingVideoStream::RenderFrame(const uint32_t stream_id, 67 const VideoFrame& video_frame) { 68 CriticalSectionScoped csS(stream_critsect_.get()); 69 70 if (!running_) { 71 return -1; 72 } 73 74 // Rate statistics. 75 num_frames_since_last_calculation_++; 76 int64_t now_ms = TickTime::MillisecondTimestamp(); 77 if (now_ms >= last_rate_calculation_time_ms_ + kFrameRatePeriodMs) { 78 incoming_rate_ = 79 static_cast<uint32_t>(1000 * num_frames_since_last_calculation_ / 80 (now_ms - last_rate_calculation_time_ms_)); 81 num_frames_since_last_calculation_ = 0; 82 last_rate_calculation_time_ms_ = now_ms; 83 } 84 85 // Hand over or insert frame. 86 if (disable_prerenderer_smoothing_) { 87 DeliverFrame(video_frame); 88 } else { 89 CriticalSectionScoped csB(buffer_critsect_.get()); 90 if (render_buffers_->AddFrame(video_frame) == 1) { 91 deliver_buffer_event_->Set(); 92 } 93 } 94 return 0; 95 } 96 97 int32_t IncomingVideoStream::SetStartImage(const VideoFrame& video_frame) { 98 CriticalSectionScoped csS(thread_critsect_.get()); 99 return start_image_.CopyFrame(video_frame); 100 } 101 102 int32_t IncomingVideoStream::SetTimeoutImage(const VideoFrame& video_frame, 103 const uint32_t timeout) { 104 CriticalSectionScoped csS(thread_critsect_.get()); 105 timeout_time_ = timeout; 106 return timeout_image_.CopyFrame(video_frame); 107 } 108 109 void IncomingVideoStream::SetRenderCallback( 110 VideoRenderCallback* render_callback) { 111 CriticalSectionScoped cs(thread_critsect_.get()); 112 render_callback_ = render_callback; 113 } 114 115 int32_t IncomingVideoStream::SetExpectedRenderDelay( 116 int32_t delay_ms) { 117 CriticalSectionScoped csS(stream_critsect_.get()); 118 if (running_) { 119 return -1; 120 } 121 CriticalSectionScoped cs(buffer_critsect_.get()); 122 return render_buffers_->SetRenderDelay(delay_ms); 123 } 124 125 void IncomingVideoStream::SetExternalCallback( 126 VideoRenderCallback* external_callback) { 127 CriticalSectionScoped cs(thread_critsect_.get()); 128 external_callback_ = external_callback; 129 } 130 131 int32_t IncomingVideoStream::Start() { 132 CriticalSectionScoped csS(stream_critsect_.get()); 133 if (running_) { 134 return 0; 135 } 136 137 if (!disable_prerenderer_smoothing_) { 138 CriticalSectionScoped csT(thread_critsect_.get()); 139 assert(incoming_render_thread_ == NULL); 140 141 incoming_render_thread_.reset(new rtc::PlatformThread( 142 IncomingVideoStreamThreadFun, this, "IncomingVideoStreamThread")); 143 if (!incoming_render_thread_) { 144 return -1; 145 } 146 147 incoming_render_thread_->Start(); 148 incoming_render_thread_->SetPriority(rtc::kRealtimePriority); 149 deliver_buffer_event_->StartTimer(false, kEventStartupTimeMs); 150 } 151 152 running_ = true; 153 return 0; 154 } 155 156 int32_t IncomingVideoStream::Stop() { 157 CriticalSectionScoped cs_stream(stream_critsect_.get()); 158 159 if (!running_) { 160 return 0; 161 } 162 163 rtc::PlatformThread* thread = NULL; 164 { 165 CriticalSectionScoped cs_thread(thread_critsect_.get()); 166 if (incoming_render_thread_) { 167 // Setting the incoming render thread to NULL marks that we're performing 168 // a shutdown and will make IncomingVideoStreamProcess abort after wakeup. 169 thread = incoming_render_thread_.release(); 170 deliver_buffer_event_->StopTimer(); 171 // Set the event to allow the thread to wake up and shut down without 172 // waiting for a timeout. 173 deliver_buffer_event_->Set(); 174 } 175 } 176 if (thread) { 177 thread->Stop(); 178 delete thread; 179 } 180 running_ = false; 181 return 0; 182 } 183 184 int32_t IncomingVideoStream::Reset() { 185 CriticalSectionScoped cs_buffer(buffer_critsect_.get()); 186 render_buffers_->ReleaseAllFrames(); 187 return 0; 188 } 189 190 uint32_t IncomingVideoStream::StreamId() const { 191 return stream_id_; 192 } 193 194 uint32_t IncomingVideoStream::IncomingRate() const { 195 CriticalSectionScoped cs(stream_critsect_.get()); 196 return incoming_rate_; 197 } 198 199 bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) { 200 return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess(); 201 } 202 203 bool IncomingVideoStream::IncomingVideoStreamProcess() { 204 if (kEventError != deliver_buffer_event_->Wait(kEventMaxWaitTimeMs)) { 205 CriticalSectionScoped cs(thread_critsect_.get()); 206 if (incoming_render_thread_ == NULL) { 207 // Terminating 208 return false; 209 } 210 211 // Get a new frame to render and the time for the frame after this one. 212 VideoFrame frame_to_render; 213 uint32_t wait_time; 214 { 215 CriticalSectionScoped cs(buffer_critsect_.get()); 216 frame_to_render = render_buffers_->FrameToRender(); 217 wait_time = render_buffers_->TimeToNextFrameRelease(); 218 } 219 220 // Set timer for next frame to render. 221 if (wait_time > kEventMaxWaitTimeMs) { 222 wait_time = kEventMaxWaitTimeMs; 223 } 224 deliver_buffer_event_->StartTimer(false, wait_time); 225 226 DeliverFrame(frame_to_render); 227 } 228 return true; 229 } 230 231 void IncomingVideoStream::DeliverFrame(const VideoFrame& video_frame) { 232 CriticalSectionScoped cs(thread_critsect_.get()); 233 if (video_frame.IsZeroSize()) { 234 if (render_callback_) { 235 if (last_render_time_ms_ == 0 && !start_image_.IsZeroSize()) { 236 // We have not rendered anything and have a start image. 237 temp_frame_.CopyFrame(start_image_); 238 render_callback_->RenderFrame(stream_id_, temp_frame_); 239 } else if (!timeout_image_.IsZeroSize() && 240 last_render_time_ms_ + timeout_time_ < 241 TickTime::MillisecondTimestamp()) { 242 // Render a timeout image. 243 temp_frame_.CopyFrame(timeout_image_); 244 render_callback_->RenderFrame(stream_id_, temp_frame_); 245 } 246 } 247 248 // No frame. 249 return; 250 } 251 252 // Send frame for rendering. 253 if (external_callback_) { 254 external_callback_->RenderFrame(stream_id_, video_frame); 255 } else if (render_callback_) { 256 render_callback_->RenderFrame(stream_id_, video_frame); 257 } 258 259 // We're done with this frame. 260 last_render_time_ms_ = video_frame.render_time_ms(); 261 } 262 263 } // namespace webrtc 264