UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
video_frame_pool.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2020-2024 CESNET
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include "config_msvc.h"
39
40#include "video_frame_pool.h"
41
42#include <cassert>
43#include <cstdlib> // for free, malloc
44#include <condition_variable>
45#include <exception> // for exception
46#include <functional>
47#include <iostream>
48#include <memory>
49#include <mutex>
50#include <queue>
51#include <stdexcept>
52#include <utility>
53
54#include "video_codec.h"
55#include "video_frame.h"
56
58 public:
59 explicit impl(
60 unsigned int max_used_frames = 0,
62 ~impl();
63 void reconfigure(struct video_desc new_desc,
64 size_t new_size = SIZE_MAX);
65 std::shared_ptr<video_frame> get_frame();
67 struct video_frame *get_pod_frame();
69
70 private:
71 void remove_free_frames();
73
74 std::unique_ptr<video_frame_pool_allocator> m_allocator = std::unique_ptr<video_frame_pool_allocator>(new default_data_allocator);
75 std::queue<struct video_frame *> m_free_frames;
76 std::mutex m_lock;
77 std::condition_variable m_frame_returned;
78 int m_generation = 0;
80 size_t m_max_data_len = 0;
81 unsigned int m_unreturned_frames = 0;
82 unsigned int m_max_used_frames;
83};
84
85// _
86// . _| _ _ (_ _ _ _ _ _ _ _ |
87// \/ | (_| (- (_) __ | | (_| ||| (- __ |_) (_) (_) |
88// |
90video_frame_pool::video_frame_pool(unsigned int max_used_frames,
91 video_frame_pool_allocator const &alloc)
92 : m_impl(std::make_unique<video_frame_pool::impl>(max_used_frames, alloc))
93{
94}
96void
97video_frame_pool::reconfigure(struct video_desc new_desc, size_t new_size)
98{
99 m_impl->reconfigure(new_desc, new_size);
100}
101std::shared_ptr<video_frame>
103{
104 return m_impl->get_frame();
105}
106struct video_frame *
108{
109 return m_impl->get_disposable_frame();
110}
111struct video_frame *
113{
114 return m_impl->get_pod_frame();
115}
118{
119 return m_impl->get_allocator();
120}
121
122// _
123// _| _ (_ _ | |_ _| _ |_ _ _ | | _ _ _ |_ _ _
124// (_| (- | (_| |_| | |_ __ (_| (_| |_ (_| __ (_| | | (_) (_ (_| |_ (_) |
125//
127 return malloc(size);
128}
130 free(ptr);
131}
135
136// _
137// . _| _ _ (_ _ _ _ _ _ _ _ | . . . _ _ |
138// \/ | (_| (- (_) __ | | (_| ||| (- __ |_) (_) (_) | . . | ||| |_) |
139// | |
140video_frame_pool::impl::impl(unsigned int max_used_frames,
141 video_frame_pool_allocator const &alloc)
142 : m_allocator(alloc.clone()), m_max_used_frames(max_used_frames)
143{
144}
145
147 std::unique_lock<std::mutex> lk(m_lock);
148 remove_free_frames();
149 // wait also for all frames we gave out to return us
150 m_frame_returned.wait(lk, [this] {return m_unreturned_frames == 0;});
151}
152
153void video_frame_pool::impl::reconfigure(struct video_desc new_desc, size_t new_size) {
154 std::unique_lock<std::mutex> lk(m_lock);
155 m_desc = new_desc;
156 m_max_data_len = new_size != SIZE_MAX ? new_size : new_desc.height * vc_get_linesize(new_desc.width, new_desc.color_spec);
157 remove_free_frames();
158 m_generation++;
159}
160
161std::shared_ptr<video_frame> video_frame_pool::impl::get_frame() {
162 struct video_frame *ret = NULL;
163 std::unique_lock<std::mutex> lk(m_lock);
164 assert(m_generation != 0);
165 if (!m_free_frames.empty()) {
166 ret = m_free_frames.front();
167 m_free_frames.pop();
168 } else if (m_max_used_frames > 0 && m_max_used_frames == m_unreturned_frames) {
169 m_frame_returned.wait(lk, [this] {return m_unreturned_frames < m_max_used_frames;});
170 assert(!m_free_frames.empty());
171 ret = m_free_frames.front();
172 m_free_frames.pop();
173 } else {
174 try {
175 ret = vf_alloc_desc(m_desc);
176 for (unsigned int i = 0; i < m_desc.tile_count; ++i) {
177 ret->tiles[i].data =
178 (char *) m_allocator->allocate(
179 m_max_data_len + MAX_PADDING);
180 if (ret->tiles[i].data == NULL) {
181 throw std::runtime_error("Cannot allocate data");
182 }
183 ret->tiles[i].data_len = m_max_data_len;
184 }
185 } catch (std::exception &e) {
186 std::cerr << e.what() << std::endl;
187 deallocate_frame(ret);
188 throw e;
189 }
190 }
191 m_unreturned_frames += 1;
192 return std::shared_ptr<video_frame>(ret, std::bind([this](struct video_frame *frame, int generation) {
193 std::unique_lock<std::mutex> lk(m_lock);
194
195 assert(m_unreturned_frames > 0);
196 m_unreturned_frames -= 1;
197 m_frame_returned.notify_one();
198
199 if (this->m_generation != generation) {
200 this->deallocate_frame(frame);
201 } else {
202 m_free_frames.push(frame);
203 }
204 }, std::placeholders::_1, m_generation));
205}
206
208 auto && frame = get_frame();
209 struct video_frame *out = frame.get();
211 new std::shared_ptr<video_frame>(std::move(frame));
212 static auto dispose = [](video_frame *f) { delete static_cast<std::shared_ptr<video_frame> *>(f->callbacks.dispose_udata); };
213 out->callbacks.dispose = dispose;
214 return out;
215}
216
218 auto && frame = get_frame();
220 for (unsigned int i = 0; i < frame->tile_count; ++i) {
221 out->tiles[i].data = frame->tiles[i].data;
222 }
224 new std::shared_ptr<video_frame>(std::move(frame));
225 static auto deleter = [](video_frame *f) { delete static_cast<std::shared_ptr<video_frame> *>(f->callbacks.dispose_udata); };
227 return out;
228}
229
233
235 while (!m_free_frames.empty()) {
236 struct video_frame *frame = m_free_frames.front();
237 m_free_frames.pop();
238 deallocate_frame(frame);
239 }
240}
241
243 if (frame == NULL)
244 return;
245 for (unsigned int i = 0; i < frame->tile_count; ++i) {
246 m_allocator->deallocate(frame->tiles[i].data);
247 }
248 vf_free(frame);
249}
250
251// __ __
252// / /\ |__) |
253// \__ /--\ | |
254//
256 auto *out = new video_frame_pool(len, default_data_allocator());
257 out->reconfigure(desc);
258 return (void *) out;
259}
260
262 auto *s = static_cast<video_frame_pool* >(state);
263 return s->get_disposable_frame();
264}
265
267 auto *s = static_cast<video_frame_pool* >(state);
268 delete s;
269}
void ** state
struct video_desc desc
struct video_frame * frame
int void size_t * len
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
struct video_frame_pool_allocator * clone() const override
void * allocate(size_t size) override
void deallocate(void *ptr) override
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
codec_t color_spec
Definition types.h:182
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
void(* data_deleter)(struct video_frame *)
Definition types.h:256
void(* dispose)(struct video_frame *)
Definition types.h:245
void reconfigure(struct video_desc new_desc, size_t new_size=SIZE_MAX)
std::shared_ptr< video_frame > get_frame()
std::unique_ptr< video_frame_pool_allocator > m_allocator
video_frame_pool_allocator const & get_allocator()
impl(unsigned int max_used_frames=0, video_frame_pool_allocator const &alloc=default_data_allocator())
std::queue< struct video_frame * > m_free_frames
void deallocate_frame(struct video_frame *frame)
std::condition_variable m_frame_returned
struct video_frame * get_pod_frame()
struct video_frame * get_disposable_frame()
virtual struct video_frame_pool_allocator * clone() const =0
struct video_frame * get_pod_frame()
std::unique_ptr< impl > m_impl
video_frame_pool(unsigned int max_used_frames=0, video_frame_pool_allocator const &alloc=default_data_allocator())
video_frame_pool_allocator const & get_allocator()
struct video_frame * get_disposable_frame()
std::shared_ptr< video_frame > get_frame()
video_frame_pool & operator=(video_frame_pool &&)
void reconfigure(struct video_desc new_desc, size_t new_size=SIZE_MAX)
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
unsigned int tile_count
Definition types.h:344
struct video_frame_callbacks callbacks
Definition types.h:324
#define MAX_PADDING
Definition video_codec.h:63
EXTERN_C struct video_frame * video_frame_pool_get_disposable_frame(void *)
EXTERN_C void video_frame_pool_destroy(void *)
EXTERN_C void * video_frame_pool_init(struct video_desc desc, int len)