xref: /haiku/src/tests/kits/media/nodetest/ProducerNode.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 #include "ProducerNode.h"
2 
3 #include <string.h>
4 
5 #include <Buffer.h>
6 #include <BufferGroup.h>
7 #include <MediaNode.h>
8 #include <TimeSource.h>
9 
10 #include "misc.h"
11 
12 
13 #define DELAY 2000000
14 
15 
16 ProducerNode::ProducerNode()
17 	:
18 	BMediaNode("ProducerNode"),
19 	BBufferProducer(B_MEDIA_RAW_AUDIO),
20 	BMediaEventLooper(),
21 	mBufferGroup(0),
22 	mBufferProducerSem(-1),
23 	mBufferProducer(-1),
24 	mOutputEnabled(false)
25 {
26 	out("ProducerNode::ProducerNode\n");
27 	mBufferGroup = new BBufferGroup(4096,3);
28 }
29 
30 
31 ProducerNode::~ProducerNode()
32 {
33 	out("ProducerNode::~ProducerNode\n");
34 	Quit();
35 	delete mBufferGroup;
36 }
37 
38 
39 void
40 ProducerNode::NodeRegistered()
41 {
42 	out("ProducerNode::NodeRegistered\n");
43 	InitializeOutput();
44 	SetPriority(108);
45 	Run();
46 }
47 
48 
49 status_t
50 ProducerNode::GetNodeAttributes(
51 	media_node_attribute* attributes, size_t count)
52 {
53 	uint32 what = media_node_attribute::B_FIRST_USER_ATTRIBUTE;
54 	for (size_t i = 0; i < count; i++) {
55 		attributes[i].what = what;
56 		what++;
57 	}
58 	return B_OK;
59 }
60 
61 
62 status_t
63 ProducerNode::FormatSuggestionRequested(media_type type, int32 quality,
64 	media_format* format)
65 {
66 	out("ProducerNode::FormatSuggestionRequested\n");
67 
68 	if (type != B_MEDIA_RAW_AUDIO)
69 		return B_MEDIA_BAD_FORMAT;
70 
71 	format->u.raw_audio = media_raw_audio_format::wildcard;
72 	format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
73 	format->u.raw_audio.channel_count = 1;
74 	format->u.raw_audio.frame_rate = 44100;
75 	format->u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
76 
77 	return B_OK;
78 }
79 
80 
81 status_t
82 ProducerNode::FormatProposal(const media_source& output, media_format* format)
83 {
84 	out("ProducerNode::FormatProposal\n");
85 
86 	if (format == NULL)
87 		return B_BAD_VALUE;
88 
89 	if (output != mOutput.source)
90 		return B_MEDIA_BAD_SOURCE;
91 
92 	return B_OK;
93 }
94 
95 
96 status_t
97 ProducerNode::FormatChangeRequested(const media_source& source,
98 	const media_destination& destination, media_format* _format,
99 	int32* _deprecated_)
100 {
101 	out("ProducerNode::FormatChangeRequested\n");
102 	return B_ERROR;
103 }
104 
105 
106 status_t
107 ProducerNode::GetNextOutput(int32* cookie, media_output* _output)
108 {
109 	out("ProducerNode::GetNextOutput\n");
110 	if (++(*cookie) > 1)
111 		return B_BAD_INDEX;
112 
113 	mOutput.node = Node();
114 	*_output = mOutput;
115 	return B_OK;
116 }
117 
118 
119 status_t
120 ProducerNode::DisposeOutputCookie(int32 cookie)
121 {
122 	out("ProducerNode::DisposeOutputCookie\n");
123 	return B_OK;
124 }
125 
126 
127 /*!	In this function, you should either pass on the group to your upstream guy,
128 	or delete your current group and hang on to this group. Deleting the
129 	previous group (unless you passed it on with the reclaim flag set to false)
130 	is very important, else you will 1) leak memory and 2) block someone who may
131 	want to reclaim the buffers living in that group.
132 */
133 status_t
134 ProducerNode::SetBufferGroup(const media_source& forSource, BBufferGroup* group)
135 {
136 	out("ProducerNode::SetBufferGroup\n");
137 
138 	if (forSource != mOutput.source)
139 		return B_MEDIA_BAD_SOURCE;
140 
141 #if 0
142 	if (mBufferGroup != NULL && mBufferGroup != mOwnBufferGroup) {
143 		// fixme! really delete if it isn't ours ?
144 		trace("deleting buffer group!...\n");
145 		delete mBufferGroup;
146 		trace("done!\n");
147 	}
148 
149 	/* release the previous buffer group */
150 	if (mOwnBufferGroup != NULL) {
151 		delete_own_buffer_group();
152 	}
153 
154 	mBufferGroup = group;
155 
156 	/* allocate new buffer group if necessary */
157 	if (mBufferGroup == NULL) {
158 		create_own_buffer_group();
159 		mBufferGroup = mOwnBufferGroup;
160 	}
161 	return B_OK;
162 #endif
163 
164 	return B_ERROR;
165 }
166 
167 
168 status_t
169 ProducerNode::VideoClippingChanged(const media_source& forSource,
170 	int16 numShorts, int16* clipData, const media_video_display_info& display,
171 	int32* _deprecated_)
172 {
173 	out("ProducerNode::VideoClippingChanged\n");
174 	return B_ERROR;
175 }
176 
177 
178 status_t
179 ProducerNode::GetLatency(bigtime_t* _latency)
180 {
181 	out("ProducerNode::GetLatency\n");
182 	*_latency = 23000;
183 	return B_OK;
184 }
185 
186 
187 status_t
188 ProducerNode::PrepareToConnect(const media_source& what,
189 	const media_destination& where, media_format* format, media_source* _source,
190 	char* _name)
191 {
192 	out("ProducerNode::PrepareToConnect\n");
193 
194 	if (mOutput.source != what)
195 		return B_MEDIA_BAD_SOURCE;
196 
197 	if (mOutput.destination != media_destination::null)
198 		return B_MEDIA_ALREADY_CONNECTED;
199 
200 	if (format == NULL || _source == NULL || _name == NULL)
201 		return B_BAD_VALUE;
202 
203 #if 0
204 	ASSERT(mOutputEnabled == false);
205 
206 	trace("old format:\n");
207 	dump_format(format);
208 
209 	status_t status;
210 
211 	status = specialize_format_to_inputformat(format);
212 	if (status != B_OK)
213 		return status;
214 
215 #endif
216 
217 
218 	*_source = mOutput.source;
219 	strcpy(_name, mOutput.name);
220 	//mOutput.destination = where; //really now? fixme
221 
222 	return B_OK;
223 }
224 
225 
226 void
227 ProducerNode::Connect(status_t error, const media_source& source,
228 	const media_destination& destination, const media_format& format,
229 	char* name)
230 {
231 	out("ProducerNode::Connect\n");
232 
233 	if (error != B_OK) {
234 		InitializeOutput();
235 		return;
236 	}
237 /*
238 	if (mOutput.destination != destination) { //if connected in PrepareToConnect fixme?
239 		trace("error mOutput.destination != destination\n");
240 		return;
241 	}
242 */
243 	mOutput.destination = destination;
244 
245 	if (mOutput.source != source) {
246 		out("error mOutput.source != source\n");
247 		return;
248 	}
249 
250 	strcpy(name, mOutput.name);
251 
252 #if 0
253 	trace("format (final and approved):\n");
254 	dump_format(&format);
255 #endif
256 
257 	mOutputEnabled = true;
258 
259 	return;
260 }
261 
262 
263 void
264 ProducerNode::Disconnect(const media_source& what,
265 	const media_destination& where)
266 {
267 	out("ProducerNode::Disconnect\n");
268 	mOutputEnabled = false;
269 
270 	// unreserve connection
271 	InitializeOutput();
272 }
273 
274 
275 void
276 ProducerNode::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
277 	bigtime_t performanceTime)
278 {
279 	out("ProducerNode::LateNoticeReceived\n");
280 	return;
281 }
282 
283 
284 void
285 ProducerNode::EnableOutput(const media_source& what, bool enabled,
286 	int32* _deprecated_)
287 {
288 	out("ProducerNode::EnableOutput\n");
289 	mOutputEnabled = enabled;
290 	return;
291 }
292 
293 
294 BMediaAddOn*
295 ProducerNode::AddOn(int32* internalID) const
296 {
297 	out("ProducerNode::AddOn\n");
298 	return NULL;
299 }
300 
301 
302 void
303 ProducerNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
304 	bool realTimeEvent)
305 {
306 	out("ProducerNode::HandleEvent\n");
307 	switch (event->type) {
308 		case BTimedEventQueue::B_HANDLE_BUFFER:
309 			out("B_HANDLE_BUFFER (should not happen)\n");
310 			break;
311 
312 		case BTimedEventQueue::B_PARAMETER:
313 			out("B_PARAMETER\n");
314 			break;
315 
316 		case BTimedEventQueue::B_START:
317 		{
318 			out("B_START\n");
319 			if (mBufferProducer != -1) {
320 				out("already running\n");
321 				break;
322 			}
323 			mBufferProducerSem = create_sem(0, "producer blocking sem");
324 			mBufferProducer = spawn_thread(_bufferproducer, "Buffer Producer",
325 				B_NORMAL_PRIORITY, this);
326 			resume_thread(mBufferProducer);
327 			break;
328 		}
329 
330 		case BTimedEventQueue::B_STOP:
331 		{
332 			out("B_STOP\n");
333 			if (mBufferProducer == -1) {
334 				out("not running\n");
335 				break;
336 			}
337 			status_t err;
338 			delete_sem(mBufferProducerSem);
339 			wait_for_thread(mBufferProducer,&err);
340 			mBufferProducer = -1;
341 			mBufferProducerSem = -1;
342 
343 			// stopping implies not handling any more buffers.  So, we flush
344 			// all pending buffers out of the event queue before returning to
345 			// the event loop.
346 			EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
347 				BTimedEventQueue::B_HANDLE_BUFFER);
348 			break;
349 		}
350 
351 		case BTimedEventQueue::B_SEEK:
352 			out("B_SEEK\n");
353 			break;
354 
355 		case BTimedEventQueue::B_WARP:
356 			out("B_WARP\n");
357 			// similarly, time warps aren't meaningful to the logger, so just
358 			// record it and return
359 			//mLogger->Log(LOG_WARP_HANDLED, logMsg);
360 			break;
361 
362 		case BTimedEventQueue::B_DATA_STATUS:
363 			out("B_DATA_STATUS\n");
364 			break;
365 
366 		default:
367 			out("default\n");
368 			break;
369 	}
370 }
371 
372 
373 status_t
374 ProducerNode::HandleMessage(int32 message,const void *data, size_t size)
375 {
376 	out("ProducerNode::HandleMessage %lx\n",message);
377 	if (B_OK == BBufferProducer::HandleMessage(message,data,size))
378 		return B_OK;
379 	if (B_OK == BMediaEventLooper::HandleMessage(message,data,size))
380 		return B_OK;
381 	return BMediaNode::HandleMessage(message,data,size);
382 }
383 
384 
385 void
386 ProducerNode::AdditionalBufferRequested(const media_source& source,
387 	media_buffer_id previousBuffer, bigtime_t previousTime,
388 	const media_seek_tag* previousTag)
389 {
390 	out("ProducerNode::AdditionalBufferRequested\n");
391 	release_sem(mBufferProducerSem);
392 }
393 
394 
395 void
396 ProducerNode::LatencyChanged(const media_source& source,
397 	const media_destination& destination, bigtime_t newLatency, uint32 flags)
398 {
399 	out("ProducerNode::LatencyChanged\n");
400 }
401 
402 
403 void
404 ProducerNode::InitializeOutput()
405 {
406 	out("ConsumerNode::InitializeOutput()\n");
407 	mOutput.source.port = ControlPort();
408 	mOutput.source.id = 0;
409 	mOutput.destination = media_destination::null;
410 	mOutput.node = Node();
411 	mOutput.format.type = B_MEDIA_RAW_AUDIO;
412 	mOutput.format.u.raw_audio = media_raw_audio_format::wildcard;
413 	mOutput.format.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
414 	mOutput.format.u.raw_audio.channel_count = 1;
415 	mOutput.format.u.raw_audio.frame_rate = 44100;
416 	mOutput.format.u.raw_audio.byte_order = B_HOST_IS_BENDIAN
417 		? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
418 	strcpy(mOutput.name, "this way out");
419 }
420 
421 
422 int32
423 ProducerNode::_bufferproducer(void* arg)
424 {
425 	((ProducerNode*)arg)->BufferProducer();
426 	return 0;
427 }
428 
429 
430 void
431 ProducerNode::BufferProducer()
432 {
433 	// this thread produces one buffer each two seconds,
434 	// and shedules it to be handled one second later than produced
435 	// assuming a realtime timesource
436 
437 	status_t rv;
438 	for (;;) {
439 		rv = acquire_sem_etc(mBufferProducerSem, 1, B_RELATIVE_TIMEOUT, DELAY);
440 		if (rv == B_INTERRUPTED) {
441 			continue;
442 		} else if (rv == B_OK) {
443 			// triggered by AdditionalBufferRequested
444 			release_sem(mBufferProducerSem);
445 		} else if (rv != B_TIMED_OUT) {
446 			// triggered by deleting the semaphore (stop request)
447 			break;
448 		}
449 		if (!mOutputEnabled)
450 			continue;
451 
452 		BBuffer *buffer;
453 //		out("ProducerNode: RequestBuffer\n");
454 		buffer = mBufferGroup->RequestBuffer(2048);
455 		if (!buffer) {
456 		}
457 		buffer->Header()->start_time = TimeSource()->Now() + DELAY / 2;
458 		out("ProducerNode: SendBuffer, sheduled time = %5.4f\n",
459 			buffer->Header()->start_time / 1E6);
460 		rv = SendBuffer(buffer, mOutput.source, mOutput.destination);
461 		if (rv != B_OK) {
462 		}
463 	}
464 }
465