2 * Copyright 2015, Dario Casalinuovo. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 #include "MediaClientNode.h"
8 #include <MediaClient.h>
9 #include <MediaConnection.h>
10 #include <scheduler.h>
11 #include <TimeSource.h>
17 #define B_NEW_BUFFER (BTimedEventQueue::B_USER_EVENT + 1)
20 BMediaClientNode::BMediaClientNode(const char* name
,
21 BMediaClient
* owner
, media_type type
)
24 BBufferConsumer(type
),
25 BBufferProducer(type
),
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
);
42 BMediaClientNode::SendBuffer(BBuffer
* buffer
, BMediaConnection
* conn
)
44 return BBufferProducer::SendBuffer(buffer
, conn
->_Source(), conn
->_Destination());
49 BMediaClientNode::AddOn(int32
* id
) const
53 return fOwner
->AddOn(id
);
58 BMediaClientNode::NodeRegistered()
67 BMediaClientNode::SetRunMode(run_mode mode
)
72 if (mode
== BMediaNode::B_OFFLINE
)
73 priority
= B_OFFLINE_PROCESSING
;
75 switch(ConsumerType()) {
76 case B_MEDIA_RAW_AUDIO
:
77 case B_MEDIA_ENCODED_AUDIO
:
78 priority
= B_AUDIO_RECORDING
;
81 case B_MEDIA_RAW_VIDEO
:
82 case B_MEDIA_ENCODED_VIDEO
:
83 priority
= B_VIDEO_RECORDING
;
87 priority
= B_DEFAULT_MEDIA_PRIORITY
;
91 SetPriority(suggest_thread_priority(priority
));
92 BMediaNode::SetRunMode(mode
);
97 BMediaClientNode::Start(bigtime_t performanceTime
)
101 BMediaEventLooper::Start(performanceTime
);
106 BMediaClientNode::Stop(bigtime_t performanceTime
, bool immediate
)
110 BMediaEventLooper::Stop(performanceTime
, immediate
);
115 BMediaClientNode::Seek(bigtime_t mediaTime
, bigtime_t performanceTime
)
119 BMediaEventLooper::Seek(mediaTime
, performanceTime
);
124 BMediaClientNode::TimeWarp(bigtime_t realTime
, bigtime_t performanceTime
)
128 BMediaEventLooper::TimeWarp(realTime
, performanceTime
);
133 BMediaClientNode::HandleMessage(int32 message
,
134 const void* data
, size_t size
)
143 BMediaClientNode::AcceptFormat(const media_destination
& dest
,
144 media_format
* format
)
148 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
150 return B_MEDIA_BAD_DESTINATION
;
152 if (format_is_compatible(*format
, conn
->AcceptedFormat()))
155 *format
= conn
->AcceptedFormat();
157 return B_MEDIA_BAD_FORMAT
;
162 BMediaClientNode::GetNextInput(int32
* cookie
,
167 if (fOwner
->CountInputs() == 0)
170 if (*cookie
< 0 || *cookie
>= fOwner
->CountInputs()) {
174 BMediaInput
* conn
= fOwner
->InputAt(*cookie
);
176 *input
= conn
->_MediaInput();
186 BMediaClientNode::DisposeInputCookie(int32 cookie
)
193 BMediaClientNode::BufferReceived(BBuffer
* buffer
)
197 EventQueue()->AddEvent(media_timed_event(buffer
->Header()->start_time
,
198 BTimedEventQueue::B_HANDLE_BUFFER
, buffer
,
199 BTimedEventQueue::B_RECYCLE_BUFFER
));
204 BMediaClientNode::GetLatencyFor(const media_destination
& dest
,
205 bigtime_t
* latency
, media_node_id
* timesource
)
209 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
211 return B_MEDIA_BAD_DESTINATION
;
213 *latency
= conn
->fMaxLatency
;
214 *timesource
= TimeSource()->ID();
220 BMediaClientNode::Connected(const media_source
& source
,
221 const media_destination
& dest
, const media_format
& format
,
222 media_input
* outInput
)
226 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
228 return B_MEDIA_BAD_DESTINATION
;
230 conn
->fConnection
.source
= source
;
231 conn
->SetAcceptedFormat(format
);
233 conn
->Connected(format
);
235 *outInput
= conn
->_MediaInput();
241 BMediaClientNode::Disconnected(const media_source
& source
,
242 const media_destination
& dest
)
246 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
250 if (conn
->_Source() == source
) {
252 conn
->Disconnected();
258 BMediaClientNode::FormatChanged(const media_source
& source
,
259 const media_destination
& dest
,
260 int32 tag
, const media_format
& format
)
264 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
266 return B_MEDIA_BAD_DESTINATION
;
268 return conn
->FormatChanged(format
);
273 BMediaClientNode::FormatSuggestionRequested(media_type type
,
274 int32 quality
, media_format
* format
)
278 if (type
!= ConsumerType()
279 && type
!= ProducerType()) {
280 return B_MEDIA_BAD_FORMAT
;
283 status_t ret
= fOwner
->FormatSuggestion(type
, quality
, format
);
285 // In that case we return just a very generic format.
286 media_format outFormat
;
287 outFormat
.type
= fOwner
->MediaType();
297 BMediaClientNode::FormatProposal(const media_source
& source
,
298 media_format
* format
)
302 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
304 return B_MEDIA_BAD_DESTINATION
;
306 return conn
->FormatProposal(format
);
311 BMediaClientNode::FormatChangeRequested(const media_source
& source
,
312 const media_destination
& dest
, media_format
* format
,
317 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
319 return B_MEDIA_BAD_DESTINATION
;
321 return conn
->FormatChangeRequested(format
);
326 BMediaClientNode::LateNoticeReceived(const media_source
& source
,
327 bigtime_t late
, bigtime_t when
)
335 BMediaClientNode::GetNextOutput(int32
* cookie
, media_output
* output
)
339 if (fOwner
->CountOutputs() == 0)
342 if (*cookie
< 0 || *cookie
>= fOwner
->CountOutputs()) {
346 BMediaOutput
* conn
= fOwner
->OutputAt(*cookie
);
348 *output
= conn
->_MediaOutput();
358 BMediaClientNode::DisposeOutputCookie(int32 cookie
)
367 BMediaClientNode::SetBufferGroup(const media_source
& source
, BBufferGroup
* group
)
371 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
373 return B_MEDIA_BAD_SOURCE
;
375 if (group
== conn
->fBufferGroup
)
378 delete conn
->fBufferGroup
;
381 conn
->fBufferGroup
= group
;
385 conn
->fBufferGroup
= new BBufferGroup(conn
->BufferSize(), 3);
386 if (conn
->fBufferGroup
== NULL
)
389 return conn
->fBufferGroup
->InitCheck();
394 BMediaClientNode::PrepareToConnect(const media_source
& source
,
395 const media_destination
& dest
, media_format
* format
,
396 media_source
* out_source
, char *name
)
400 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
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
);
418 *out_source
= conn
->_Source();
419 strcpy(name
, Name());
426 BMediaClientNode::Connect(status_t status
, const media_source
& source
,
427 const media_destination
& dest
, const media_format
& format
,
432 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
436 // Reset the connection to reuse it
437 if (status
!= B_OK
) {
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
);
458 BMediaClientNode::Disconnect(const media_source
& source
,
459 const media_destination
& dest
)
463 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
467 if (conn
->_Destination() == dest
) {
469 conn
->Disconnected();
475 BMediaClientNode::EnableOutput(const media_source
& source
,
476 bool enabled
, int32
* _deprecated_
)
480 BMediaOutput
* conn
= fOwner
->_FindOutput(source
);
482 conn
->SetEnabled(enabled
);
487 BMediaClientNode::GetLatency(bigtime_t
* outLatency
)
491 // TODO: finish latency handling
498 BMediaClientNode::LatencyChanged(const media_source
& source
,
499 const media_destination
& dest
, bigtime_t latency
, uint32 flags
)
506 BMediaClientNode::ProducerDataStatus(const media_destination
& dest
,
507 int32 status
, bigtime_t when
)
514 BMediaClientNode::HandleEvent(const media_timed_event
* event
,
515 bigtime_t late
, bool realTimeEvent
)
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
);
526 // This is used for connections which produce buffers only.
528 _ProduceNewBuffer(event
, late
);
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
);
542 case BTimedEventQueue::B_STOP
:
544 fOwner
->HandleStop(event
->event_time
);
546 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS
, true,
547 BTimedEventQueue::B_HANDLE_BUFFER
);
551 case BTimedEventQueue::B_SEEK
:
552 fOwner
->HandleSeek(event
->event_time
, event
->bigdata
);
555 case BTimedEventQueue::B_WARP
:
556 // NOTE: We have no need to handle it
562 BMediaClientNode::~BMediaClientNode()
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())
579 media_timed_event
firstBufferEvent(eventTime
,
582 output
->fFramesSent
= 0;
584 firstBufferEvent
.pointer
= (void*) output
;
585 EventQueue()->AddEvent(firstBufferEvent
);
591 BMediaClientNode::_HandleBuffer(BBuffer
* buffer
)
595 media_destination dest
;
596 dest
.id
= buffer
->Header()->destination
;
597 BMediaInput
* conn
= fOwner
->_FindInput(dest
);
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
);
612 BMediaClientNode::_ProduceNewBuffer(const media_timed_event
* event
,
617 if (RunState() != BMediaEventLooper::B_STARTED
)
620 // The connection is get through the event
622 = dynamic_cast<BMediaOutput
*>((BMediaConnection
*)event
->pointer
);
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
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
);
657 BMediaClientNode::_GetNextBuffer(BMediaOutput
* output
, bigtime_t eventTime
)
661 BBuffer
* buffer
= NULL
;
662 if (output
->fBufferGroup
->RequestBuffer(buffer
, 0) != B_OK
) {
663 TRACE("MediaClientNode:::_GetNextBuffer: Failed to get the buffer\n");
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
;