blob: 6d2d2c2bc383d52c1dcb34963c199bb9667358ee [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.org0e703f42012-03-06 12:02:20 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
andrew@webrtc.org9841d922012-10-31 05:22:11 +000011#include "webrtc/modules/video_render//incoming_video_stream.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
13#include <cassert>
14
niklase@google.com470e71d2011-07-07 08:21:25 +000015#if defined(_WIN32)
16#include <windows.h>
17#elif defined(WEBRTC_LINUX)
18#include <ctime>
19#include <sys/time.h>
20#else
21#include <sys/time.h>
22#endif
23
andrew@webrtc.orgc1354bd2012-07-27 18:21:16 +000024#include "common_video/libyuv/include/webrtc_libyuv.h"
andrew@webrtc.org9841d922012-10-31 05:22:11 +000025#include "webrtc/modules/video_render//video_render_frames.h"
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000026#include "system_wrappers/interface/critical_section_wrapper.h"
27#include "system_wrappers/interface/event_wrapper.h"
28#include "system_wrappers/interface/map_wrapper.h"
29#include "system_wrappers/interface/thread_wrapper.h"
30#include "system_wrappers/interface/tick_util.h"
31#include "system_wrappers/interface/trace.h"
32
niklase@google.com470e71d2011-07-07 08:21:25 +000033namespace webrtc {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000034
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +000035IncomingVideoStream::IncomingVideoStream(const int32_t module_id,
36 const uint32_t stream_id)
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000037 : module_id_(module_id),
38 stream_id_(stream_id),
39 stream_critsect_(*CriticalSectionWrapper::CreateCriticalSection()),
40 thread_critsect_(*CriticalSectionWrapper::CreateCriticalSection()),
41 buffer_critsect_(*CriticalSectionWrapper::CreateCriticalSection()),
42 incoming_render_thread_(),
43 deliver_buffer_event_(*EventWrapper::Create()),
44 running_(false),
45 external_callback_(NULL),
46 render_callback_(NULL),
47 render_buffers_(*(new VideoRenderFrames)),
48 callbackVideoType_(kVideoI420),
49 callbackWidth_(0),
50 callbackHeight_(0),
51 incoming_rate_(0),
52 last_rate_calculation_time_ms_(0),
53 num_frames_since_last_calculation_(0),
54 last_rendered_frame_(),
55 temp_frame_(),
56 start_image_(),
57 timeout_image_(),
58 timeout_time_(),
59 mirror_frames_enabled_(false),
60 mirroring_(),
61 transformed_video_frame_() {
62 WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, module_id_,
63 "%s created for stream %d", __FUNCTION__, stream_id);
niklase@google.com470e71d2011-07-07 08:21:25 +000064}
65
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000066IncomingVideoStream::~IncomingVideoStream() {
67 WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, module_id_,
68 "%s deleted for stream %d", __FUNCTION__, stream_id_);
niklase@google.com470e71d2011-07-07 08:21:25 +000069
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000070 Stop();
niklase@google.com470e71d2011-07-07 08:21:25 +000071
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000072 // incoming_render_thread_ - Delete in stop
73 delete &render_buffers_;
74 delete &stream_critsect_;
75 delete &buffer_critsect_;
76 delete &thread_critsect_;
77 delete &deliver_buffer_event_;
niklase@google.com470e71d2011-07-07 08:21:25 +000078}
79
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +000080int32_t IncomingVideoStream::ChangeModuleId(const int32_t id) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000081 CriticalSectionScoped cs(&stream_critsect_);
82 module_id_ = id;
83 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000084}
85
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000086VideoRenderCallback* IncomingVideoStream::ModuleCallback() {
87 CriticalSectionScoped cs(&stream_critsect_);
88 return this;
niklase@google.com470e71d2011-07-07 08:21:25 +000089}
90
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +000091int32_t IncomingVideoStream::RenderFrame(const uint32_t stream_id,
92 I420VideoFrame& video_frame) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000093 CriticalSectionScoped csS(&stream_critsect_);
94 WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, module_id_,
95 "%s for stream %d, render time: %u", __FUNCTION__, stream_id_,
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000096 video_frame.render_time_ms());
niklase@google.com470e71d2011-07-07 08:21:25 +000097
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000098 if (!running_) {
99 WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, module_id_,
100 "%s: Not running", __FUNCTION__);
101 return -1;
102 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000103
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000104 if (true == mirror_frames_enabled_) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000105 transformed_video_frame_.CreateEmptyFrame(video_frame.width(),
106 video_frame.height(),
107 video_frame.stride(kYPlane),
108 video_frame.stride(kUPlane),
109 video_frame.stride(kVPlane));
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000110 if (mirroring_.mirror_x_axis) {
mikhal@webrtc.org23381312012-09-27 15:36:15 +0000111 MirrorI420UpDown(&video_frame,
112 &transformed_video_frame_);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000113 video_frame.SwapFrame(&transformed_video_frame_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000114 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000115 if (mirroring_.mirror_y_axis) {
mikhal@webrtc.org23381312012-09-27 15:36:15 +0000116 MirrorI420LeftRight(&video_frame,
117 &transformed_video_frame_);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000118 video_frame.SwapFrame(&transformed_video_frame_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000119 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000120 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000121
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000122 // Rate statistics.
123 num_frames_since_last_calculation_++;
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000124 int64_t now_ms = TickTime::MillisecondTimestamp();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000125 if (now_ms >= last_rate_calculation_time_ms_ + KFrameRatePeriodMs) {
126 incoming_rate_ =
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000127 static_cast<uint32_t>(1000 * num_frames_since_last_calculation_ /
128 (now_ms - last_rate_calculation_time_ms_));
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000129 num_frames_since_last_calculation_ = 0;
130 last_rate_calculation_time_ms_ = now_ms;
131 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000132
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000133 // Insert frame.
134 CriticalSectionScoped csB(&buffer_critsect_);
135 if (render_buffers_.AddFrame(&video_frame) == 1)
136 deliver_buffer_event_.Set();
niklase@google.com470e71d2011-07-07 08:21:25 +0000137
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000138 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000139}
140
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000141int32_t IncomingVideoStream::SetStartImage(
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000142 const I420VideoFrame& video_frame) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000143 CriticalSectionScoped csS(&thread_critsect_);
144 return start_image_.CopyFrame(video_frame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000145}
146
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000147int32_t IncomingVideoStream::SetTimeoutImage(
148 const I420VideoFrame& video_frame, const uint32_t timeout) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000149 CriticalSectionScoped csS(&thread_critsect_);
150 timeout_time_ = timeout;
151 return timeout_image_.CopyFrame(video_frame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000152}
153
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000154int32_t IncomingVideoStream::SetRenderCallback(
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000155 VideoRenderCallback* render_callback) {
156 CriticalSectionScoped cs(&stream_critsect_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000157
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000158 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
159 "%s(%x) for stream %d", __FUNCTION__, render_callback,
160 stream_id_);
161 render_callback_ = render_callback;
162 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000163}
164
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000165int32_t IncomingVideoStream::EnableMirroring(const bool enable,
166 const bool mirror_x_axis,
167 const bool mirror_y_axis) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000168 CriticalSectionScoped cs(&stream_critsect_);
169 mirror_frames_enabled_ = enable;
170 mirroring_.mirror_x_axis = mirror_x_axis;
171 mirroring_.mirror_y_axis = mirror_y_axis;
niklase@google.com470e71d2011-07-07 08:21:25 +0000172
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000173 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000174}
175
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000176int32_t IncomingVideoStream::SetExpectedRenderDelay(
177 int32_t delay_ms) {
mflodman@webrtc.orgf4f21452012-09-28 11:27:35 +0000178 CriticalSectionScoped csS(&stream_critsect_);
179 if (running_) {
180 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
181 "%s(%d) for stream %d", __FUNCTION__, delay_ms, stream_id_);
182 return -1;
183 }
mflodman@webrtc.orgaeb37d32012-10-11 16:31:00 +0000184 CriticalSectionScoped cs(&buffer_critsect_);
mflodman@webrtc.orgf4f21452012-09-28 11:27:35 +0000185 return render_buffers_.SetRenderDelay(delay_ms);
186}
187
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000188int32_t IncomingVideoStream::SetExternalCallback(
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000189 VideoRenderCallback* external_callback) {
190 CriticalSectionScoped cs(&stream_critsect_);
191 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
192 "%s(%x) for stream %d", __FUNCTION__, external_callback,
193 stream_id_);
194 external_callback_ = external_callback;
195 callbackVideoType_ = kVideoI420;
196 callbackWidth_ = 0;
197 callbackHeight_ = 0;
198 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000199}
200
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000201int32_t IncomingVideoStream::Start() {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000202 CriticalSectionScoped csS(&stream_critsect_);
203 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
204 "%s for stream %d", __FUNCTION__, stream_id_);
205 if (running_) {
206 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, module_id_,
207 "%s: Already running", __FUNCTION__);
niklase@google.com470e71d2011-07-07 08:21:25 +0000208 return 0;
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000209 }
210
211 CriticalSectionScoped csT(&thread_critsect_);
212 assert(incoming_render_thread_ == NULL);
213
214 incoming_render_thread_ = ThreadWrapper::CreateThread(
215 IncomingVideoStreamThreadFun, this, kRealtimePriority,
216 "IncomingVideoStreamThread");
217 if (!incoming_render_thread_) {
218 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, module_id_,
219 "%s: No thread", __FUNCTION__);
220 return -1;
221 }
222
223 unsigned int t_id = 0;
224 if (incoming_render_thread_->Start(t_id)) {
225 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
226 "%s: thread started: %u", __FUNCTION__, t_id);
227 } else {
228 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, module_id_,
229 "%s: Could not start send thread", __FUNCTION__);
230 return -1;
231 }
232 deliver_buffer_event_.StartTimer(false, KEventStartupTimeMS);
233
234 running_ = true;
235 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000236}
237
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000238int32_t IncomingVideoStream::Stop() {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000239 CriticalSectionScoped cs_stream(&stream_critsect_);
240 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, module_id_,
241 "%s for stream %d", __FUNCTION__, stream_id_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000242
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000243 if (!running_) {
244 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, module_id_,
245 "%s: Not running", __FUNCTION__);
246 return 0;
247 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000248
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000249 thread_critsect_.Enter();
250 if (incoming_render_thread_) {
251 ThreadWrapper* thread = incoming_render_thread_;
252 incoming_render_thread_ = NULL;
253 thread->SetNotAlive();
254#ifndef WIN32_
255 deliver_buffer_event_.StopTimer();
niklase@google.com470e71d2011-07-07 08:21:25 +0000256#endif
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000257 thread_critsect_.Leave();
258 if (thread->Stop()) {
259 delete thread;
260 } else {
261 assert(false);
262 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, module_id_,
263 "%s: Not able to stop thread, leaking", __FUNCTION__);
niklase@google.com470e71d2011-07-07 08:21:25 +0000264 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000265 } else {
266 thread_critsect_.Leave();
267 }
268 running_ = false;
269 return 0;
270}
271
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000272int32_t IncomingVideoStream::Reset() {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000273 CriticalSectionScoped cs_stream(&stream_critsect_);
274 CriticalSectionScoped cs_buffer(&buffer_critsect_);
275 render_buffers_.ReleaseAllFrames();
276 return 0;
277}
278
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000279uint32_t IncomingVideoStream::StreamId() const {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000280 CriticalSectionScoped cs_stream(&stream_critsect_);
281 return stream_id_;
282}
283
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000284uint32_t IncomingVideoStream::IncomingRate() const {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000285 CriticalSectionScoped cs(&stream_critsect_);
286 return incoming_rate_;
287}
288
289bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) {
290 return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess();
291}
292
293bool IncomingVideoStream::IncomingVideoStreamProcess() {
294 if (kEventError != deliver_buffer_event_.Wait(KEventMaxWaitTimeMs)) {
vikasmarwaha@webrtc.org455370d2013-03-20 16:57:09 +0000295 thread_critsect_.Enter();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000296 if (incoming_render_thread_ == NULL) {
297 // Terminating
vikasmarwaha@webrtc.org455370d2013-03-20 16:57:09 +0000298 thread_critsect_.Leave();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000299 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000300 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000301
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000302 I420VideoFrame* frame_to_render = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000303
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000304 // Get a new frame to render and the time for the frame after this one.
305 buffer_critsect_.Enter();
306 frame_to_render = render_buffers_.FrameToRender();
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000307 uint32_t wait_time = render_buffers_.TimeToNextFrameRelease();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000308 buffer_critsect_.Leave();
niklase@google.com470e71d2011-07-07 08:21:25 +0000309
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000310 // Set timer for next frame to render.
311 if (wait_time > KEventMaxWaitTimeMs) {
312 wait_time = KEventMaxWaitTimeMs;
niklase@google.com470e71d2011-07-07 08:21:25 +0000313 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000314 deliver_buffer_event_.StartTimer(false, wait_time);
315
316 if (!frame_to_render) {
317 if (render_callback_) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000318 if (last_rendered_frame_.render_time_ms() == 0 &&
319 !start_image_.IsZeroSize()) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000320 // We have not rendered anything and have a start image.
321 temp_frame_.CopyFrame(start_image_);
322 render_callback_->RenderFrame(stream_id_, temp_frame_);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000323 } else if (!timeout_image_.IsZeroSize() &&
324 last_rendered_frame_.render_time_ms() + timeout_time_ <
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000325 TickTime::MillisecondTimestamp()) {
326 // Render a timeout image.
327 temp_frame_.CopyFrame(timeout_image_);
328 render_callback_->RenderFrame(stream_id_, temp_frame_);
329 }
330 }
331
332 // No frame.
333 thread_critsect_.Leave();
334 return true;
335 }
336
337 // Send frame for rendering.
338 if (external_callback_) {
339 WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, module_id_,
340 "%s: executing external renderer callback to deliver frame",
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000341 __FUNCTION__, frame_to_render->render_time_ms());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000342 external_callback_->RenderFrame(stream_id_, *frame_to_render);
343 } else {
344 if (render_callback_) {
345 WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, module_id_,
346 "%s: Render frame, time: ", __FUNCTION__,
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000347 frame_to_render->render_time_ms());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000348 render_callback_->RenderFrame(stream_id_, *frame_to_render);
349 }
350 }
351
352 // Release critsect before calling the module user.
353 thread_critsect_.Leave();
354
355 // We're done with this frame, delete it.
356 if (frame_to_render) {
357 CriticalSectionScoped cs(&buffer_critsect_);
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000358 last_rendered_frame_.SwapFrame(frame_to_render);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000359 render_buffers_.ReturnFrame(frame_to_render);
360 }
361 }
362 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000363}
364
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000365int32_t IncomingVideoStream::GetLastRenderedFrame(
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000366 I420VideoFrame& video_frame) const {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000367 CriticalSectionScoped cs(&buffer_critsect_);
368 return video_frame.CopyFrame(last_rendered_frame_);
369}
niklase@google.com470e71d2011-07-07 08:21:25 +0000370
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000371} // namespace webrtc