xref: /haiku/src/kits/media/BufferGroup.cpp (revision 5bd0fbd13a1e832f91643aaa921fbc0879abd518)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 
31 #include <BufferGroup.h>
32 
33 #include <Buffer.h>
34 
35 #include "MediaDebug.h"
36 #include "DataExchange.h"
37 #include "SharedBufferList.h"
38 
39 
40 BBufferGroup::BBufferGroup(size_t size, int32 count, uint32 placement,
41 	uint32 lock)
42 {
43 	CALLED();
44 	fInitError = _Init();
45 	if (fInitError != B_OK)
46 		return;
47 
48 	// This one is easy. We need to create "count" BBuffers,
49 	// each one "size" bytes large. They all go into one
50 	// area, with "placement" and "lock" attributes.
51 	// The BBuffers created will clone the area, and
52 	// then we delete our area. This way BBuffers are
53 	// independent from the BBufferGroup
54 
55 	// don't allow all placement parameter values
56 	if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) {
57 		ERROR("BBufferGroup: placement != B_ANY_ADDRESS "
58 			"&& placement != B_ANY_KERNEL_ADDRESS (0x%#" B_PRIx32 ")\n",
59 			placement);
60 		placement = B_ANY_ADDRESS;
61 	}
62 
63 	// first we roundup for a better placement in memory
64 	size_t allocSize = (size + 63) & ~63;
65 
66 	// now we create the area
67 	size_t areaSize
68 		= ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
69 
70 	void* startAddress;
71 	area_id bufferArea = create_area("some buffers area", &startAddress,
72 		placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA);
73 	if (bufferArea < 0) {
74 		ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize);
75 		fInitError = (status_t)bufferArea;
76 		return;
77 	}
78 
79 	buffer_clone_info info;
80 
81 	for (int32 i = 0; i < count; i++) {
82 		info.area = bufferArea;
83 		info.offset = i * allocSize;
84 		info.size = size;
85 
86 		fInitError = AddBuffer(info);
87 		if (fInitError != B_OK)
88 			break;
89 	}
90 
91 	delete_area(bufferArea);
92 }
93 
94 
95 BBufferGroup::BBufferGroup()
96 {
97 	CALLED();
98 	fInitError = _Init();
99 	if (fInitError != B_OK)
100 		return;
101 
102 	// this one simply creates an empty BBufferGroup
103 }
104 
105 
106 BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers)
107 {
108 	CALLED();
109 	fInitError = _Init();
110 	if (fInitError != B_OK)
111 		return;
112 
113 	// This one creates "BBuffer"s from "media_buffer_id"s passed
114 	// by the application.
115 
116 	buffer_clone_info info;
117 
118 	for (int32 i = 0; i < count; i++) {
119 		info.buffer = buffers[i];
120 
121 		fInitError = AddBuffer(info);
122 		if (fInitError != B_OK)
123 			break;
124 	}
125 }
126 
127 
128 BBufferGroup::~BBufferGroup()
129 {
130 	CALLED();
131 	if (fBufferList != NULL)
132 		fBufferList->DeleteGroupAndPut(fReclaimSem);
133 
134 	delete_sem(fReclaimSem);
135 }
136 
137 
138 status_t
139 BBufferGroup::InitCheck()
140 {
141 	CALLED();
142 	return fInitError;
143 }
144 
145 
146 status_t
147 BBufferGroup::AddBuffer(const buffer_clone_info& info, BBuffer** _buffer)
148 {
149 	CALLED();
150 	if (fInitError != B_OK)
151 		return B_NO_INIT;
152 
153 	status_t status = fBufferList->AddBuffer(fReclaimSem, info, _buffer);
154 	if (status != B_OK) {
155 		ERROR("BBufferGroup: error when adding buffer\n");
156 		return status;
157 	}
158 	atomic_add(&fBufferCount, 1);
159 	return B_OK;
160 }
161 
162 
163 BBuffer*
164 BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout)
165 {
166 	CALLED();
167 	if (fInitError != B_OK)
168 		return NULL;
169 
170 	if (size <= 0)
171 		return NULL;
172 
173 	BBuffer *buffer = NULL;
174 	fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount,
175 		size, 0, &buffer, timeout);
176 
177 	return fRequestError == B_OK ? buffer : NULL;
178 }
179 
180 
181 status_t
182 BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout)
183 {
184 	CALLED();
185 	if (fInitError != B_OK)
186 		return B_NO_INIT;
187 
188 	if (buffer == NULL)
189 		return B_BAD_VALUE;
190 
191 	fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0,
192 		&buffer, timeout);
193 
194 	return fRequestError;
195 }
196 
197 
198 status_t
199 BBufferGroup::RequestError()
200 {
201 	CALLED();
202 	if (fInitError != B_OK)
203 		return B_NO_INIT;
204 
205 	return fRequestError;
206 }
207 
208 
209 status_t
210 BBufferGroup::CountBuffers(int32* _count)
211 {
212 	CALLED();
213 	if (fInitError != B_OK)
214 		return B_NO_INIT;
215 
216 	*_count = fBufferCount;
217 	return B_OK;
218 }
219 
220 
221 status_t
222 BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers)
223 {
224 	CALLED();
225 	if (fInitError != B_OK)
226 		return B_NO_INIT;
227 
228 	if (bufferCount <= 0 || bufferCount > fBufferCount)
229 		return B_BAD_VALUE;
230 
231 	return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers);
232 }
233 
234 
235 status_t
236 BBufferGroup::WaitForBuffers()
237 {
238 	CALLED();
239 	if (fInitError != B_OK)
240 		return B_NO_INIT;
241 
242 	// TODO: this function is not really useful anyway, and will
243 	// not work exactly as documented, but it is close enough
244 
245 	if (fBufferCount < 0)
246 		return B_BAD_VALUE;
247 	if (fBufferCount == 0)
248 		return B_OK;
249 
250 	// We need to wait until at least one buffer belonging to this group is
251 	// reclaimed.
252 	// This has happened when can aquire "fReclaimSem"
253 
254 	status_t status;
255 	while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED)
256 		;
257 	if (status != B_OK)
258 		return status;
259 
260 	// we need to release the "fReclaimSem" now, else we would block
261 	// requesting of new buffers
262 
263 	return release_sem(fReclaimSem);
264 }
265 
266 
267 status_t
268 BBufferGroup::ReclaimAllBuffers()
269 {
270 	CALLED();
271 	if (fInitError != B_OK)
272 		return B_NO_INIT;
273 
274 	// because additional BBuffers might get added to this group betweeen
275 	// acquire and release
276 	int32 count = fBufferCount;
277 
278 	if (count < 0)
279 		return B_BAD_VALUE;
280 	if (count == 0)
281 		return B_OK;
282 
283 	// we need to wait until all BBuffers belonging to this group are reclaimed.
284 	// this has happened when the "fReclaimSem" can be aquired "fBufferCount"
285 	// times
286 
287 	status_t status = B_ERROR;
288 	do {
289 		status = acquire_sem_etc(fReclaimSem, count, B_RELATIVE_TIMEOUT, 0);
290 	} while (status == B_INTERRUPTED);
291 
292 	if (status != B_OK)
293 		return status;
294 
295 	// we need to release the "fReclaimSem" now, else we would block
296 	// requesting of new buffers
297 
298 	return release_sem_etc(fReclaimSem, count, 0);
299 }
300 
301 
302 //	#pragma mark - deprecated BeOS R4 API
303 
304 
305 status_t
306 BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock)
307 {
308 	CALLED();
309 	if (fInitError != B_OK)
310 		return B_NO_INIT;
311 
312 	// BeOS R4 legacy API. Implemented as a wrapper around GetBufferList
313 	// "needLock" is ignored, GetBufferList will do locking
314 
315 	if (message == NULL)
316 		return B_BAD_VALUE;
317 
318 	if (name == NULL || strlen(name) == 0)
319 		return B_BAD_VALUE;
320 
321 	BBuffer* buffers[fBufferCount];
322 	status_t status = GetBufferList(fBufferCount, buffers);
323 	if (status != B_OK)
324 		return status;
325 
326 	for (int32 i = 0; i < fBufferCount; i++) {
327 		status = message->AddInt32(name, int32(buffers[i]->ID()));
328 		if (status != B_OK)
329 			return status;
330 	}
331 
332 	return B_OK;
333 }
334 
335 
336 //	#pragma mark - private methods
337 
338 
339 /* not implemented */
340 //BBufferGroup::BBufferGroup(const BBufferGroup &)
341 //BBufferGroup & BBufferGroup::operator=(const BBufferGroup &)
342 
343 
344 status_t
345 BBufferGroup::_Init()
346 {
347 	CALLED();
348 
349 	// some defaults in case we drop out early
350 	fBufferList = NULL;
351 	fRequestError = B_ERROR;
352 	fBufferCount = 0;
353 
354 	// Create the reclaim semaphore
355 	// This is also used as a system wide unique identifier for this group
356 	fReclaimSem = create_sem(0, "buffer reclaim sem");
357 	if (fReclaimSem < 0) {
358 		ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n");
359 		return (status_t)fReclaimSem;
360 	}
361 
362 	fBufferList = BPrivate::SharedBufferList::Get();
363 	if (fBufferList == NULL) {
364 		ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() "
365 			"failed\n");
366 		return B_ERROR;
367 	}
368 
369 	return B_OK;
370 }
371 
372