headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / media / experimental / MediaClientNode.cpp
blob23e3565e1287b365e4a94c4f8b6a8986e64a5da2
1 /*
2 * Copyright 2015, Dario Casalinuovo. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "MediaClientNode.h"
8 #include <MediaClient.h>
9 #include <MediaConnection.h>
10 #include <scheduler.h>
11 #include <TimeSource.h>
13 #include <string.h>
15 #include "debug.h"
17 #define B_NEW_BUFFER (BTimedEventQueue::B_USER_EVENT + 1)
20 BMediaClientNode::BMediaClientNode(const char* name,
21 BMediaClient* owner, media_type type)
23 BMediaNode(name),
24 BBufferConsumer(type),
25 BBufferProducer(type),
26 BMediaEventLooper(),
27 fOwner(owner)
29 CALLED();
31 // Configure the node to do the requested jobs
32 if (fOwner->Kinds() & B_MEDIA_PLAYER)
33 AddNodeKind(B_BUFFER_PRODUCER);
34 if (fOwner->Kinds() & B_MEDIA_RECORDER)
35 AddNodeKind(B_BUFFER_CONSUMER);
36 if (fOwner->Kinds() & B_MEDIA_CONTROLLABLE)
37 AddNodeKind(B_CONTROLLABLE);
41 status_t
42 BMediaClientNode::SendBuffer(BBuffer* buffer, BMediaConnection* conn)
44 return BBufferProducer::SendBuffer(buffer, conn->_Source(), conn->_Destination());
48 BMediaAddOn*
49 BMediaClientNode::AddOn(int32* id) const
51 CALLED();
53 return fOwner->AddOn(id);
57 void
58 BMediaClientNode::NodeRegistered()
60 CALLED();
62 Run();
66 void
67 BMediaClientNode::SetRunMode(run_mode mode)
69 CALLED();
71 int32 priority;
72 if (mode == BMediaNode::B_OFFLINE)
73 priority = B_OFFLINE_PROCESSING;
74 else {
75 switch(ConsumerType()) {
76 case B_MEDIA_RAW_AUDIO:
77 case B_MEDIA_ENCODED_AUDIO:
78 priority = B_AUDIO_RECORDING;
79 break;
81 case B_MEDIA_RAW_VIDEO:
82 case B_MEDIA_ENCODED_VIDEO:
83 priority = B_VIDEO_RECORDING;
84 break;
86 default:
87 priority = B_DEFAULT_MEDIA_PRIORITY;
91 SetPriority(suggest_thread_priority(priority));
92 BMediaNode::SetRunMode(mode);
96 void
97 BMediaClientNode::Start(bigtime_t performanceTime)
99 CALLED();
101 BMediaEventLooper::Start(performanceTime);
105 void
106 BMediaClientNode::Stop(bigtime_t performanceTime, bool immediate)
108 CALLED();
110 BMediaEventLooper::Stop(performanceTime, immediate);
114 void
115 BMediaClientNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
117 CALLED();
119 BMediaEventLooper::Seek(mediaTime, performanceTime);
123 void
124 BMediaClientNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
126 CALLED();
128 BMediaEventLooper::TimeWarp(realTime, performanceTime);
132 status_t
133 BMediaClientNode::HandleMessage(int32 message,
134 const void* data, size_t size)
136 CALLED();
138 return B_ERROR;
142 status_t
143 BMediaClientNode::AcceptFormat(const media_destination& dest,
144 media_format* format)
146 CALLED();
148 BMediaInput* conn = fOwner->_FindInput(dest);
149 if (conn == NULL)
150 return B_MEDIA_BAD_DESTINATION;
152 if (format_is_compatible(*format, conn->AcceptedFormat()))
153 return B_OK;
155 *format = conn->AcceptedFormat();
157 return B_MEDIA_BAD_FORMAT;
161 status_t
162 BMediaClientNode::GetNextInput(int32* cookie,
163 media_input* input)
165 CALLED();
167 if (fOwner->CountInputs() == 0)
168 return B_BAD_INDEX;
170 if (*cookie < 0 || *cookie >= fOwner->CountInputs()) {
171 *cookie = -1;
172 input = NULL;
173 } else {
174 BMediaInput* conn = fOwner->InputAt(*cookie);
175 if (conn != NULL) {
176 *input = conn->_MediaInput();
177 *cookie += 1;
178 return B_OK;
181 return B_BAD_INDEX;
185 void
186 BMediaClientNode::DisposeInputCookie(int32 cookie)
188 CALLED();
192 void
193 BMediaClientNode::BufferReceived(BBuffer* buffer)
195 CALLED();
197 EventQueue()->AddEvent(media_timed_event(buffer->Header()->start_time,
198 BTimedEventQueue::B_HANDLE_BUFFER, buffer,
199 BTimedEventQueue::B_RECYCLE_BUFFER));
203 status_t
204 BMediaClientNode::GetLatencyFor(const media_destination& dest,
205 bigtime_t* latency, media_node_id* timesource)
207 CALLED();
209 BMediaInput* conn = fOwner->_FindInput(dest);
210 if (conn == NULL)
211 return B_MEDIA_BAD_DESTINATION;
213 *latency = conn->fMaxLatency;
214 *timesource = TimeSource()->ID();
215 return B_OK;
219 status_t
220 BMediaClientNode::Connected(const media_source& source,
221 const media_destination& dest, const media_format& format,
222 media_input* outInput)
224 CALLED();
226 BMediaInput* conn = fOwner->_FindInput(dest);
227 if (conn == NULL)
228 return B_MEDIA_BAD_DESTINATION;
230 conn->fConnection.source = source;
231 conn->SetAcceptedFormat(format);
233 conn->Connected(format);
235 *outInput = conn->_MediaInput();
236 return B_OK;
240 void
241 BMediaClientNode::Disconnected(const media_source& source,
242 const media_destination& dest)
244 CALLED();
246 BMediaInput* conn = fOwner->_FindInput(dest);
247 if (conn == NULL)
248 return;
250 if (conn->_Source() == source) {
251 conn->Disconnect();
252 conn->Disconnected();
257 status_t
258 BMediaClientNode::FormatChanged(const media_source& source,
259 const media_destination& dest,
260 int32 tag, const media_format& format)
262 CALLED();
264 BMediaInput* conn = fOwner->_FindInput(dest);
265 if (conn == NULL)
266 return B_MEDIA_BAD_DESTINATION;
268 return conn->FormatChanged(format);
272 status_t
273 BMediaClientNode::FormatSuggestionRequested(media_type type,
274 int32 quality, media_format* format)
276 CALLED();
278 if (type != ConsumerType()
279 && type != ProducerType()) {
280 return B_MEDIA_BAD_FORMAT;
283 status_t ret = fOwner->FormatSuggestion(type, quality, format);
284 if (ret != B_OK) {
285 // In that case we return just a very generic format.
286 media_format outFormat;
287 outFormat.type = fOwner->MediaType();
288 *format = outFormat;
289 return B_OK;
292 return ret;
296 status_t
297 BMediaClientNode::FormatProposal(const media_source& source,
298 media_format* format)
300 CALLED();
302 BMediaOutput* conn = fOwner->_FindOutput(source);
303 if (conn == NULL)
304 return B_MEDIA_BAD_DESTINATION;
306 return conn->FormatProposal(format);
310 status_t
311 BMediaClientNode::FormatChangeRequested(const media_source& source,
312 const media_destination& dest, media_format* format,
313 int32* _deprecated_)
315 CALLED();
317 BMediaOutput* conn = fOwner->_FindOutput(source);
318 if (conn == NULL)
319 return B_MEDIA_BAD_DESTINATION;
321 return conn->FormatChangeRequested(format);
325 void
326 BMediaClientNode::LateNoticeReceived(const media_source& source,
327 bigtime_t late, bigtime_t when)
329 CALLED();
334 status_t
335 BMediaClientNode::GetNextOutput(int32* cookie, media_output* output)
337 CALLED();
339 if (fOwner->CountOutputs() == 0)
340 return B_BAD_INDEX;
342 if (*cookie < 0 || *cookie >= fOwner->CountOutputs()) {
343 *cookie = -1;
344 output = NULL;
345 } else {
346 BMediaOutput* conn = fOwner->OutputAt(*cookie);
347 if (conn != NULL) {
348 *output = conn->_MediaOutput();
349 *cookie += 1;
350 return B_OK;
353 return B_BAD_INDEX;
357 status_t
358 BMediaClientNode::DisposeOutputCookie(int32 cookie)
360 CALLED();
362 return B_OK;
366 status_t
367 BMediaClientNode::SetBufferGroup(const media_source& source, BBufferGroup* group)
369 CALLED();
371 BMediaOutput* conn = fOwner->_FindOutput(source);
372 if (conn == NULL)
373 return B_MEDIA_BAD_SOURCE;
375 if (group == conn->fBufferGroup)
376 return B_OK;
378 delete conn->fBufferGroup;
380 if (group != NULL) {
381 conn->fBufferGroup = group;
382 return B_OK;
385 conn->fBufferGroup = new BBufferGroup(conn->BufferSize(), 3);
386 if (conn->fBufferGroup == NULL)
387 return B_NO_MEMORY;
389 return conn->fBufferGroup->InitCheck();
393 status_t
394 BMediaClientNode::PrepareToConnect(const media_source& source,
395 const media_destination& dest, media_format* format,
396 media_source* out_source, char *name)
398 CALLED();
400 BMediaOutput* conn = fOwner->_FindOutput(source);
401 if (conn == NULL)
402 return B_MEDIA_BAD_SOURCE;
404 if (conn->_Destination() != media_destination::null)
405 return B_MEDIA_ALREADY_CONNECTED;
407 if (fOwner->MediaType() != B_MEDIA_UNKNOWN_TYPE
408 && format->type != fOwner->MediaType()) {
409 return B_MEDIA_BAD_FORMAT;
412 conn->fConnection.destination = dest;
414 status_t err = conn->PrepareToConnect(format);
415 if (err != B_OK)
416 return err;
418 *out_source = conn->_Source();
419 strcpy(name, Name());
421 return B_OK;
425 void
426 BMediaClientNode::Connect(status_t status, const media_source& source,
427 const media_destination& dest, const media_format& format,
428 char* name)
430 CALLED();
432 BMediaOutput* conn = fOwner->_FindOutput(source);
433 if (conn == NULL)
434 return;
436 // Reset the connection to reuse it
437 if (status != B_OK) {
438 conn->Disconnect();
439 return;
442 conn->fConnection.destination = dest;
443 conn->SetAcceptedFormat(format);
444 strcpy(name, Name());
446 // TODO: add correct latency estimate
447 SetEventLatency(1000);
449 conn->fBufferGroup = new BBufferGroup(conn->BufferSize(), 3);
450 if (conn->fBufferGroup == NULL)
451 TRACE("Can't allocate the buffer group\n");
453 conn->Connected(format);
457 void
458 BMediaClientNode::Disconnect(const media_source& source,
459 const media_destination& dest)
461 CALLED();
463 BMediaOutput* conn = fOwner->_FindOutput(source);
464 if (conn == NULL)
465 return;
467 if (conn->_Destination() == dest) {
468 conn->Disconnect();
469 conn->Disconnected();
474 void
475 BMediaClientNode::EnableOutput(const media_source& source,
476 bool enabled, int32* _deprecated_)
478 CALLED();
480 BMediaOutput* conn = fOwner->_FindOutput(source);
481 if (conn != NULL)
482 conn->SetEnabled(enabled);
486 status_t
487 BMediaClientNode::GetLatency(bigtime_t* outLatency)
489 CALLED();
491 // TODO: finish latency handling
492 *outLatency = 1000;
493 return B_OK;
497 void
498 BMediaClientNode::LatencyChanged(const media_source& source,
499 const media_destination& dest, bigtime_t latency, uint32 flags)
501 CALLED();
505 void
506 BMediaClientNode::ProducerDataStatus(const media_destination& dest,
507 int32 status, bigtime_t when)
509 CALLED();
513 void
514 BMediaClientNode::HandleEvent(const media_timed_event* event,
515 bigtime_t late, bool realTimeEvent)
517 CALLED();
519 switch (event->type) {
520 // This event is used for inputs which consumes buffers
521 // or binded connections which also send them to an output.
522 case BTimedEventQueue::B_HANDLE_BUFFER:
523 _HandleBuffer((BBuffer*)event->pointer);
524 break;
526 // This is used for connections which produce buffers only.
527 case B_NEW_BUFFER:
528 _ProduceNewBuffer(event, late);
529 break;
531 case BTimedEventQueue::B_START:
533 if (RunState() != B_STARTED)
534 fOwner->HandleStart(event->event_time);
536 fStartTime = event->event_time;
538 _ScheduleConnections(event->event_time);
539 break;
542 case BTimedEventQueue::B_STOP:
544 fOwner->HandleStop(event->event_time);
546 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
547 BTimedEventQueue::B_HANDLE_BUFFER);
548 break;
551 case BTimedEventQueue::B_SEEK:
552 fOwner->HandleSeek(event->event_time, event->bigdata);
553 break;
555 case BTimedEventQueue::B_WARP:
556 // NOTE: We have no need to handle it
557 break;
562 BMediaClientNode::~BMediaClientNode()
564 CALLED();
566 Quit();
570 void
571 BMediaClientNode::_ScheduleConnections(bigtime_t eventTime)
573 for (int32 i = 0; i < fOwner->CountOutputs(); i++) {
574 BMediaOutput* output = fOwner->OutputAt(i);
576 if (output->HasBinding())
577 continue;
579 media_timed_event firstBufferEvent(eventTime,
580 B_NEW_BUFFER);
582 output->fFramesSent = 0;
584 firstBufferEvent.pointer = (void*) output;
585 EventQueue()->AddEvent(firstBufferEvent);
590 void
591 BMediaClientNode::_HandleBuffer(BBuffer* buffer)
593 CALLED();
595 media_destination dest;
596 dest.id = buffer->Header()->destination;
597 BMediaInput* conn = fOwner->_FindInput(dest);
599 if (conn != NULL)
600 conn->HandleBuffer(buffer);
602 // TODO: Investigate system level latency logging
604 if (conn->HasBinding()) {
605 BMediaOutput* output = dynamic_cast<BMediaOutput*>(conn->Binding());
606 output->SendBuffer(buffer);
611 void
612 BMediaClientNode::_ProduceNewBuffer(const media_timed_event* event,
613 bigtime_t late)
615 CALLED();
617 if (RunState() != BMediaEventLooper::B_STARTED)
618 return;
620 // The connection is get through the event
621 BMediaOutput* output
622 = dynamic_cast<BMediaOutput*>((BMediaConnection*)event->pointer);
623 if (output == NULL)
624 return;
626 if (output->IsEnabled()) {
627 BBuffer* buffer = _GetNextBuffer(output, event->event_time);
629 if (buffer != NULL) {
630 if (output->SendBuffer(buffer) != B_OK) {
631 TRACE("BMediaClientNode: Failed to send buffer\n");
632 // The output failed, let's recycle the buffer
633 buffer->Recycle();
638 bigtime_t time = 0;
639 media_format format = output->AcceptedFormat();
640 if (format.IsAudio()) {
641 size_t nFrames = format.u.raw_audio.buffer_size
642 / ((format.u.raw_audio.format
643 & media_raw_audio_format::B_AUDIO_SIZE_MASK)
644 * format.u.raw_audio.channel_count);
645 output->fFramesSent += nFrames;
647 time = fStartTime + bigtime_t((1000000LL * output->fFramesSent)
648 / (int32)format.u.raw_audio.frame_rate);
651 media_timed_event nextEvent(time, B_NEW_BUFFER);
652 EventQueue()->AddEvent(nextEvent);
656 BBuffer*
657 BMediaClientNode::_GetNextBuffer(BMediaOutput* output, bigtime_t eventTime)
659 CALLED();
661 BBuffer* buffer = NULL;
662 if (output->fBufferGroup->RequestBuffer(buffer, 0) != B_OK) {
663 TRACE("MediaClientNode:::_GetNextBuffer: Failed to get the buffer\n");
664 return NULL;
667 media_header* header = buffer->Header();
668 header->type = output->AcceptedFormat().type;
669 header->size_used = output->BufferSize();
670 header->time_source = TimeSource()->ID();
671 header->start_time = eventTime;
673 return buffer;