1/*
2 *  Copyright (c) 2011 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/utility/source/video_frames_queue.h"
12
13#ifdef WEBRTC_MODULE_UTILITY_VIDEO
14
15#include <assert.h>
16
17#include "webrtc/common_video/interface/texture_video_frame.h"
18#include "webrtc/modules/interface/module_common_types.h"
19#include "webrtc/system_wrappers/interface/logging.h"
20#include "webrtc/system_wrappers/interface/tick_util.h"
21
22namespace webrtc {
23VideoFramesQueue::VideoFramesQueue()
24    : _renderDelayMs(10)
25{
26}
27
28VideoFramesQueue::~VideoFramesQueue() {
29  for (FrameList::iterator iter = _incomingFrames.begin();
30       iter != _incomingFrames.end(); ++iter) {
31      delete *iter;
32  }
33  for (FrameList::iterator iter = _emptyFrames.begin();
34       iter != _emptyFrames.end(); ++iter) {
35      delete *iter;
36  }
37}
38
39int32_t VideoFramesQueue::AddFrame(const I420VideoFrame& newFrame) {
40  if (newFrame.native_handle() != NULL) {
41    _incomingFrames.push_back(newFrame.CloneFrame());
42    return 0;
43  }
44
45  I420VideoFrame* ptrFrameToAdd = NULL;
46  // Try to re-use a VideoFrame. Only allocate new memory if it is necessary.
47  if (!_emptyFrames.empty()) {
48    ptrFrameToAdd = _emptyFrames.front();
49    _emptyFrames.pop_front();
50  }
51  if (!ptrFrameToAdd) {
52    if (_emptyFrames.size() + _incomingFrames.size() >
53        KMaxNumberOfFrames) {
54      LOG(LS_WARNING) << "Too many frames, limit: " << KMaxNumberOfFrames;
55      return -1;
56    }
57    ptrFrameToAdd = new I420VideoFrame();
58  }
59  ptrFrameToAdd->CopyFrame(newFrame);
60  _incomingFrames.push_back(ptrFrameToAdd);
61  return 0;
62}
63
64// Find the most recent frame that has a VideoFrame::RenderTimeMs() that is
65// lower than current time in ms (TickTime::MillisecondTimestamp()).
66// Note _incomingFrames is sorted so that the oldest frame is first.
67// Recycle all frames that are older than the most recent frame.
68I420VideoFrame* VideoFramesQueue::FrameToRecord() {
69  I420VideoFrame* ptrRenderFrame = NULL;
70  for (FrameList::iterator iter = _incomingFrames.begin();
71       iter != _incomingFrames.end(); ++iter) {
72    I420VideoFrame* ptrOldestFrameInList = *iter;
73    if (ptrOldestFrameInList->render_time_ms() <=
74        TickTime::MillisecondTimestamp() + _renderDelayMs) {
75      // List is traversed beginning to end. If ptrRenderFrame is not
76      // NULL it must be the first, and thus oldest, VideoFrame in the
77      // queue. It can be recycled.
78      if (ptrRenderFrame) {
79        ReturnFrame(ptrRenderFrame);
80       _incomingFrames.pop_front();
81      }
82      ptrRenderFrame = ptrOldestFrameInList;
83    } else {
84      // All VideoFrames following this one will be even newer. No match
85      // will be found.
86      break;
87    }
88  }
89  return ptrRenderFrame;
90}
91
92int32_t VideoFramesQueue::ReturnFrame(I420VideoFrame* ptrOldFrame) {
93  // No need to reuse texture frames because they do not allocate memory.
94  if (ptrOldFrame->native_handle() == NULL) {
95    ptrOldFrame->set_timestamp(0);
96    ptrOldFrame->set_width(0);
97    ptrOldFrame->set_height(0);
98    ptrOldFrame->set_render_time_ms(0);
99    ptrOldFrame->ResetSize();
100    _emptyFrames.push_back(ptrOldFrame);
101  } else {
102    delete ptrOldFrame;
103  }
104  return 0;
105}
106
107int32_t VideoFramesQueue::SetRenderDelay(uint32_t renderDelay) {
108  _renderDelayMs = renderDelay;
109  return 0;
110}
111}  // namespace webrtc
112#endif // WEBRTC_MODULE_UTILITY_VIDEO
113