blob: bedb8be95d81e471d9e8ca37ec51876b01775019 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.org327ada12012-05-30 10:45:18 +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//video_render_frames.h"
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014
wu@webrtc.org9dba5252013-08-05 20:36:57 +000015#include "webrtc/common_video/interface/texture_video_frame.h"
pbos@webrtc.org5aa3f1b2013-07-12 08:12:08 +000016#include "webrtc/modules/interface/module_common_types.h"
17#include "webrtc/system_wrappers/interface/tick_util.h"
18#include "webrtc/system_wrappers/interface/trace.h"
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000019
niklase@google.com470e71d2011-07-07 08:21:25 +000020namespace webrtc {
21
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +000022const int32_t KEventMaxWaitTimeMs = 200;
23const uint32_t kMinRenderDelayMs = 10;
24const uint32_t kMaxRenderDelayMs= 500;
niklase@google.com470e71d2011-07-07 08:21:25 +000025
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000026VideoRenderFrames::VideoRenderFrames()
27 : incoming_frames_(),
28 render_delay_ms_(10) {
29}
30
31VideoRenderFrames::~VideoRenderFrames() {
32 ReleaseAllFrames();
33}
34
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +000035int32_t VideoRenderFrames::AddFrame(I420VideoFrame* new_frame) {
36 const int64_t time_now = TickTime::MillisecondTimestamp();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000037
pbos@webrtc.orgeb7b7bc2013-12-16 18:24:37 +000038 // Drop old frames only when there are other frames in the queue, otherwise, a
39 // really slow system never renders any frames.
40 if (!incoming_frames_.Empty() &&
41 new_frame->render_time_ms() + KOldRenderTimestampMS < time_now) {
42 WEBRTC_TRACE(kTraceWarning,
43 kTraceVideoRenderer,
44 -1,
hclam@chromium.org0d540c32013-05-21 00:16:01 +000045 "%s: too old frame, timestamp=%u.",
pbos@webrtc.orgeb7b7bc2013-12-16 18:24:37 +000046 __FUNCTION__,
47 new_frame->timestamp());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000048 return -1;
49 }
pbos@webrtc.orgeb7b7bc2013-12-16 18:24:37 +000050
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000051 if (new_frame->render_time_ms() > time_now + KFutureRenderTimestampMS) {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000052 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1,
hclam@chromium.org0d540c32013-05-21 00:16:01 +000053 "%s: frame too long into the future, timestamp=%u.",
54 __FUNCTION__, new_frame->timestamp());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000055 return -1;
56 }
57
wu@webrtc.org9dba5252013-08-05 20:36:57 +000058 if (new_frame->native_handle() != NULL) {
59 incoming_frames_.PushBack(new TextureVideoFrame(
60 static_cast<NativeHandle*>(new_frame->native_handle()),
61 new_frame->width(),
62 new_frame->height(),
63 new_frame->timestamp(),
64 new_frame->render_time_ms()));
65 return incoming_frames_.GetSize();
66 }
67
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000068 // Get an empty frame
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000069 I420VideoFrame* frame_to_add = NULL;
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000070 if (!empty_frames_.Empty()) {
71 ListItem* item = empty_frames_.First();
72 if (item) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000073 frame_to_add = static_cast<I420VideoFrame*>(item->GetItem());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000074 empty_frames_.Erase(item);
75 }
76 }
77 if (!frame_to_add) {
78 if (empty_frames_.GetSize() + incoming_frames_.GetSize() >
79 KMaxNumberOfFrames) {
80 // Already allocated too many frames.
81 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer,
hclam@chromium.org0d540c32013-05-21 00:16:01 +000082 -1, "%s: too many frames, timestamp=%u, limit=%d",
83 __FUNCTION__, new_frame->timestamp(), KMaxNumberOfFrames);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000084 return -1;
85 }
86
87 // Allocate new memory.
88 WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, -1,
89 "%s: allocating buffer %d", __FUNCTION__,
90 empty_frames_.GetSize() + incoming_frames_.GetSize());
91
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +000092 frame_to_add = new I420VideoFrame();
mflodman@webrtc.org327ada12012-05-30 10:45:18 +000093 if (!frame_to_add) {
94 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1,
95 "%s: could not create new frame for", __FUNCTION__);
96 return -1;
97 }
98 }
99
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000100 frame_to_add->CreateEmptyFrame(new_frame->width(), new_frame->height(),
101 new_frame->stride(kYPlane),
102 new_frame->stride(kUPlane),
103 new_frame->stride(kVPlane));
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000104 // TODO(mflodman) Change this!
105 // Remove const ness. Copying will be costly.
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000106 frame_to_add->SwapFrame(new_frame);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000107 incoming_frames_.PushBack(frame_to_add);
108
109 return incoming_frames_.GetSize();
110}
111
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000112I420VideoFrame* VideoRenderFrames::FrameToRender() {
113 I420VideoFrame* render_frame = NULL;
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000114 while (!incoming_frames_.Empty()) {
115 ListItem* item = incoming_frames_.First();
116 if (item) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000117 I420VideoFrame* oldest_frame_in_list =
118 static_cast<I420VideoFrame*>(item->GetItem());
119 if (oldest_frame_in_list->render_time_ms() <=
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000120 TickTime::MillisecondTimestamp() + render_delay_ms_) {
121 // This is the oldest one so far and it's OK to render.
122 if (render_frame) {
123 // This one is older than the newly found frame, remove this one.
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000124 ReturnFrame(render_frame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000125 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000126 render_frame = oldest_frame_in_list;
127 incoming_frames_.Erase(item);
128 } else {
129 // We can't release this one yet, we're done here.
130 break;
131 }
132 } else {
133 assert(false);
niklase@google.com470e71d2011-07-07 08:21:25 +0000134 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000135 }
136 return render_frame;
niklase@google.com470e71d2011-07-07 08:21:25 +0000137}
138
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000139int32_t VideoRenderFrames::ReturnFrame(I420VideoFrame* old_frame) {
wu@webrtc.org9dba5252013-08-05 20:36:57 +0000140 // No need to reuse texture frames because they do not allocate memory.
141 if (old_frame->native_handle() == NULL) {
142 old_frame->ResetSize();
143 old_frame->set_timestamp(0);
144 old_frame->set_render_time_ms(0);
145 empty_frames_.PushBack(old_frame);
146 } else {
147 delete old_frame;
148 }
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000149 return 0;
150}
151
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000152int32_t VideoRenderFrames::ReleaseAllFrames() {
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000153 while (!incoming_frames_.Empty()) {
154 ListItem* item = incoming_frames_.First();
155 if (item) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000156 I420VideoFrame* frame = static_cast<I420VideoFrame*>(item->GetItem());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000157 assert(frame != NULL);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000158 delete frame;
159 }
160 incoming_frames_.Erase(item);
161 }
162 while (!empty_frames_.Empty()) {
163 ListItem* item = empty_frames_.First();
164 if (item) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000165 I420VideoFrame* frame = static_cast<I420VideoFrame*>(item->GetItem());
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000166 assert(frame != NULL);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000167 delete frame;
168 }
169 empty_frames_.Erase(item);
170 }
171 return 0;
172}
173
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000174uint32_t VideoRenderFrames::TimeToNextFrameRelease() {
175 int64_t time_to_release = 0;
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000176 ListItem* item = incoming_frames_.First();
177 if (item) {
mikhal@webrtc.org9fedff72012-10-24 18:33:04 +0000178 I420VideoFrame* oldest_frame =
179 static_cast<I420VideoFrame*>(item->GetItem());
180 time_to_release = oldest_frame->render_time_ms() - render_delay_ms_
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000181 - TickTime::MillisecondTimestamp();
182 if (time_to_release < 0) {
183 time_to_release = 0;
184 }
185 } else {
186 time_to_release = KEventMaxWaitTimeMs;
187 }
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000188 return static_cast<uint32_t>(time_to_release);
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000189}
190
pbos@webrtc.orgddf94e72013-04-10 08:09:04 +0000191int32_t VideoRenderFrames::SetRenderDelay(
192 const uint32_t render_delay) {
mflodman@webrtc.orgf4f21452012-09-28 11:27:35 +0000193 if (render_delay < kMinRenderDelayMs ||
194 render_delay > kMaxRenderDelayMs) {
195 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer,
196 -1, "%s(%d): Invalid argument.", __FUNCTION__,
197 render_delay);
198 return -1;
199 }
200
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000201 render_delay_ms_ = render_delay;
202 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000203}
204
mflodman@webrtc.org327ada12012-05-30 10:45:18 +0000205} // namespace webrtc