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