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/modules/video_render/video_render_frames.h"
12
13#include <assert.h>
14
15#include "webrtc/common_video/interface/texture_video_frame.h"
16#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"
19
20namespace webrtc {
21
22const uint32_t KEventMaxWaitTimeMs = 200;
23const uint32_t kMinRenderDelayMs = 10;
24const uint32_t kMaxRenderDelayMs= 500;
25
26VideoRenderFrames::VideoRenderFrames()
27    : render_delay_ms_(10) {
28}
29
30VideoRenderFrames::~VideoRenderFrames() {
31  ReleaseAllFrames();
32}
33
34int32_t VideoRenderFrames::AddFrame(I420VideoFrame* new_frame) {
35  const int64_t time_now = TickTime::MillisecondTimestamp();
36
37  // Drop old frames only when there are other frames in the queue, otherwise, a
38  // really slow system never renders any frames.
39  if (!incoming_frames_.empty() &&
40      new_frame->render_time_ms() + KOldRenderTimestampMS < time_now) {
41    WEBRTC_TRACE(kTraceWarning,
42                 kTraceVideoRenderer,
43                 -1,
44                 "%s: too old frame, timestamp=%u.",
45                 __FUNCTION__,
46                 new_frame->timestamp());
47    return -1;
48  }
49
50  if (new_frame->render_time_ms() > time_now + KFutureRenderTimestampMS) {
51    WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1,
52                 "%s: frame too long into the future, timestamp=%u.",
53                 __FUNCTION__, new_frame->timestamp());
54    return -1;
55  }
56
57  if (new_frame->native_handle() != NULL) {
58    incoming_frames_.push_back(new_frame->CloneFrame());
59    return static_cast<int32_t>(incoming_frames_.size());
60  }
61
62  // Get an empty frame
63  I420VideoFrame* frame_to_add = NULL;
64  if (!empty_frames_.empty()) {
65    frame_to_add = empty_frames_.front();
66    empty_frames_.pop_front();
67  }
68  if (!frame_to_add) {
69    if (empty_frames_.size() + incoming_frames_.size() >
70        KMaxNumberOfFrames) {
71      // Already allocated too many frames.
72      WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer,
73                   -1, "%s: too many frames, timestamp=%u, limit=%d",
74                   __FUNCTION__, new_frame->timestamp(), KMaxNumberOfFrames);
75      return -1;
76    }
77
78    // Allocate new memory.
79    WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, -1,
80                 "%s: allocating buffer %d", __FUNCTION__,
81                 empty_frames_.size() + incoming_frames_.size());
82
83    frame_to_add = new I420VideoFrame();
84    if (!frame_to_add) {
85      WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1,
86                   "%s: could not create new frame for", __FUNCTION__);
87      return -1;
88    }
89  }
90
91  frame_to_add->CreateEmptyFrame(new_frame->width(), new_frame->height(),
92                                 new_frame->stride(kYPlane),
93                                 new_frame->stride(kUPlane),
94                                 new_frame->stride(kVPlane));
95  // TODO(mflodman) Change this!
96  // Remove const ness. Copying will be costly.
97  frame_to_add->SwapFrame(new_frame);
98  incoming_frames_.push_back(frame_to_add);
99
100  return static_cast<int32_t>(incoming_frames_.size());
101}
102
103I420VideoFrame* VideoRenderFrames::FrameToRender() {
104  I420VideoFrame* render_frame = NULL;
105  FrameList::iterator iter = incoming_frames_.begin();
106  while(iter != incoming_frames_.end()) {
107    I420VideoFrame* oldest_frame_in_list = *iter;
108    if (oldest_frame_in_list->render_time_ms() <=
109        TickTime::MillisecondTimestamp() + render_delay_ms_) {
110      // This is the oldest one so far and it's OK to render.
111      if (render_frame) {
112        // This one is older than the newly found frame, remove this one.
113        ReturnFrame(render_frame);
114      }
115      render_frame = oldest_frame_in_list;
116      iter = incoming_frames_.erase(iter);
117    } else {
118      // We can't release this one yet, we're done here.
119      break;
120    }
121  }
122  return render_frame;
123}
124
125int32_t VideoRenderFrames::ReturnFrame(I420VideoFrame* old_frame) {
126  // No need to reuse texture frames because they do not allocate memory.
127  if (old_frame->native_handle() == NULL) {
128    old_frame->ResetSize();
129    old_frame->set_timestamp(0);
130    old_frame->set_render_time_ms(0);
131    empty_frames_.push_back(old_frame);
132  } else {
133    delete old_frame;
134  }
135  return 0;
136}
137
138int32_t VideoRenderFrames::ReleaseAllFrames() {
139  for (FrameList::iterator iter = incoming_frames_.begin();
140       iter != incoming_frames_.end(); ++iter) {
141      delete *iter;
142  }
143  incoming_frames_.clear();
144
145  for (FrameList::iterator iter = empty_frames_.begin();
146       iter != empty_frames_.end(); ++iter) {
147      delete *iter;
148  }
149  empty_frames_.clear();
150  return 0;
151}
152
153uint32_t VideoRenderFrames::TimeToNextFrameRelease() {
154  if (incoming_frames_.empty()) {
155    return KEventMaxWaitTimeMs;
156  }
157  I420VideoFrame* oldest_frame = incoming_frames_.front();
158  int64_t time_to_release = oldest_frame->render_time_ms() - render_delay_ms_
159      - TickTime::MillisecondTimestamp();
160  if (time_to_release < 0) {
161    time_to_release = 0;
162  }
163  return static_cast<uint32_t>(time_to_release);
164}
165
166int32_t VideoRenderFrames::SetRenderDelay(
167    const uint32_t render_delay) {
168  if (render_delay < kMinRenderDelayMs ||
169      render_delay > kMaxRenderDelayMs) {
170    WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer,
171                 -1, "%s(%d): Invalid argument.", __FUNCTION__,
172                 render_delay);
173    return -1;
174  }
175
176  render_delay_ms_ = render_delay;
177  return 0;
178}
179
180}  // namespace webrtc
181