• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IncomingVideoStream(uint32_t stream_id,bool disable_prerenderer_smoothing)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 
~IncomingVideoStream()57 IncomingVideoStream::~IncomingVideoStream() {
58   Stop();
59 }
60 
ModuleCallback()61 VideoRenderCallback* IncomingVideoStream::ModuleCallback() {
62   CriticalSectionScoped cs(stream_critsect_.get());
63   return this;
64 }
65 
RenderFrame(const uint32_t stream_id,const VideoFrame & video_frame)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 
SetStartImage(const VideoFrame & video_frame)97 int32_t IncomingVideoStream::SetStartImage(const VideoFrame& video_frame) {
98   CriticalSectionScoped csS(thread_critsect_.get());
99   return start_image_.CopyFrame(video_frame);
100 }
101 
SetTimeoutImage(const VideoFrame & video_frame,const uint32_t timeout)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 
SetRenderCallback(VideoRenderCallback * render_callback)109 void IncomingVideoStream::SetRenderCallback(
110     VideoRenderCallback* render_callback) {
111   CriticalSectionScoped cs(thread_critsect_.get());
112   render_callback_ = render_callback;
113 }
114 
SetExpectedRenderDelay(int32_t delay_ms)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 
SetExternalCallback(VideoRenderCallback * external_callback)125 void IncomingVideoStream::SetExternalCallback(
126     VideoRenderCallback* external_callback) {
127   CriticalSectionScoped cs(thread_critsect_.get());
128   external_callback_ = external_callback;
129 }
130 
Start()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 
Stop()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 
Reset()184 int32_t IncomingVideoStream::Reset() {
185   CriticalSectionScoped cs_buffer(buffer_critsect_.get());
186   render_buffers_->ReleaseAllFrames();
187   return 0;
188 }
189 
StreamId() const190 uint32_t IncomingVideoStream::StreamId() const {
191   return stream_id_;
192 }
193 
IncomingRate() const194 uint32_t IncomingVideoStream::IncomingRate() const {
195   CriticalSectionScoped cs(stream_critsect_.get());
196   return incoming_rate_;
197 }
198 
IncomingVideoStreamThreadFun(void * obj)199 bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) {
200   return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess();
201 }
202 
IncomingVideoStreamProcess()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 
DeliverFrame(const VideoFrame & video_frame)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